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