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