1 /* 2 * ********************************************************************** 3 * 4 * ld_pd_map.c 5 * 6 * Solaris MegaRAID device driver for SAS2.0 controllers 7 * Copyright (c) 2008-2012, LSI Logic Corporation. 8 * All rights reserved. 9 * 10 * Version: 11 * Author: 12 * Swaminathan K S 13 * Arun Chandrashekhar 14 * Manju R 15 * Rasheed 16 * Shakeel Bukhari 17 * 18 * 19 * This module contains functions for device drivers 20 * to get pd-ld mapping information. 21 * 22 * ********************************************************************** 23 */ 24 25 #include <sys/scsi/scsi.h> 26 #include "mr_sas.h" 27 #include "ld_pd_map.h" 28 29 /* 30 * This function will check if FAST IO is possible on this logical drive 31 * by checking the EVENT information available in the driver 32 */ 33 #define MR_LD_STATE_OPTIMAL 3 34 #define ABS_DIFF(a, b) (((a) > (b)) ? ((a) - (b)) : ((b) - (a))) 35 36 static void mr_update_load_balance_params(MR_FW_RAID_MAP_ALL *, 37 PLD_LOAD_BALANCE_INFO); 38 39 #define FALSE 0 40 #define TRUE 1 41 42 typedef U64 REGION_KEY; 43 typedef U32 REGION_LEN; 44 extern int debug_level_g; 45 46 47 MR_LD_RAID 48 *MR_LdRaidGet(U32 ld, MR_FW_RAID_MAP_ALL *map) 49 { 50 return (&map->raidMap.ldSpanMap[ld].ldRaid); 51 } 52 53 U16 54 MR_GetLDTgtId(U32 ld, MR_FW_RAID_MAP_ALL *map) 55 { 56 return (map->raidMap.ldSpanMap[ld].ldRaid.targetId); 57 } 58 59 60 static MR_SPAN_BLOCK_INFO * 61 MR_LdSpanInfoGet(U32 ld, MR_FW_RAID_MAP_ALL *map) 62 { 63 return (&map->raidMap.ldSpanMap[ld].spanBlock[0]); 64 } 65 66 static U8 67 MR_LdDataArmGet(U32 ld, U32 armIdx, MR_FW_RAID_MAP_ALL *map) 68 { 69 return (map->raidMap.ldSpanMap[ld].dataArmMap[armIdx]); 70 } 71 72 static U16 73 MR_ArPdGet(U32 ar, U32 arm, MR_FW_RAID_MAP_ALL *map) 74 { 75 return (map->raidMap.arMapInfo[ar].pd[arm]); 76 } 77 78 static U16 79 MR_LdSpanArrayGet(U32 ld, U32 span, MR_FW_RAID_MAP_ALL *map) 80 { 81 return (map->raidMap.ldSpanMap[ld].spanBlock[span].span.arrayRef); 82 } 83 84 static U16 85 MR_PdDevHandleGet(U32 pd, MR_FW_RAID_MAP_ALL *map) 86 { 87 return (map->raidMap.devHndlInfo[pd].curDevHdl); 88 } 89 90 U16 91 MR_TargetIdToLdGet(U32 ldTgtId, MR_FW_RAID_MAP_ALL *map) 92 { 93 return (map->raidMap.ldTgtIdToLd[ldTgtId]); 94 } 95 96 U16 97 MR_CheckDIF(U32 ldTgtId, MR_FW_RAID_MAP_ALL *map) 98 { 99 MR_LD_RAID *raid; 100 U32 ld; 101 102 ld = MR_TargetIdToLdGet(ldTgtId, map); 103 104 if (ld >= MAX_LOGICAL_DRIVES) { 105 return (FALSE); 106 } 107 108 raid = MR_LdRaidGet(ld, map); 109 110 return (raid->capability.ldPiMode == 0x8); 111 } 112 113 static MR_LD_SPAN * 114 MR_LdSpanPtrGet(U32 ld, U32 span, MR_FW_RAID_MAP_ALL *map) 115 { 116 return (&map->raidMap.ldSpanMap[ld].spanBlock[span].span); 117 } 118 119 /* 120 * This function will validate Map info data provided by FW 121 */ 122 U8 123 MR_ValidateMapInfo(MR_FW_RAID_MAP_ALL *map, PLD_LOAD_BALANCE_INFO lbInfo) 124 { 125 MR_FW_RAID_MAP *pFwRaidMap = &map->raidMap; 126 U32 fwsize = sizeof (MR_FW_RAID_MAP) - sizeof (MR_LD_SPAN_MAP) + 127 (sizeof (MR_LD_SPAN_MAP) * pFwRaidMap->ldCount); 128 129 if (pFwRaidMap->totalSize != fwsize) { 130 131 con_log(CL_ANN1, (CE_NOTE, 132 "map info structure size 0x%x is " 133 "not matching with ld count\n", fwsize)); 134 /* sizeof (foo) returns size_t, which is *LONG*. */ 135 con_log(CL_ANN1, (CE_NOTE, "span map 0x%x total size 0x%x\n",\ 136 (int)sizeof (MR_LD_SPAN_MAP), pFwRaidMap->totalSize)); 137 138 return (0); 139 } 140 141 mr_update_load_balance_params(map, lbInfo); 142 143 return (1); 144 } 145 146 U32 147 MR_GetSpanBlock(U32 ld, U64 row, U64 *span_blk, MR_FW_RAID_MAP_ALL *map, 148 int *div_error) 149 { 150 MR_SPAN_BLOCK_INFO *pSpanBlock = MR_LdSpanInfoGet(ld, map); 151 MR_QUAD_ELEMENT *qe; 152 MR_LD_RAID *raid = MR_LdRaidGet(ld, map); 153 U32 span, j; 154 155 for (span = 0; span < raid->spanDepth; span++, pSpanBlock++) { 156 for (j = 0; j < pSpanBlock->block_span_info.noElements; j++) { 157 qe = &pSpanBlock->block_span_info.quads[j]; 158 if (qe->diff == 0) { 159 *div_error = 1; 160 return (span); 161 } 162 if (qe->logStart <= row && row <= qe->logEnd && 163 (((row - qe->logStart) % qe->diff)) == 0) { 164 if (span_blk != NULL) { 165 U64 blk; 166 blk = ((row - qe->logStart) / 167 (qe->diff)); 168 169 blk = (blk + qe->offsetInSpan) << 170 raid->stripeShift; 171 *span_blk = blk; 172 } 173 return (span); 174 } 175 } 176 } 177 return (span); 178 } 179 180 181 /* 182 * ************************************************************* 183 * 184 * This routine calculates the arm, span and block for 185 * the specified stripe and reference in stripe. 186 * 187 * Inputs : 188 * 189 * ld - Logical drive number 190 * stripRow - Stripe number 191 * stripRef - Reference in stripe 192 * 193 * Outputs : 194 * 195 * span - Span number 196 * block - Absolute Block number in the physical disk 197 */ 198 U8 199 MR_GetPhyParams(struct mrsas_instance *instance, U32 ld, U64 stripRow, 200 U16 stripRef, U64 *pdBlock, U16 *pDevHandle, 201 MPI2_SCSI_IO_VENDOR_UNIQUE *pRAID_Context, MR_FW_RAID_MAP_ALL *map) 202 { 203 MR_LD_RAID *raid = MR_LdRaidGet(ld, map); 204 U32 pd, arRef; 205 U8 physArm, span; 206 U64 row; 207 int error_code = 0; 208 U8 retval = TRUE; 209 U32 rowMod; 210 U32 armQ; 211 U32 arm; 212 213 ASSERT(raid->rowDataSize != 0); 214 215 row = (stripRow / raid->rowDataSize); 216 217 if (raid->level == 6) { 218 U32 logArm = (stripRow % (raid->rowDataSize)); 219 220 if (raid->rowSize == 0) { 221 return (FALSE); 222 } 223 rowMod = (row % (raid->rowSize)); 224 armQ = raid->rowSize-1-rowMod; 225 arm = armQ + 1 + logArm; 226 if (arm >= raid->rowSize) 227 arm -= raid->rowSize; 228 physArm = (U8)arm; 229 } else { 230 if (raid->modFactor == 0) 231 return (FALSE); 232 physArm = MR_LdDataArmGet(ld, 233 (stripRow % (raid->modFactor)), map); 234 } 235 if (raid->spanDepth == 1) { 236 span = 0; 237 *pdBlock = row << raid->stripeShift; 238 } else 239 span = (U8)MR_GetSpanBlock(ld, row, pdBlock, map, &error_code); 240 241 if (error_code == 1) 242 return (FALSE); 243 244 /* Get the array on which this span is present. */ 245 arRef = MR_LdSpanArrayGet(ld, span, map); 246 /* Get the Pd. */ 247 pd = MR_ArPdGet(arRef, physArm, map); 248 /* Get dev handle from Pd. */ 249 if (pd != MR_PD_INVALID) { 250 *pDevHandle = MR_PdDevHandleGet(pd, map); 251 } else { 252 *pDevHandle = MR_PD_INVALID; /* set dev handle as invalid. */ 253 if ((raid->level >= 5) && 254 ((instance->device_id != PCI_DEVICE_ID_LSI_INVADER) || 255 (instance->device_id == PCI_DEVICE_ID_LSI_INVADER && 256 raid->regTypeReqOnRead != REGION_TYPE_UNUSED))) { 257 pRAID_Context->regLockFlags = REGION_TYPE_EXCLUSIVE; 258 } else if (raid->level == 1) { 259 /* Get Alternate Pd. */ 260 pd = MR_ArPdGet(arRef, physArm + 1, map); 261 /* Get dev handle from Pd. */ 262 if (pd != MR_PD_INVALID) 263 *pDevHandle = MR_PdDevHandleGet(pd, map); 264 } 265 } 266 267 *pdBlock += stripRef + MR_LdSpanPtrGet(ld, span, map)->startBlk; 268 269 pRAID_Context->spanArm = (span << RAID_CTX_SPANARM_SPAN_SHIFT) | 270 physArm; 271 272 return (retval); 273 } 274 275 276 277 /* 278 * *********************************************************************** 279 * 280 * MR_BuildRaidContext function 281 * 282 * This function will initiate command processing. The start/end row and strip 283 * information is calculated then the lock is acquired. 284 * This function will return 0 if region lock 285 * was acquired OR return num strips ??? 286 */ 287 288 U8 289 MR_BuildRaidContext(struct mrsas_instance *instance, 290 struct IO_REQUEST_INFO *io_info, MPI2_SCSI_IO_VENDOR_UNIQUE *pRAID_Context, 291 MR_FW_RAID_MAP_ALL *map) 292 { 293 MR_LD_RAID *raid; 294 U32 ld, stripSize, stripe_mask; 295 U64 endLba, endStrip, endRow; 296 U64 start_row, start_strip; 297 REGION_KEY regStart; 298 REGION_LEN regSize; 299 U8 num_strips, numRows; 300 U16 ref_in_start_stripe; 301 U16 ref_in_end_stripe; 302 303 U64 ldStartBlock; 304 U32 numBlocks, ldTgtId; 305 U8 isRead; 306 U8 retval = 0; 307 308 ldStartBlock = io_info->ldStartBlock; 309 numBlocks = io_info->numBlocks; 310 ldTgtId = io_info->ldTgtId; 311 isRead = io_info->isRead; 312 313 if (map == NULL) { 314 io_info->fpOkForIo = FALSE; 315 return (FALSE); 316 } 317 318 ld = MR_TargetIdToLdGet(ldTgtId, map); 319 320 if (ld >= MAX_LOGICAL_DRIVES) { 321 io_info->fpOkForIo = FALSE; 322 return (FALSE); 323 } 324 325 raid = MR_LdRaidGet(ld, map); 326 327 stripSize = 1 << raid->stripeShift; 328 stripe_mask = stripSize-1; 329 /* 330 * calculate starting row and stripe, and number of strips and rows 331 */ 332 start_strip = ldStartBlock >> raid->stripeShift; 333 ref_in_start_stripe = (U16)(ldStartBlock & stripe_mask); 334 endLba = ldStartBlock + numBlocks - 1; 335 ref_in_end_stripe = (U16)(endLba & stripe_mask); 336 endStrip = endLba >> raid->stripeShift; 337 num_strips = (U8)(endStrip - start_strip + 1); 338 /* Check to make sure is not dividing by zero */ 339 if (raid->rowDataSize == 0) 340 return (FALSE); 341 start_row = (start_strip / raid->rowDataSize); 342 endRow = (endStrip / raid->rowDataSize); 343 /* get the row count */ 344 numRows = (U8)(endRow - start_row + 1); 345 346 /* 347 * calculate region info. 348 */ 349 regStart = start_row << raid->stripeShift; 350 regSize = stripSize; 351 352 /* Check if we can send this I/O via FastPath */ 353 if (raid->capability.fpCapable) { 354 if (isRead) { 355 io_info->fpOkForIo = (raid->capability.fpReadCapable && 356 ((num_strips == 1) || 357 raid->capability.fpReadAcrossStripe)); 358 } else { 359 io_info->fpOkForIo = 360 (raid->capability.fpWriteCapable && 361 ((num_strips == 1) || 362 raid->capability.fpWriteAcrossStripe)); 363 } 364 } else 365 io_info->fpOkForIo = FALSE; 366 367 368 /* 369 * Check for DIF support 370 */ 371 if (!raid->capability.ldPiMode) { 372 io_info->ldPI = FALSE; 373 } else { 374 io_info->ldPI = TRUE; 375 } 376 377 if (numRows == 1) { 378 if (num_strips == 1) { 379 regStart += ref_in_start_stripe; 380 regSize = numBlocks; 381 } 382 } else { 383 if (start_strip == (start_row + 1) * raid->rowDataSize - 1) { 384 regStart += ref_in_start_stripe; 385 regSize = stripSize - ref_in_start_stripe; 386 } 387 388 if (numRows > 2) { 389 regSize += (numRows - 2) << raid->stripeShift; 390 } 391 392 if (endStrip == endRow * raid->rowDataSize) { 393 regSize += ref_in_end_stripe + 1; 394 } else { 395 regSize += stripSize; 396 } 397 } 398 399 pRAID_Context->timeoutValue = map->raidMap.fpPdIoTimeoutSec; 400 401 if (instance->device_id == PCI_DEVICE_ID_LSI_INVADER) { 402 pRAID_Context->regLockFlags = (isRead) ? 403 raid->regTypeReqOnRead : raid->regTypeReqOnWrite; 404 } else { 405 pRAID_Context->regLockFlags = (isRead) ? 406 REGION_TYPE_SHARED_READ : raid->regTypeReqOnWrite; 407 } 408 409 pRAID_Context->ldTargetId = raid->targetId; 410 pRAID_Context->regLockRowLBA = regStart; 411 pRAID_Context->regLockLength = regSize; 412 pRAID_Context->configSeqNum = raid->seqNum; 413 414 /* 415 * Get Phy Params only if FP capable, 416 * or else leave it to MR firmware to do the calculation. 417 */ 418 if (io_info->fpOkForIo) { 419 /* if fast path possible then get the physical parameters */ 420 retval = MR_GetPhyParams(instance, ld, start_strip, 421 ref_in_start_stripe, &io_info->pdBlock, 422 &io_info->devHandle, pRAID_Context, map); 423 424 /* If IO on an invalid Pd, then FP is not possible. */ 425 if (io_info->devHandle == MR_PD_INVALID) 426 io_info->fpOkForIo = FALSE; 427 428 return (retval); 429 430 } else if (isRead) { 431 uint_t stripIdx; 432 433 for (stripIdx = 0; stripIdx < num_strips; stripIdx++) { 434 if (!MR_GetPhyParams(instance, ld, 435 start_strip + stripIdx, ref_in_start_stripe, 436 &io_info->pdBlock, &io_info->devHandle, 437 pRAID_Context, map)) { 438 return (TRUE); 439 } 440 } 441 } 442 return (TRUE); 443 } 444 445 446 void 447 mr_update_load_balance_params(MR_FW_RAID_MAP_ALL *map, 448 PLD_LOAD_BALANCE_INFO lbInfo) 449 { 450 int ldCount; 451 U16 ld; 452 MR_LD_RAID *raid; 453 454 for (ldCount = 0; ldCount < MAX_LOGICAL_DRIVES; ldCount++) { 455 ld = MR_TargetIdToLdGet(ldCount, map); 456 457 if (ld >= MAX_LOGICAL_DRIVES) { 458 con_log(CL_ANN1, 459 (CE_NOTE, "mrsas: ld=%d Invalid ld \n", ld)); 460 continue; 461 } 462 463 raid = MR_LdRaidGet(ld, map); 464 465 /* Two drive Optimal RAID 1 */ 466 if ((raid->level == 1) && (raid->rowSize == 2) && 467 (raid->spanDepth == 1) && 468 raid->ldState == MR_LD_STATE_OPTIMAL) { 469 U32 pd, arRef; 470 471 lbInfo[ldCount].loadBalanceFlag = 1; 472 473 /* Get the array on which this span is present. */ 474 arRef = MR_LdSpanArrayGet(ld, 0, map); 475 476 pd = MR_ArPdGet(arRef, 0, map); /* Get the Pd. */ 477 /* Get dev handle from Pd. */ 478 lbInfo[ldCount].raid1DevHandle[0] = 479 MR_PdDevHandleGet(pd, map); 480 481 pd = MR_ArPdGet(arRef, 1, map); /* Get the Pd. */ 482 /* Get dev handle from Pd. */ 483 lbInfo[ldCount].raid1DevHandle[1] = 484 MR_PdDevHandleGet(pd, map); 485 con_log(CL_ANN1, (CE_NOTE, 486 "mrsas: ld=%d load balancing enabled \n", ldCount)); 487 } else { 488 lbInfo[ldCount].loadBalanceFlag = 0; 489 } 490 } 491 } 492 493 494 U8 495 megasas_get_best_arm(PLD_LOAD_BALANCE_INFO lbInfo, U8 arm, U64 block, 496 U32 count) 497 { 498 U16 pend0, pend1; 499 U64 diff0, diff1; 500 U8 bestArm; 501 502 /* get the pending cmds for the data and mirror arms */ 503 pend0 = lbInfo->scsi_pending_cmds[0]; 504 pend1 = lbInfo->scsi_pending_cmds[1]; 505 506 /* Determine the disk whose head is nearer to the req. block */ 507 diff0 = ABS_DIFF(block, lbInfo->last_accessed_block[0]); 508 diff1 = ABS_DIFF(block, lbInfo->last_accessed_block[1]); 509 bestArm = (diff0 <= diff1 ? 0 : 1); 510 511 if ((bestArm == arm && pend0 > pend1 + 16) || 512 (bestArm != arm && pend1 > pend0 + 16)) { 513 bestArm ^= 1; 514 } 515 516 /* Update the last accessed block on the correct pd */ 517 lbInfo->last_accessed_block[bestArm] = block + count - 1; 518 return (bestArm); 519 } 520 521 U16 522 get_updated_dev_handle(PLD_LOAD_BALANCE_INFO lbInfo, 523 struct IO_REQUEST_INFO *io_info) 524 { 525 U8 arm, old_arm; 526 U16 devHandle; 527 528 old_arm = lbInfo->raid1DevHandle[0] == io_info->devHandle ? 0 : 1; 529 530 /* get best new arm */ 531 arm = megasas_get_best_arm(lbInfo, old_arm, io_info->ldStartBlock, 532 io_info->numBlocks); 533 534 devHandle = lbInfo->raid1DevHandle[arm]; 535 536 lbInfo->scsi_pending_cmds[arm]++; 537 538 return (devHandle); 539 }