xref: /netbsd-src/sys/dev/raidframe/rf_reconstruct.c (revision 21e37cc72a480a47828990a439cde7ac9ffaf0c6)
1 /*	$NetBSD: rf_reconstruct.c,v 1.76 2004/03/18 16:54:54 oster Exp $	*/
2 /*
3  * Copyright (c) 1995 Carnegie-Mellon University.
4  * All rights reserved.
5  *
6  * Author: Mark Holland
7  *
8  * Permission to use, copy, modify and distribute this software and
9  * its documentation is hereby granted, provided that both the copyright
10  * notice and this permission notice appear in all copies of the
11  * software, derivative works or modified versions, and any portions
12  * thereof, and that both notices appear in supporting documentation.
13  *
14  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
15  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
16  * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
17  *
18  * Carnegie Mellon requests users of this software to return to
19  *
20  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
21  *  School of Computer Science
22  *  Carnegie Mellon University
23  *  Pittsburgh PA 15213-3890
24  *
25  * any improvements or extensions that they make and grant Carnegie the
26  * rights to redistribute these changes.
27  */
28 
29 /************************************************************
30  *
31  * rf_reconstruct.c -- code to perform on-line reconstruction
32  *
33  ************************************************************/
34 
35 #include <sys/cdefs.h>
36 __KERNEL_RCSID(0, "$NetBSD: rf_reconstruct.c,v 1.76 2004/03/18 16:54:54 oster Exp $");
37 
38 #include <sys/time.h>
39 #include <sys/buf.h>
40 #include <sys/errno.h>
41 
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/proc.h>
45 #include <sys/ioctl.h>
46 #include <sys/fcntl.h>
47 #include <sys/vnode.h>
48 #include <dev/raidframe/raidframevar.h>
49 
50 #include "rf_raid.h"
51 #include "rf_reconutil.h"
52 #include "rf_revent.h"
53 #include "rf_reconbuffer.h"
54 #include "rf_acctrace.h"
55 #include "rf_etimer.h"
56 #include "rf_dag.h"
57 #include "rf_desc.h"
58 #include "rf_debugprint.h"
59 #include "rf_general.h"
60 #include "rf_driver.h"
61 #include "rf_utils.h"
62 #include "rf_shutdown.h"
63 
64 #include "rf_kintf.h"
65 
66 /* setting these to -1 causes them to be set to their default values if not set by debug options */
67 
68 #if RF_DEBUG_RECON
69 #define Dprintf(s)         if (rf_reconDebug) rf_debug_printf(s,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL)
70 #define Dprintf1(s,a)         if (rf_reconDebug) rf_debug_printf(s,(void *)((unsigned long)a),NULL,NULL,NULL,NULL,NULL,NULL,NULL)
71 #define Dprintf2(s,a,b)       if (rf_reconDebug) rf_debug_printf(s,(void *)((unsigned long)a),(void *)((unsigned long)b),NULL,NULL,NULL,NULL,NULL,NULL)
72 #define Dprintf3(s,a,b,c)     if (rf_reconDebug) rf_debug_printf(s,(void *)((unsigned long)a),(void *)((unsigned long)b),(void *)((unsigned long)c),NULL,NULL,NULL,NULL,NULL)
73 #define Dprintf4(s,a,b,c,d)   if (rf_reconDebug) rf_debug_printf(s,(void *)((unsigned long)a),(void *)((unsigned long)b),(void *)((unsigned long)c),(void *)((unsigned long)d),NULL,NULL,NULL,NULL)
74 #define Dprintf5(s,a,b,c,d,e) if (rf_reconDebug) rf_debug_printf(s,(void *)((unsigned long)a),(void *)((unsigned long)b),(void *)((unsigned long)c),(void *)((unsigned long)d),(void *)((unsigned long)e),NULL,NULL,NULL)
75 #define Dprintf6(s,a,b,c,d,e,f) if (rf_reconDebug) rf_debug_printf(s,(void *)((unsigned long)a),(void *)((unsigned long)b),(void *)((unsigned long)c),(void *)((unsigned long)d),(void *)((unsigned long)e),(void *)((unsigned long)f),NULL,NULL)
76 #define Dprintf7(s,a,b,c,d,e,f,g) if (rf_reconDebug) rf_debug_printf(s,(void *)((unsigned long)a),(void *)((unsigned long)b),(void *)((unsigned long)c),(void *)((unsigned long)d),(void *)((unsigned long)e),(void *)((unsigned long)f),(void *)((unsigned long)g),NULL)
77 
78 #define DDprintf1(s,a)         if (rf_reconDebug) rf_debug_printf(s,(void *)((unsigned long)a),NULL,NULL,NULL,NULL,NULL,NULL,NULL)
79 #define DDprintf2(s,a,b)       if (rf_reconDebug) rf_debug_printf(s,(void *)((unsigned long)a),(void *)((unsigned long)b),NULL,NULL,NULL,NULL,NULL,NULL)
80 
81 #else /* RF_DEBUG_RECON */
82 
83 #define Dprintf(s) {}
84 #define Dprintf1(s,a) {}
85 #define Dprintf2(s,a,b) {}
86 #define Dprintf3(s,a,b,c) {}
87 #define Dprintf4(s,a,b,c,d) {}
88 #define Dprintf5(s,a,b,c,d,e) {}
89 #define Dprintf6(s,a,b,c,d,e,f) {}
90 #define Dprintf7(s,a,b,c,d,e,f,g) {}
91 
92 #define DDprintf1(s,a) {}
93 #define DDprintf2(s,a,b) {}
94 
95 #endif /* RF_DEBUG_RECON */
96 
97 
98 #define RF_MAX_FREE_RECOND  10
99 #define RF_MIN_FREE_RECOND  4
100 
101 #define RF_MAX_FREE_RECONBUFFER 32
102 #define RF_MIN_FREE_RECONBUFFER 16
103 
104 static RF_RaidReconDesc_t *AllocRaidReconDesc(RF_Raid_t *, RF_RowCol_t,
105 					      RF_RaidDisk_t *, int, RF_RowCol_t);
106 static void FreeReconDesc(RF_RaidReconDesc_t *);
107 static int ProcessReconEvent(RF_Raid_t *, RF_ReconEvent_t *);
108 static int IssueNextReadRequest(RF_Raid_t *, RF_RowCol_t);
109 static int TryToRead(RF_Raid_t *, RF_RowCol_t);
110 static int ComputePSDiskOffsets(RF_Raid_t *, RF_StripeNum_t, RF_RowCol_t,
111 				RF_SectorNum_t *, RF_SectorNum_t *, RF_RowCol_t *,
112 				RF_SectorNum_t *);
113 static int IssueNextWriteRequest(RF_Raid_t *);
114 static int ReconReadDoneProc(void *, int);
115 static int ReconWriteDoneProc(void *, int);
116 static void CheckForNewMinHeadSep(RF_Raid_t *, RF_HeadSepLimit_t);
117 static int CheckHeadSeparation(RF_Raid_t *, RF_PerDiskReconCtrl_t *,
118 			       RF_RowCol_t, RF_HeadSepLimit_t,
119 			       RF_ReconUnitNum_t);
120 static int CheckForcedOrBlockedReconstruction(RF_Raid_t *,
121 					      RF_ReconParityStripeStatus_t *,
122 					      RF_PerDiskReconCtrl_t *,
123 					      RF_RowCol_t, RF_StripeNum_t,
124 					      RF_ReconUnitNum_t);
125 static void ForceReconReadDoneProc(void *, int);
126 static void rf_ShutdownReconstruction(void *);
127 
128 struct RF_ReconDoneProc_s {
129 	void    (*proc) (RF_Raid_t *, void *);
130 	void   *arg;
131 	RF_ReconDoneProc_t *next;
132 };
133 
134 /**************************************************************************
135  *
136  * sets up the parameters that will be used by the reconstruction process
137  * currently there are none, except for those that the layout-specific
138  * configuration (e.g. rf_ConfigureDeclustered) routine sets up.
139  *
140  * in the kernel, we fire off the recon thread.
141  *
142  **************************************************************************/
143 static void
144 rf_ShutdownReconstruction(void *ignored)
145 {
146 	pool_destroy(&rf_pools.recond);
147 	pool_destroy(&rf_pools.reconbuffer);
148 }
149 
150 int
151 rf_ConfigureReconstruction(RF_ShutdownList_t **listp)
152 {
153 
154 	rf_pool_init(&rf_pools.recond, sizeof(RF_RaidReconDesc_t),
155 		     "rf_recond_pl", RF_MIN_FREE_RECOND, RF_MAX_FREE_RECOND);
156 	rf_pool_init(&rf_pools.reconbuffer, sizeof(RF_ReconBuffer_t),
157 		     "rf_reconbuffer_pl", RF_MIN_FREE_RECONBUFFER, RF_MAX_FREE_RECONBUFFER);
158 	rf_ShutdownCreate(listp, rf_ShutdownReconstruction, NULL);
159 
160 	return (0);
161 }
162 
163 static RF_RaidReconDesc_t *
164 AllocRaidReconDesc(RF_Raid_t *raidPtr, RF_RowCol_t col,
165 		   RF_RaidDisk_t *spareDiskPtr, int numDisksDone,
166 		   RF_RowCol_t scol)
167 {
168 
169 	RF_RaidReconDesc_t *reconDesc;
170 
171 	reconDesc = pool_get(&rf_pools.recond, PR_WAITOK);
172 	reconDesc->raidPtr = raidPtr;
173 	reconDesc->col = col;
174 	reconDesc->spareDiskPtr = spareDiskPtr;
175 	reconDesc->numDisksDone = numDisksDone;
176 	reconDesc->scol = scol;
177 	reconDesc->state = 0;
178 	reconDesc->next = NULL;
179 
180 	return (reconDesc);
181 }
182 
183 static void
184 FreeReconDesc(RF_RaidReconDesc_t *reconDesc)
185 {
186 #if RF_RECON_STATS > 0
187 	printf("raid%d: %lu recon event waits, %lu recon delays\n",
188 	       reconDesc->raidPtr->raidid,
189 	       (long) reconDesc->numReconEventWaits,
190 	       (long) reconDesc->numReconExecDelays);
191 #endif				/* RF_RECON_STATS > 0 */
192 	printf("raid%d: %lu max exec ticks\n",
193 	       reconDesc->raidPtr->raidid,
194 	       (long) reconDesc->maxReconExecTicks);
195 #if (RF_RECON_STATS > 0) || defined(KERNEL)
196 	printf("\n");
197 #endif				/* (RF_RECON_STATS > 0) || KERNEL */
198 	pool_put(&rf_pools.recond, reconDesc);
199 }
200 
201 
202 /*****************************************************************************
203  *
204  * primary routine to reconstruct a failed disk.  This should be called from
205  * within its own thread.  It won't return until reconstruction completes,
206  * fails, or is aborted.
207  *****************************************************************************/
208 int
209 rf_ReconstructFailedDisk(RF_Raid_t *raidPtr, RF_RowCol_t col)
210 {
211 	const RF_LayoutSW_t *lp;
212 	int     rc;
213 
214 	lp = raidPtr->Layout.map;
215 	if (lp->SubmitReconBuffer) {
216 		/*
217 	         * The current infrastructure only supports reconstructing one
218 	         * disk at a time for each array.
219 	         */
220 		RF_LOCK_MUTEX(raidPtr->mutex);
221 		while (raidPtr->reconInProgress) {
222 			RF_WAIT_COND(raidPtr->waitForReconCond, raidPtr->mutex);
223 		}
224 		raidPtr->reconInProgress++;
225 		RF_UNLOCK_MUTEX(raidPtr->mutex);
226 		rc = rf_ReconstructFailedDiskBasic(raidPtr, col);
227 		RF_LOCK_MUTEX(raidPtr->mutex);
228 		raidPtr->reconInProgress--;
229 		RF_UNLOCK_MUTEX(raidPtr->mutex);
230 	} else {
231 		RF_ERRORMSG1("RECON: no way to reconstruct failed disk for arch %c\n",
232 		    lp->parityConfig);
233 		rc = EIO;
234 	}
235 	RF_SIGNAL_COND(raidPtr->waitForReconCond);
236 	return (rc);
237 }
238 
239 int
240 rf_ReconstructFailedDiskBasic(RF_Raid_t *raidPtr, RF_RowCol_t col)
241 {
242 	RF_ComponentLabel_t c_label;
243 	RF_RaidDisk_t *spareDiskPtr = NULL;
244 	RF_RaidReconDesc_t *reconDesc;
245 	RF_RowCol_t scol;
246 	int     numDisksDone = 0, rc;
247 
248 	/* first look for a spare drive onto which to reconstruct the data */
249 	/* spare disk descriptors are stored in row 0.  This may have to
250 	 * change eventually */
251 
252 	RF_LOCK_MUTEX(raidPtr->mutex);
253 	RF_ASSERT(raidPtr->Disks[col].status == rf_ds_failed);
254 #if RF_INCLUDE_PARITY_DECLUSTERING_DS > 0
255 	if (raidPtr->Layout.map->flags & RF_DISTRIBUTE_SPARE) {
256 		if (raidPtr->status != rf_rs_degraded) {
257 			RF_ERRORMSG1("Unable to reconstruct disk at col %d because status not degraded\n", col);
258 			RF_UNLOCK_MUTEX(raidPtr->mutex);
259 			return (EINVAL);
260 		}
261 		scol = (-1);
262 	} else {
263 #endif
264 		for (scol = raidPtr->numCol; scol < raidPtr->numCol + raidPtr->numSpare; scol++) {
265 			if (raidPtr->Disks[scol].status == rf_ds_spare) {
266 				spareDiskPtr = &raidPtr->Disks[scol];
267 				spareDiskPtr->status = rf_ds_used_spare;
268 				break;
269 			}
270 		}
271 		if (!spareDiskPtr) {
272 			RF_ERRORMSG1("Unable to reconstruct disk at col %d because no spares are available\n", col);
273 			RF_UNLOCK_MUTEX(raidPtr->mutex);
274 			return (ENOSPC);
275 		}
276 		printf("RECON: initiating reconstruction on col %d -> spare at col %d\n", col, scol);
277 #if RF_INCLUDE_PARITY_DECLUSTERING_DS > 0
278 	}
279 #endif
280 	RF_UNLOCK_MUTEX(raidPtr->mutex);
281 
282 	reconDesc = AllocRaidReconDesc((void *) raidPtr, col, spareDiskPtr, numDisksDone, scol);
283 	raidPtr->reconDesc = (void *) reconDesc;
284 #if RF_RECON_STATS > 0
285 	reconDesc->hsStallCount = 0;
286 	reconDesc->numReconExecDelays = 0;
287 	reconDesc->numReconEventWaits = 0;
288 #endif				/* RF_RECON_STATS > 0 */
289 	reconDesc->reconExecTimerRunning = 0;
290 	reconDesc->reconExecTicks = 0;
291 	reconDesc->maxReconExecTicks = 0;
292 	rc = rf_ContinueReconstructFailedDisk(reconDesc);
293 
294 	if (!rc) {
295 		/* fix up the component label */
296 		/* Don't actually need the read here.. */
297 		raidread_component_label(
298                         raidPtr->raid_cinfo[scol].ci_dev,
299 			raidPtr->raid_cinfo[scol].ci_vp,
300 			&c_label);
301 
302 		raid_init_component_label( raidPtr, &c_label);
303 		c_label.row = 0;
304 		c_label.column = col;
305 		c_label.clean = RF_RAID_DIRTY;
306 		c_label.status = rf_ds_optimal;
307 		c_label.partitionSize = raidPtr->Disks[scol].partitionSize;
308 
309 		/* We've just done a rebuild based on all the other
310 		   disks, so at this point the parity is known to be
311 		   clean, even if it wasn't before. */
312 
313 		/* XXX doesn't hold for RAID 6!!*/
314 
315 		RF_LOCK_MUTEX(raidPtr->mutex);
316 		raidPtr->parity_good = RF_RAID_CLEAN;
317 		RF_UNLOCK_MUTEX(raidPtr->mutex);
318 
319 		/* XXXX MORE NEEDED HERE */
320 
321 		raidwrite_component_label(
322                         raidPtr->raid_cinfo[scol].ci_dev,
323 			raidPtr->raid_cinfo[scol].ci_vp,
324 			&c_label);
325 
326 
327 		rf_update_component_labels(raidPtr,
328 					   RF_NORMAL_COMPONENT_UPDATE);
329 
330 	}
331 	return (rc);
332 }
333 
334 /*
335 
336    Allow reconstructing a disk in-place -- i.e. component /dev/sd2e goes AWOL,
337    and you don't get a spare until the next Monday.  With this function
338    (and hot-swappable drives) you can now put your new disk containing
339    /dev/sd2e on the bus, scsictl it alive, and then use raidctl(8) to
340    rebuild the data "on the spot".
341 
342 */
343 
344 int
345 rf_ReconstructInPlace(RF_Raid_t *raidPtr, RF_RowCol_t col)
346 {
347 	RF_RaidDisk_t *spareDiskPtr = NULL;
348 	RF_RaidReconDesc_t *reconDesc;
349 	const RF_LayoutSW_t *lp;
350 	RF_ComponentLabel_t c_label;
351 	int     numDisksDone = 0, rc;
352 	struct partinfo dpart;
353 	struct vnode *vp;
354 	struct vattr va;
355 	struct proc *proc;
356 	int retcode;
357 	int ac;
358 
359 	lp = raidPtr->Layout.map;
360 	if (!lp->SubmitReconBuffer) {
361 		RF_ERRORMSG1("RECON: no way to reconstruct failed disk for arch %c\n",
362 			     lp->parityConfig);
363 		/* wakeup anyone who might be waiting to do a reconstruct */
364 		RF_SIGNAL_COND(raidPtr->waitForReconCond);
365 		return(EIO);
366 	}
367 
368 	/*
369 	 * The current infrastructure only supports reconstructing one
370 	 * disk at a time for each array.
371 	 */
372 	RF_LOCK_MUTEX(raidPtr->mutex);
373 
374 	if (raidPtr->Disks[col].status != rf_ds_failed) {
375 		/* "It's gone..." */
376 		raidPtr->numFailures++;
377 		raidPtr->Disks[col].status = rf_ds_failed;
378 		raidPtr->status = rf_rs_degraded;
379 		RF_UNLOCK_MUTEX(raidPtr->mutex);
380 		rf_update_component_labels(raidPtr,
381 					   RF_NORMAL_COMPONENT_UPDATE);
382 		RF_LOCK_MUTEX(raidPtr->mutex);
383 	}
384 
385 	while (raidPtr->reconInProgress) {
386 		RF_WAIT_COND(raidPtr->waitForReconCond, raidPtr->mutex);
387 	}
388 
389 	raidPtr->reconInProgress++;
390 
391 	/* first look for a spare drive onto which to reconstruct the
392 	   data.  spare disk descriptors are stored in row 0.  This
393 	   may have to change eventually */
394 
395 	/* Actually, we don't care if it's failed or not...  On a RAID
396 	   set with correct parity, this function should be callable
397 	   on any component without ill affects. */
398 	/* RF_ASSERT(raidPtr->Disks[col].status == rf_ds_failed); */
399 
400 #if RF_INCLUDE_PARITY_DECLUSTERING_DS > 0
401 	if (raidPtr->Layout.map->flags & RF_DISTRIBUTE_SPARE) {
402 		RF_ERRORMSG1("Unable to reconstruct to disk at col %d: operation not supported for RF_DISTRIBUTE_SPARE\n", col);
403 
404 		raidPtr->reconInProgress--;
405 		RF_UNLOCK_MUTEX(raidPtr->mutex);
406 		RF_SIGNAL_COND(raidPtr->waitForReconCond);
407 		return (EINVAL);
408 	}
409 #endif
410 	proc = raidPtr->engine_thread;
411 
412 	/* This device may have been opened successfully the
413 	   first time. Close it before trying to open it again.. */
414 
415 	if (raidPtr->raid_cinfo[col].ci_vp != NULL) {
416 #if 0
417 		printf("Closed the open device: %s\n",
418 		       raidPtr->Disks[col].devname);
419 #endif
420 		vp = raidPtr->raid_cinfo[col].ci_vp;
421 		ac = raidPtr->Disks[col].auto_configured;
422 		RF_UNLOCK_MUTEX(raidPtr->mutex);
423 		rf_close_component(raidPtr, vp, ac);
424 		RF_LOCK_MUTEX(raidPtr->mutex);
425 		raidPtr->raid_cinfo[col].ci_vp = NULL;
426 	}
427 	/* note that this disk was *not* auto_configured (any longer)*/
428 	raidPtr->Disks[col].auto_configured = 0;
429 
430 #if 0
431 	printf("About to (re-)open the device for rebuilding: %s\n",
432 	       raidPtr->Disks[col].devname);
433 #endif
434 	RF_UNLOCK_MUTEX(raidPtr->mutex);
435 	retcode = raidlookup(raidPtr->Disks[col].devname, proc, &vp);
436 
437 	if (retcode) {
438 		printf("raid%d: rebuilding: raidlookup on device: %s failed: %d!\n",raidPtr->raidid,
439 		       raidPtr->Disks[col].devname, retcode);
440 
441 		/* the component isn't responding properly...
442 		   must be still dead :-( */
443 		RF_LOCK_MUTEX(raidPtr->mutex);
444 		raidPtr->reconInProgress--;
445 		RF_UNLOCK_MUTEX(raidPtr->mutex);
446 		RF_SIGNAL_COND(raidPtr->waitForReconCond);
447 		return(retcode);
448 	}
449 
450 	/* Ok, so we can at least do a lookup...
451 	   How about actually getting a vp for it? */
452 
453 	if ((retcode = VOP_GETATTR(vp, &va, proc->p_ucred, proc)) != 0) {
454 		RF_LOCK_MUTEX(raidPtr->mutex);
455 		raidPtr->reconInProgress--;
456 		RF_UNLOCK_MUTEX(raidPtr->mutex);
457 		RF_SIGNAL_COND(raidPtr->waitForReconCond);
458 		return(retcode);
459 	}
460 
461 	retcode = VOP_IOCTL(vp, DIOCGPART, &dpart, FREAD, proc->p_ucred, proc);
462 	if (retcode) {
463 		RF_LOCK_MUTEX(raidPtr->mutex);
464 		raidPtr->reconInProgress--;
465 		RF_UNLOCK_MUTEX(raidPtr->mutex);
466 		RF_SIGNAL_COND(raidPtr->waitForReconCond);
467 		return(retcode);
468 	}
469 	RF_LOCK_MUTEX(raidPtr->mutex);
470 	raidPtr->Disks[col].blockSize =	dpart.disklab->d_secsize;
471 
472 	raidPtr->Disks[col].numBlocks = dpart.part->p_size -
473 		rf_protectedSectors;
474 
475 	raidPtr->raid_cinfo[col].ci_vp = vp;
476 	raidPtr->raid_cinfo[col].ci_dev = va.va_rdev;
477 
478 	raidPtr->Disks[col].dev = va.va_rdev;
479 
480 	/* we allow the user to specify that only a fraction
481 	   of the disks should be used this is just for debug:
482 	   it speeds up * the parity scan */
483 	raidPtr->Disks[col].numBlocks = raidPtr->Disks[col].numBlocks *
484 		rf_sizePercentage / 100;
485 	RF_UNLOCK_MUTEX(raidPtr->mutex);
486 
487 	spareDiskPtr = &raidPtr->Disks[col];
488 	spareDiskPtr->status = rf_ds_used_spare;
489 
490 	printf("raid%d: initiating in-place reconstruction on column %d\n",
491 	       raidPtr->raidid, col);
492 
493 	reconDesc = AllocRaidReconDesc((void *) raidPtr, col, spareDiskPtr,
494 				       numDisksDone, col);
495 	raidPtr->reconDesc = (void *) reconDesc;
496 #if RF_RECON_STATS > 0
497 	reconDesc->hsStallCount = 0;
498 	reconDesc->numReconExecDelays = 0;
499 	reconDesc->numReconEventWaits = 0;
500 #endif				/* RF_RECON_STATS > 0 */
501 	reconDesc->reconExecTimerRunning = 0;
502 	reconDesc->reconExecTicks = 0;
503 	reconDesc->maxReconExecTicks = 0;
504 	rc = rf_ContinueReconstructFailedDisk(reconDesc);
505 
506 	RF_LOCK_MUTEX(raidPtr->mutex);
507 	raidPtr->reconInProgress--;
508 	RF_UNLOCK_MUTEX(raidPtr->mutex);
509 
510 	if (!rc) {
511 		RF_LOCK_MUTEX(raidPtr->mutex);
512 		/* Need to set these here, as at this point it'll be claiming
513 		   that the disk is in rf_ds_spared!  But we know better :-) */
514 
515 		raidPtr->Disks[col].status = rf_ds_optimal;
516 		raidPtr->status = rf_rs_optimal;
517 		RF_UNLOCK_MUTEX(raidPtr->mutex);
518 
519 		/* fix up the component label */
520 		/* Don't actually need the read here.. */
521 		raidread_component_label(raidPtr->raid_cinfo[col].ci_dev,
522 					 raidPtr->raid_cinfo[col].ci_vp,
523 					 &c_label);
524 
525 		RF_LOCK_MUTEX(raidPtr->mutex);
526 		raid_init_component_label(raidPtr, &c_label);
527 
528 		c_label.row = 0;
529 		c_label.column = col;
530 
531 		/* We've just done a rebuild based on all the other
532 		   disks, so at this point the parity is known to be
533 		   clean, even if it wasn't before. */
534 
535 		/* XXX doesn't hold for RAID 6!!*/
536 
537 		raidPtr->parity_good = RF_RAID_CLEAN;
538 		RF_UNLOCK_MUTEX(raidPtr->mutex);
539 
540 		raidwrite_component_label(raidPtr->raid_cinfo[col].ci_dev,
541 					  raidPtr->raid_cinfo[col].ci_vp,
542 					  &c_label);
543 
544 		rf_update_component_labels(raidPtr,
545 					   RF_NORMAL_COMPONENT_UPDATE);
546 
547 	}
548 	RF_SIGNAL_COND(raidPtr->waitForReconCond);
549 	return (rc);
550 }
551 
552 
553 int
554 rf_ContinueReconstructFailedDisk(RF_RaidReconDesc_t *reconDesc)
555 {
556 	RF_Raid_t *raidPtr = reconDesc->raidPtr;
557 	RF_RowCol_t col = reconDesc->col;
558 	RF_RowCol_t scol = reconDesc->scol;
559 	RF_ReconMap_t *mapPtr;
560 	RF_ReconCtrl_t *tmp_reconctrl;
561 	RF_ReconEvent_t *event;
562 	struct timeval etime, elpsd;
563 	unsigned long xor_s, xor_resid_us;
564 	int     i, ds;
565 
566 	switch (reconDesc->state) {
567 
568 
569 	case 0:
570 
571 		raidPtr->accumXorTimeUs = 0;
572 #if RF_ACC_TRACE > 0
573 		/* create one trace record per physical disk */
574 		RF_Malloc(raidPtr->recon_tracerecs, raidPtr->numCol * sizeof(RF_AccTraceEntry_t), (RF_AccTraceEntry_t *));
575 #endif
576 
577 		/* quiesce the array prior to starting recon.  this is needed
578 		 * to assure no nasty interactions with pending user writes.
579 		 * We need to do this before we change the disk or row status. */
580 		reconDesc->state = 1;
581 
582 		Dprintf("RECON: begin request suspend\n");
583 		rf_SuspendNewRequestsAndWait(raidPtr);
584 		Dprintf("RECON: end request suspend\n");
585 
586 		/* fall through to state 1 */
587 
588 	case 1:
589 
590 		/* allocate our RF_ReconCTRL_t before we protect raidPtr->reconControl[row] */
591 		tmp_reconctrl = rf_MakeReconControl(reconDesc, col, scol);
592 
593 		RF_LOCK_MUTEX(raidPtr->mutex);
594 
595 		/* create the reconstruction control pointer and install it in
596 		 * the right slot */
597 		raidPtr->reconControl = tmp_reconctrl;
598 		mapPtr = raidPtr->reconControl->reconMap;
599 		raidPtr->status = rf_rs_reconstructing;
600 		raidPtr->Disks[col].status = rf_ds_reconstructing;
601 		raidPtr->Disks[col].spareCol = scol;
602 
603 		RF_UNLOCK_MUTEX(raidPtr->mutex);
604 
605 		RF_GETTIME(raidPtr->reconControl->starttime);
606 
607 		/* now start up the actual reconstruction: issue a read for
608 		 * each surviving disk */
609 
610 		reconDesc->numDisksDone = 0;
611 		for (i = 0; i < raidPtr->numCol; i++) {
612 			if (i != col) {
613 				/* find and issue the next I/O on the
614 				 * indicated disk */
615 				if (IssueNextReadRequest(raidPtr, i)) {
616 					Dprintf1("RECON: done issuing for c%d\n", i);
617 					reconDesc->numDisksDone++;
618 				}
619 			}
620 		}
621 
622 	case 2:
623 		Dprintf("RECON: resume requests\n");
624 		rf_ResumeNewRequests(raidPtr);
625 
626 
627 		reconDesc->state = 3;
628 
629 	case 3:
630 
631 		/* process reconstruction events until all disks report that
632 		 * they've completed all work */
633 		mapPtr = raidPtr->reconControl->reconMap;
634 
635 
636 
637 		while (reconDesc->numDisksDone < raidPtr->numCol - 1) {
638 
639 			event = rf_GetNextReconEvent(reconDesc, (void (*) (void *)) rf_ContinueReconstructFailedDisk, reconDesc);
640 			RF_ASSERT(event);
641 
642 			if (ProcessReconEvent(raidPtr, event))
643 				reconDesc->numDisksDone++;
644 			raidPtr->reconControl->numRUsTotal =
645 				mapPtr->totalRUs;
646 			raidPtr->reconControl->numRUsComplete =
647 				mapPtr->totalRUs -
648 				rf_UnitsLeftToReconstruct(mapPtr);
649 #if RF_DEBUG_RECON
650 			raidPtr->reconControl->percentComplete =
651 				(raidPtr->reconControl->numRUsComplete * 100 / raidPtr->reconControl->numRUsTotal);
652 			if (rf_prReconSched) {
653 				rf_PrintReconSchedule(raidPtr->reconControl->reconMap, &(raidPtr->reconControl->starttime));
654 			}
655 #endif
656 		}
657 
658 
659 
660 		reconDesc->state = 4;
661 
662 
663 	case 4:
664 		mapPtr = raidPtr->reconControl->reconMap;
665 		if (rf_reconDebug) {
666 			printf("RECON: all reads completed\n");
667 		}
668 		/* at this point all the reads have completed.  We now wait
669 		 * for any pending writes to complete, and then we're done */
670 
671 		while (rf_UnitsLeftToReconstruct(raidPtr->reconControl->reconMap) > 0) {
672 
673 			event = rf_GetNextReconEvent(reconDesc, (void (*) (void *)) rf_ContinueReconstructFailedDisk, reconDesc);
674 			RF_ASSERT(event);
675 
676 			(void) ProcessReconEvent(raidPtr, event);	/* ignore return code */
677 #if RF_DEBUG_RECON
678 			raidPtr->reconControl->percentComplete = 100 - (rf_UnitsLeftToReconstruct(mapPtr) * 100 / mapPtr->totalRUs);
679 			if (rf_prReconSched) {
680 				rf_PrintReconSchedule(raidPtr->reconControl->reconMap, &(raidPtr->reconControl->starttime));
681 			}
682 #endif
683 		}
684 		reconDesc->state = 5;
685 
686 	case 5:
687 		/* Success:  mark the dead disk as reconstructed.  We quiesce
688 		 * the array here to assure no nasty interactions with pending
689 		 * user accesses when we free up the psstatus structure as
690 		 * part of FreeReconControl() */
691 
692 		reconDesc->state = 6;
693 
694 		rf_SuspendNewRequestsAndWait(raidPtr);
695 
696 		/* fall through to state 6 */
697 	case 6:
698 
699 
700 
701 		RF_LOCK_MUTEX(raidPtr->mutex);
702 		raidPtr->numFailures--;
703 		ds = (raidPtr->Layout.map->flags & RF_DISTRIBUTE_SPARE);
704 		raidPtr->Disks[col].status = (ds) ? rf_ds_dist_spared : rf_ds_spared;
705 		raidPtr->status = (ds) ? rf_rs_reconfigured : rf_rs_optimal;
706 		RF_UNLOCK_MUTEX(raidPtr->mutex);
707 		RF_GETTIME(etime);
708 		RF_TIMEVAL_DIFF(&(raidPtr->reconControl->starttime), &etime, &elpsd);
709 
710 		/* XXX -- why is state 7 different from state 6 if there is no
711 		 * return() here? -- XXX Note that I set elpsd above & use it
712 		 * below, so if you put a return here you'll have to fix this.
713 		 * (also, FreeReconControl is called below) */
714 
715 	case 7:
716 
717 		rf_ResumeNewRequests(raidPtr);
718 
719 		printf("raid%d: Reconstruction of disk at col %d completed\n",
720 		       raidPtr->raidid, col);
721 		xor_s = raidPtr->accumXorTimeUs / 1000000;
722 		xor_resid_us = raidPtr->accumXorTimeUs % 1000000;
723 		printf("raid%d: Recon time was %d.%06d seconds, accumulated XOR time was %ld us (%ld.%06ld)\n",
724 		       raidPtr->raidid,
725 		       (int) elpsd.tv_sec, (int) elpsd.tv_usec,
726 		       raidPtr->accumXorTimeUs, xor_s, xor_resid_us);
727 		printf("raid%d:  (start time %d sec %d usec, end time %d sec %d usec)\n",
728 		       raidPtr->raidid,
729 		       (int) raidPtr->reconControl->starttime.tv_sec,
730 		       (int) raidPtr->reconControl->starttime.tv_usec,
731 		       (int) etime.tv_sec, (int) etime.tv_usec);
732 
733 #if RF_RECON_STATS > 0
734 		printf("raid%d: Total head-sep stall count was %d\n",
735 		       raidPtr->raidid, (int) reconDesc->hsStallCount);
736 #endif				/* RF_RECON_STATS > 0 */
737 		rf_FreeReconControl(raidPtr);
738 #if RF_ACC_TRACE > 0
739 		RF_Free(raidPtr->recon_tracerecs, raidPtr->numCol * sizeof(RF_AccTraceEntry_t));
740 #endif
741 		FreeReconDesc(reconDesc);
742 
743 	}
744 
745 	return (0);
746 }
747 /*****************************************************************************
748  * do the right thing upon each reconstruction event.
749  * returns nonzero if and only if there is nothing left unread on the
750  * indicated disk
751  *****************************************************************************/
752 static int
753 ProcessReconEvent(RF_Raid_t *raidPtr, RF_ReconEvent_t *event)
754 {
755 	int     retcode = 0, submitblocked;
756 	RF_ReconBuffer_t *rbuf;
757 	RF_SectorCount_t sectorsPerRU;
758 
759 	Dprintf1("RECON: ProcessReconEvent type %d\n", event->type);
760 	switch (event->type) {
761 
762 		/* a read I/O has completed */
763 	case RF_REVENT_READDONE:
764 		rbuf = raidPtr->reconControl->perDiskInfo[event->col].rbuf;
765 		Dprintf2("RECON: READDONE EVENT: col %d psid %ld\n",
766 		    event->col, rbuf->parityStripeID);
767 		Dprintf7("RECON: done read  psid %ld buf %lx  %02x %02x %02x %02x %02x\n",
768 		    rbuf->parityStripeID, rbuf->buffer, rbuf->buffer[0] & 0xff, rbuf->buffer[1] & 0xff,
769 		    rbuf->buffer[2] & 0xff, rbuf->buffer[3] & 0xff, rbuf->buffer[4] & 0xff);
770 		rf_FreeDiskQueueData((RF_DiskQueueData_t *) rbuf->arg);
771 		submitblocked = rf_SubmitReconBuffer(rbuf, 0, 0);
772 		Dprintf1("RECON: submitblocked=%d\n", submitblocked);
773 		if (!submitblocked)
774 			retcode = IssueNextReadRequest(raidPtr, event->col);
775 		break;
776 
777 		/* a write I/O has completed */
778 	case RF_REVENT_WRITEDONE:
779 #if RF_DEBUG_RECON
780 		if (rf_floatingRbufDebug) {
781 			rf_CheckFloatingRbufCount(raidPtr, 1);
782 		}
783 #endif
784 		sectorsPerRU = raidPtr->Layout.sectorsPerStripeUnit * raidPtr->Layout.SUsPerRU;
785 		rbuf = (RF_ReconBuffer_t *) event->arg;
786 		rf_FreeDiskQueueData((RF_DiskQueueData_t *) rbuf->arg);
787 		Dprintf3("RECON: WRITEDONE EVENT: psid %d ru %d (%d %% complete)\n",
788 		    rbuf->parityStripeID, rbuf->which_ru, raidPtr->reconControl->percentComplete);
789 		rf_ReconMapUpdate(raidPtr, raidPtr->reconControl->reconMap,
790 		    rbuf->failedDiskSectorOffset, rbuf->failedDiskSectorOffset + sectorsPerRU - 1);
791 		rf_RemoveFromActiveReconTable(raidPtr, rbuf->parityStripeID, rbuf->which_ru);
792 
793 		if (rbuf->type == RF_RBUF_TYPE_FLOATING) {
794 			RF_LOCK_MUTEX(raidPtr->reconControl->rb_mutex);
795 			while(raidPtr->reconControl->rb_lock) {
796 				ltsleep(&raidPtr->reconControl->rb_lock, PRIBIO, "reconctrlpre1", 0,
797 					&raidPtr->reconControl->rb_mutex);
798 			}
799 			raidPtr->reconControl->rb_lock = 1;
800 			RF_UNLOCK_MUTEX(raidPtr->reconControl->rb_mutex);
801 
802 			raidPtr->numFullReconBuffers--;
803 			rf_ReleaseFloatingReconBuffer(raidPtr, rbuf);
804 
805 			RF_LOCK_MUTEX(raidPtr->reconControl->rb_mutex);
806 			raidPtr->reconControl->rb_lock = 0;
807 			wakeup(&raidPtr->reconControl->rb_lock);
808 			RF_UNLOCK_MUTEX(raidPtr->reconControl->rb_mutex);
809 		} else
810 			if (rbuf->type == RF_RBUF_TYPE_FORCED)
811 				rf_FreeReconBuffer(rbuf);
812 			else
813 				RF_ASSERT(0);
814 		break;
815 
816 	case RF_REVENT_BUFCLEAR:	/* A buffer-stall condition has been
817 					 * cleared */
818 		Dprintf1("RECON: BUFCLEAR EVENT: col %d\n", event->col);
819 		submitblocked = rf_SubmitReconBuffer(raidPtr->reconControl->perDiskInfo[event->col].rbuf, 0, (int) (long) event->arg);
820 		RF_ASSERT(!submitblocked);	/* we wouldn't have gotten the
821 						 * BUFCLEAR event if we
822 						 * couldn't submit */
823 		retcode = IssueNextReadRequest(raidPtr, event->col);
824 		break;
825 
826 	case RF_REVENT_BLOCKCLEAR:	/* A user-write reconstruction
827 					 * blockage has been cleared */
828 		DDprintf1("RECON: BLOCKCLEAR EVENT: col %d\n", event->col);
829 		retcode = TryToRead(raidPtr, event->col);
830 		break;
831 
832 	case RF_REVENT_HEADSEPCLEAR:	/* A max-head-separation
833 					 * reconstruction blockage has been
834 					 * cleared */
835 		Dprintf1("RECON: HEADSEPCLEAR EVENT: col %d\n", event->col);
836 		retcode = TryToRead(raidPtr, event->col);
837 		break;
838 
839 		/* a buffer has become ready to write */
840 	case RF_REVENT_BUFREADY:
841 		Dprintf1("RECON: BUFREADY EVENT: col %d\n", event->col);
842 		retcode = IssueNextWriteRequest(raidPtr);
843 #if RF_DEBUG_RECON
844 		if (rf_floatingRbufDebug) {
845 			rf_CheckFloatingRbufCount(raidPtr, 1);
846 		}
847 #endif
848 		break;
849 
850 		/* we need to skip the current RU entirely because it got
851 		 * recon'd while we were waiting for something else to happen */
852 	case RF_REVENT_SKIP:
853 		DDprintf1("RECON: SKIP EVENT: col %d\n", event->col);
854 		retcode = IssueNextReadRequest(raidPtr, event->col);
855 		break;
856 
857 		/* a forced-reconstruction read access has completed.  Just
858 		 * submit the buffer */
859 	case RF_REVENT_FORCEDREADDONE:
860 		rbuf = (RF_ReconBuffer_t *) event->arg;
861 		rf_FreeDiskQueueData((RF_DiskQueueData_t *) rbuf->arg);
862 		DDprintf1("RECON: FORCEDREADDONE EVENT: col %d\n", event->col);
863 		submitblocked = rf_SubmitReconBuffer(rbuf, 1, 0);
864 		RF_ASSERT(!submitblocked);
865 		break;
866 
867 		/* A read I/O failed to complete */
868 	case RF_REVENT_READ_FAILED:
869 		/* fallthru to panic... */
870 
871 		/* A write I/O failed to complete */
872 	case RF_REVENT_WRITE_FAILED:
873 		/* fallthru to panic... */
874 
875 		/* a forced read I/O failed to complete */
876 	case RF_REVENT_FORCEDREAD_FAILED:
877 		/* fallthru to panic... */
878 
879 	default:
880 		RF_PANIC();
881 	}
882 	rf_FreeReconEventDesc(event);
883 	return (retcode);
884 }
885 /*****************************************************************************
886  *
887  * find the next thing that's needed on the indicated disk, and issue
888  * a read request for it.  We assume that the reconstruction buffer
889  * associated with this process is free to receive the data.  If
890  * reconstruction is blocked on the indicated RU, we issue a
891  * blockage-release request instead of a physical disk read request.
892  * If the current disk gets too far ahead of the others, we issue a
893  * head-separation wait request and return.
894  *
895  * ctrl->{ru_count, curPSID, diskOffset} and
896  * rbuf->failedDiskSectorOffset are maintained to point to the unit
897  * we're currently accessing.  Note that this deviates from the
898  * standard C idiom of having counters point to the next thing to be
899  * accessed.  This allows us to easily retry when we're blocked by
900  * head separation or reconstruction-blockage events.
901  *
902  * returns nonzero if and only if there is nothing left unread on the
903  * indicated disk
904  *
905  *****************************************************************************/
906 static int
907 IssueNextReadRequest(RF_Raid_t *raidPtr, RF_RowCol_t col)
908 {
909 	RF_PerDiskReconCtrl_t *ctrl = &raidPtr->reconControl->perDiskInfo[col];
910 	RF_RaidLayout_t *layoutPtr = &raidPtr->Layout;
911 	RF_ReconBuffer_t *rbuf = ctrl->rbuf;
912 	RF_ReconUnitCount_t RUsPerPU = layoutPtr->SUsPerPU / layoutPtr->SUsPerRU;
913 	RF_SectorCount_t sectorsPerRU = layoutPtr->sectorsPerStripeUnit * layoutPtr->SUsPerRU;
914 	int     do_new_check = 0, retcode = 0, status;
915 
916 	/* if we are currently the slowest disk, mark that we have to do a new
917 	 * check */
918 	if (ctrl->headSepCounter <= raidPtr->reconControl->minHeadSepCounter)
919 		do_new_check = 1;
920 
921 	while (1) {
922 
923 		ctrl->ru_count++;
924 		if (ctrl->ru_count < RUsPerPU) {
925 			ctrl->diskOffset += sectorsPerRU;
926 			rbuf->failedDiskSectorOffset += sectorsPerRU;
927 		} else {
928 			ctrl->curPSID++;
929 			ctrl->ru_count = 0;
930 			/* code left over from when head-sep was based on
931 			 * parity stripe id */
932 			if (ctrl->curPSID >= raidPtr->reconControl->lastPSID) {
933 				CheckForNewMinHeadSep(raidPtr, ++(ctrl->headSepCounter));
934 				return (1);	/* finito! */
935 			}
936 			/* find the disk offsets of the start of the parity
937 			 * stripe on both the current disk and the failed
938 			 * disk. skip this entire parity stripe if either disk
939 			 * does not appear in the indicated PS */
940 			status = ComputePSDiskOffsets(raidPtr, ctrl->curPSID, col, &ctrl->diskOffset, &rbuf->failedDiskSectorOffset,
941 			    &rbuf->spCol, &rbuf->spOffset);
942 			if (status) {
943 				ctrl->ru_count = RUsPerPU - 1;
944 				continue;
945 			}
946 		}
947 		rbuf->which_ru = ctrl->ru_count;
948 
949 		/* skip this RU if it's already been reconstructed */
950 		if (rf_CheckRUReconstructed(raidPtr->reconControl->reconMap, rbuf->failedDiskSectorOffset)) {
951 			Dprintf2("Skipping psid %ld ru %d: already reconstructed\n", ctrl->curPSID, ctrl->ru_count);
952 			continue;
953 		}
954 		break;
955 	}
956 	ctrl->headSepCounter++;
957 	if (do_new_check)
958 		CheckForNewMinHeadSep(raidPtr, ctrl->headSepCounter);	/* update min if needed */
959 
960 
961 	/* at this point, we have definitely decided what to do, and we have
962 	 * only to see if we can actually do it now */
963 	rbuf->parityStripeID = ctrl->curPSID;
964 	rbuf->which_ru = ctrl->ru_count;
965 #if RF_ACC_TRACE > 0
966 	memset((char *) &raidPtr->recon_tracerecs[col], 0,
967 	    sizeof(raidPtr->recon_tracerecs[col]));
968 	raidPtr->recon_tracerecs[col].reconacc = 1;
969 	RF_ETIMER_START(raidPtr->recon_tracerecs[col].recon_timer);
970 #endif
971 	retcode = TryToRead(raidPtr, col);
972 	return (retcode);
973 }
974 
975 /*
976  * tries to issue the next read on the indicated disk.  We may be
977  * blocked by (a) the heads being too far apart, or (b) recon on the
978  * indicated RU being blocked due to a write by a user thread.  In
979  * this case, we issue a head-sep or blockage wait request, which will
980  * cause this same routine to be invoked again later when the blockage
981  * has cleared.
982  */
983 
984 static int
985 TryToRead(RF_Raid_t *raidPtr, RF_RowCol_t col)
986 {
987 	RF_PerDiskReconCtrl_t *ctrl = &raidPtr->reconControl->perDiskInfo[col];
988 	RF_SectorCount_t sectorsPerRU = raidPtr->Layout.sectorsPerStripeUnit * raidPtr->Layout.SUsPerRU;
989 	RF_StripeNum_t psid = ctrl->curPSID;
990 	RF_ReconUnitNum_t which_ru = ctrl->ru_count;
991 	RF_DiskQueueData_t *req;
992 	int     status;
993 	RF_ReconParityStripeStatus_t *pssPtr, *newpssPtr;
994 
995 	/* if the current disk is too far ahead of the others, issue a
996 	 * head-separation wait and return */
997 	if (CheckHeadSeparation(raidPtr, ctrl, col, ctrl->headSepCounter, which_ru))
998 		return (0);
999 
1000 	/* allocate a new PSS in case we need it */
1001 	newpssPtr = rf_AllocPSStatus(raidPtr);
1002 
1003 	RF_LOCK_PSS_MUTEX(raidPtr, psid);
1004 	pssPtr = rf_LookupRUStatus(raidPtr, raidPtr->reconControl->pssTable, psid, which_ru, RF_PSS_CREATE, newpssPtr);
1005 
1006 	if (pssPtr != newpssPtr) {
1007 		rf_FreePSStatus(raidPtr, newpssPtr);
1008 	}
1009 
1010 	/* if recon is blocked on the indicated parity stripe, issue a
1011 	 * block-wait request and return. this also must mark the indicated RU
1012 	 * in the stripe as under reconstruction if not blocked. */
1013 	status = CheckForcedOrBlockedReconstruction(raidPtr, pssPtr, ctrl, col, psid, which_ru);
1014 	if (status == RF_PSS_RECON_BLOCKED) {
1015 		Dprintf2("RECON: Stalling psid %ld ru %d: recon blocked\n", psid, which_ru);
1016 		goto out;
1017 	} else
1018 		if (status == RF_PSS_FORCED_ON_WRITE) {
1019 			rf_CauseReconEvent(raidPtr, col, NULL, RF_REVENT_SKIP);
1020 			goto out;
1021 		}
1022 	/* make one last check to be sure that the indicated RU didn't get
1023 	 * reconstructed while we were waiting for something else to happen.
1024 	 * This is unfortunate in that it causes us to make this check twice
1025 	 * in the normal case.  Might want to make some attempt to re-work
1026 	 * this so that we only do this check if we've definitely blocked on
1027 	 * one of the above checks.  When this condition is detected, we may
1028 	 * have just created a bogus status entry, which we need to delete. */
1029 	if (rf_CheckRUReconstructed(raidPtr->reconControl->reconMap, ctrl->rbuf->failedDiskSectorOffset)) {
1030 		Dprintf2("RECON: Skipping psid %ld ru %d: prior recon after stall\n", psid, which_ru);
1031 		if (pssPtr == newpssPtr)
1032 			rf_PSStatusDelete(raidPtr, raidPtr->reconControl->pssTable, pssPtr);
1033 		rf_CauseReconEvent(raidPtr, col, NULL, RF_REVENT_SKIP);
1034 		goto out;
1035 	}
1036 	/* found something to read.  issue the I/O */
1037 	Dprintf4("RECON: Read for psid %ld on col %d offset %ld buf %lx\n",
1038 	    psid, col, ctrl->diskOffset, ctrl->rbuf->buffer);
1039 #if RF_ACC_TRACE > 0
1040 	RF_ETIMER_STOP(raidPtr->recon_tracerecs[col].recon_timer);
1041 	RF_ETIMER_EVAL(raidPtr->recon_tracerecs[col].recon_timer);
1042 	raidPtr->recon_tracerecs[col].specific.recon.recon_start_to_fetch_us =
1043 	    RF_ETIMER_VAL_US(raidPtr->recon_tracerecs[col].recon_timer);
1044 	RF_ETIMER_START(raidPtr->recon_tracerecs[col].recon_timer);
1045 #endif
1046 	/* should be ok to use a NULL proc pointer here, all the bufs we use
1047 	 * should be in kernel space */
1048 	req = rf_CreateDiskQueueData(RF_IO_TYPE_READ, ctrl->diskOffset, sectorsPerRU, ctrl->rbuf->buffer, psid, which_ru,
1049 	    ReconReadDoneProc, (void *) ctrl, NULL,
1050 #if RF_ACC_TRACE > 0
1051 				     &raidPtr->recon_tracerecs[col],
1052 #else
1053 				     NULL,
1054 #endif
1055 				     (void *) raidPtr, 0, NULL);
1056 
1057 	RF_ASSERT(req);		/* XXX -- fix this -- XXX */
1058 
1059 	ctrl->rbuf->arg = (void *) req;
1060 	rf_DiskIOEnqueue(&raidPtr->Queues[col], req, RF_IO_RECON_PRIORITY);
1061 	pssPtr->issued[col] = 1;
1062 
1063 out:
1064 	RF_UNLOCK_PSS_MUTEX(raidPtr, psid);
1065 	return (0);
1066 }
1067 
1068 
1069 /*
1070  * given a parity stripe ID, we want to find out whether both the
1071  * current disk and the failed disk exist in that parity stripe.  If
1072  * not, we want to skip this whole PS.  If so, we want to find the
1073  * disk offset of the start of the PS on both the current disk and the
1074  * failed disk.
1075  *
1076  * this works by getting a list of disks comprising the indicated
1077  * parity stripe, and searching the list for the current and failed
1078  * disks.  Once we've decided they both exist in the parity stripe, we
1079  * need to decide whether each is data or parity, so that we'll know
1080  * which mapping function to call to get the corresponding disk
1081  * offsets.
1082  *
1083  * this is kind of unpleasant, but doing it this way allows the
1084  * reconstruction code to use parity stripe IDs rather than physical
1085  * disks address to march through the failed disk, which greatly
1086  * simplifies a lot of code, as well as eliminating the need for a
1087  * reverse-mapping function.  I also think it will execute faster,
1088  * since the calls to the mapping module are kept to a minimum.
1089  *
1090  * ASSUMES THAT THE STRIPE IDENTIFIER IDENTIFIES THE DISKS COMPRISING
1091  * THE STRIPE IN THE CORRECT ORDER
1092  *
1093  * raidPtr          - raid descriptor
1094  * psid             - parity stripe identifier
1095  * col              - column of disk to find the offsets for
1096  * spCol            - out: col of spare unit for failed unit
1097  * spOffset         - out: offset into disk containing spare unit
1098  *
1099  */
1100 
1101 
1102 static int
1103 ComputePSDiskOffsets(RF_Raid_t *raidPtr, RF_StripeNum_t psid,
1104 		     RF_RowCol_t col, RF_SectorNum_t *outDiskOffset,
1105 		     RF_SectorNum_t *outFailedDiskSectorOffset,
1106 		     RF_RowCol_t *spCol, RF_SectorNum_t *spOffset)
1107 {
1108 	RF_RaidLayout_t *layoutPtr = &raidPtr->Layout;
1109 	RF_RowCol_t fcol = raidPtr->reconControl->fcol;
1110 	RF_RaidAddr_t sosRaidAddress;	/* start-of-stripe */
1111 	RF_RowCol_t *diskids;
1112 	u_int   i, j, k, i_offset, j_offset;
1113 	RF_RowCol_t pcol;
1114 	int     testcol;
1115 	RF_SectorNum_t poffset;
1116 	char    i_is_parity = 0, j_is_parity = 0;
1117 	RF_RowCol_t stripeWidth = layoutPtr->numDataCol + layoutPtr->numParityCol;
1118 
1119 	/* get a listing of the disks comprising that stripe */
1120 	sosRaidAddress = rf_ParityStripeIDToRaidAddress(layoutPtr, psid);
1121 	(layoutPtr->map->IdentifyStripe) (raidPtr, sosRaidAddress, &diskids);
1122 	RF_ASSERT(diskids);
1123 
1124 	/* reject this entire parity stripe if it does not contain the
1125 	 * indicated disk or it does not contain the failed disk */
1126 
1127 	for (i = 0; i < stripeWidth; i++) {
1128 		if (col == diskids[i])
1129 			break;
1130 	}
1131 	if (i == stripeWidth)
1132 		goto skipit;
1133 	for (j = 0; j < stripeWidth; j++) {
1134 		if (fcol == diskids[j])
1135 			break;
1136 	}
1137 	if (j == stripeWidth) {
1138 		goto skipit;
1139 	}
1140 	/* find out which disk the parity is on */
1141 	(layoutPtr->map->MapParity) (raidPtr, sosRaidAddress, &pcol, &poffset, RF_DONT_REMAP);
1142 
1143 	/* find out if either the current RU or the failed RU is parity */
1144 	/* also, if the parity occurs in this stripe prior to the data and/or
1145 	 * failed col, we need to decrement i and/or j */
1146 	for (k = 0; k < stripeWidth; k++)
1147 		if (diskids[k] == pcol)
1148 			break;
1149 	RF_ASSERT(k < stripeWidth);
1150 	i_offset = i;
1151 	j_offset = j;
1152 	if (k < i)
1153 		i_offset--;
1154 	else
1155 		if (k == i) {
1156 			i_is_parity = 1;
1157 			i_offset = 0;
1158 		}		/* set offsets to zero to disable multiply
1159 				 * below */
1160 	if (k < j)
1161 		j_offset--;
1162 	else
1163 		if (k == j) {
1164 			j_is_parity = 1;
1165 			j_offset = 0;
1166 		}
1167 	/* at this point, [ij]_is_parity tells us whether the [current,failed]
1168 	 * disk is parity at the start of this RU, and, if data, "[ij]_offset"
1169 	 * tells us how far into the stripe the [current,failed] disk is. */
1170 
1171 	/* call the mapping routine to get the offset into the current disk,
1172 	 * repeat for failed disk. */
1173 	if (i_is_parity)
1174 		layoutPtr->map->MapParity(raidPtr, sosRaidAddress + i_offset * layoutPtr->sectorsPerStripeUnit, &testcol, outDiskOffset, RF_DONT_REMAP);
1175 	else
1176 		layoutPtr->map->MapSector(raidPtr, sosRaidAddress + i_offset * layoutPtr->sectorsPerStripeUnit, &testcol, outDiskOffset, RF_DONT_REMAP);
1177 
1178 	RF_ASSERT(col == testcol);
1179 
1180 	if (j_is_parity)
1181 		layoutPtr->map->MapParity(raidPtr, sosRaidAddress + j_offset * layoutPtr->sectorsPerStripeUnit, &testcol, outFailedDiskSectorOffset, RF_DONT_REMAP);
1182 	else
1183 		layoutPtr->map->MapSector(raidPtr, sosRaidAddress + j_offset * layoutPtr->sectorsPerStripeUnit, &testcol, outFailedDiskSectorOffset, RF_DONT_REMAP);
1184 	RF_ASSERT(fcol == testcol);
1185 
1186 	/* now locate the spare unit for the failed unit */
1187 #if RF_INCLUDE_PARITY_DECLUSTERING_DS > 0
1188 	if (layoutPtr->map->flags & RF_DISTRIBUTE_SPARE) {
1189 		if (j_is_parity)
1190 			layoutPtr->map->MapParity(raidPtr, sosRaidAddress + j_offset * layoutPtr->sectorsPerStripeUnit, spCol, spOffset, RF_REMAP);
1191 		else
1192 			layoutPtr->map->MapSector(raidPtr, sosRaidAddress + j_offset * layoutPtr->sectorsPerStripeUnit, spCol, spOffset, RF_REMAP);
1193 	} else {
1194 #endif
1195 		*spCol = raidPtr->reconControl->spareCol;
1196 		*spOffset = *outFailedDiskSectorOffset;
1197 #if RF_INCLUDE_PARITY_DECLUSTERING_DS > 0
1198 	}
1199 #endif
1200 	return (0);
1201 
1202 skipit:
1203 	Dprintf2("RECON: Skipping psid %ld: nothing needed from r%d c%d\n",
1204 	    psid, col);
1205 	return (1);
1206 }
1207 /* this is called when a buffer has become ready to write to the replacement disk */
1208 static int
1209 IssueNextWriteRequest(RF_Raid_t *raidPtr)
1210 {
1211 	RF_RaidLayout_t *layoutPtr = &raidPtr->Layout;
1212 	RF_SectorCount_t sectorsPerRU = layoutPtr->sectorsPerStripeUnit * layoutPtr->SUsPerRU;
1213 #if RF_ACC_TRACE > 0
1214 	RF_RowCol_t fcol = raidPtr->reconControl->fcol;
1215 #endif
1216 	RF_ReconBuffer_t *rbuf;
1217 	RF_DiskQueueData_t *req;
1218 
1219 	rbuf = rf_GetFullReconBuffer(raidPtr->reconControl);
1220 	RF_ASSERT(rbuf);	/* there must be one available, or we wouldn't
1221 				 * have gotten the event that sent us here */
1222 	RF_ASSERT(rbuf->pssPtr);
1223 
1224 	rbuf->pssPtr->writeRbuf = rbuf;
1225 	rbuf->pssPtr = NULL;
1226 
1227 	Dprintf6("RECON: New write (c %d offs %d) for psid %ld ru %d (failed disk offset %ld) buf %lx\n",
1228 	    rbuf->spCol, rbuf->spOffset, rbuf->parityStripeID,
1229 	    rbuf->which_ru, rbuf->failedDiskSectorOffset, rbuf->buffer);
1230 	Dprintf6("RECON: new write psid %ld   %02x %02x %02x %02x %02x\n",
1231 	    rbuf->parityStripeID, rbuf->buffer[0] & 0xff, rbuf->buffer[1] & 0xff,
1232 	    rbuf->buffer[2] & 0xff, rbuf->buffer[3] & 0xff, rbuf->buffer[4] & 0xff);
1233 
1234 	/* should be ok to use a NULL b_proc here b/c all addrs should be in
1235 	 * kernel space */
1236 	req = rf_CreateDiskQueueData(RF_IO_TYPE_WRITE, rbuf->spOffset,
1237 	    sectorsPerRU, rbuf->buffer,
1238 	    rbuf->parityStripeID, rbuf->which_ru,
1239 	    ReconWriteDoneProc, (void *) rbuf, NULL,
1240 #if RF_ACC_TRACE > 0
1241 	    &raidPtr->recon_tracerecs[fcol],
1242 #else
1243 				     NULL,
1244 #endif
1245 	    (void *) raidPtr, 0, NULL);
1246 
1247 	RF_ASSERT(req);		/* XXX -- fix this -- XXX */
1248 
1249 	rbuf->arg = (void *) req;
1250 	rf_DiskIOEnqueue(&raidPtr->Queues[rbuf->spCol], req, RF_IO_RECON_PRIORITY);
1251 
1252 	return (0);
1253 }
1254 
1255 /*
1256  * this gets called upon the completion of a reconstruction read
1257  * operation the arg is a pointer to the per-disk reconstruction
1258  * control structure for the process that just finished a read.
1259  *
1260  * called at interrupt context in the kernel, so don't do anything
1261  * illegal here.
1262  */
1263 static int
1264 ReconReadDoneProc(void *arg, int status)
1265 {
1266 	RF_PerDiskReconCtrl_t *ctrl = (RF_PerDiskReconCtrl_t *) arg;
1267 	RF_Raid_t *raidPtr = ctrl->reconCtrl->reconDesc->raidPtr;
1268 
1269 	if (status) {
1270 		printf("raid%d: Recon read failed!\n", raidPtr->raidid);
1271 		rf_CauseReconEvent(raidPtr, ctrl->col, NULL, RF_REVENT_READ_FAILED);
1272 		return(0);
1273 	}
1274 #if RF_ACC_TRACE > 0
1275 	RF_ETIMER_STOP(raidPtr->recon_tracerecs[ctrl->col].recon_timer);
1276 	RF_ETIMER_EVAL(raidPtr->recon_tracerecs[ctrl->col].recon_timer);
1277 	raidPtr->recon_tracerecs[ctrl->col].specific.recon.recon_fetch_to_return_us =
1278 	    RF_ETIMER_VAL_US(raidPtr->recon_tracerecs[ctrl->col].recon_timer);
1279 	RF_ETIMER_START(raidPtr->recon_tracerecs[ctrl->col].recon_timer);
1280 #endif
1281 	rf_CauseReconEvent(raidPtr, ctrl->col, NULL, RF_REVENT_READDONE);
1282 	return (0);
1283 }
1284 /* this gets called upon the completion of a reconstruction write operation.
1285  * the arg is a pointer to the rbuf that was just written
1286  *
1287  * called at interrupt context in the kernel, so don't do anything illegal here.
1288  */
1289 static int
1290 ReconWriteDoneProc(void *arg, int status)
1291 {
1292 	RF_ReconBuffer_t *rbuf = (RF_ReconBuffer_t *) arg;
1293 
1294 	Dprintf2("Reconstruction completed on psid %ld ru %d\n", rbuf->parityStripeID, rbuf->which_ru);
1295 	if (status) {
1296 		printf("raid%d: Recon write failed!\n", rbuf->raidPtr->raidid);
1297 		rf_CauseReconEvent(rbuf->raidPtr, rbuf->col, arg, RF_REVENT_WRITE_FAILED);
1298 		return(0);
1299 	}
1300 	rf_CauseReconEvent(rbuf->raidPtr, rbuf->col, arg, RF_REVENT_WRITEDONE);
1301 	return (0);
1302 }
1303 
1304 
1305 /*
1306  * computes a new minimum head sep, and wakes up anyone who needs to
1307  * be woken as a result
1308  */
1309 static void
1310 CheckForNewMinHeadSep(RF_Raid_t *raidPtr, RF_HeadSepLimit_t hsCtr)
1311 {
1312 	RF_ReconCtrl_t *reconCtrlPtr = raidPtr->reconControl;
1313 	RF_HeadSepLimit_t new_min;
1314 	RF_RowCol_t i;
1315 	RF_CallbackDesc_t *p;
1316 	RF_ASSERT(hsCtr >= reconCtrlPtr->minHeadSepCounter);	/* from the definition
1317 								 * of a minimum */
1318 
1319 
1320 	RF_LOCK_MUTEX(reconCtrlPtr->rb_mutex);
1321 	while(reconCtrlPtr->rb_lock) {
1322 		ltsleep(&reconCtrlPtr->rb_lock, PRIBIO, "reconctlcnmhs", 0, &reconCtrlPtr->rb_mutex);
1323 	}
1324 	reconCtrlPtr->rb_lock = 1;
1325 	RF_UNLOCK_MUTEX(reconCtrlPtr->rb_mutex);
1326 
1327 	new_min = ~(1L << (8 * sizeof(long) - 1));	/* 0x7FFF....FFF */
1328 	for (i = 0; i < raidPtr->numCol; i++)
1329 		if (i != reconCtrlPtr->fcol) {
1330 			if (reconCtrlPtr->perDiskInfo[i].headSepCounter < new_min)
1331 				new_min = reconCtrlPtr->perDiskInfo[i].headSepCounter;
1332 		}
1333 	/* set the new minimum and wake up anyone who can now run again */
1334 	if (new_min != reconCtrlPtr->minHeadSepCounter) {
1335 		reconCtrlPtr->minHeadSepCounter = new_min;
1336 		Dprintf1("RECON:  new min head pos counter val is %ld\n", new_min);
1337 		while (reconCtrlPtr->headSepCBList) {
1338 			if (reconCtrlPtr->headSepCBList->callbackArg.v > new_min)
1339 				break;
1340 			p = reconCtrlPtr->headSepCBList;
1341 			reconCtrlPtr->headSepCBList = p->next;
1342 			p->next = NULL;
1343 			rf_CauseReconEvent(raidPtr, p->col, NULL, RF_REVENT_HEADSEPCLEAR);
1344 			rf_FreeCallbackDesc(p);
1345 		}
1346 
1347 	}
1348 	RF_LOCK_MUTEX(reconCtrlPtr->rb_mutex);
1349 	reconCtrlPtr->rb_lock = 0;
1350 	wakeup(&reconCtrlPtr->rb_lock);
1351 	RF_UNLOCK_MUTEX(reconCtrlPtr->rb_mutex);
1352 }
1353 
1354 /*
1355  * checks to see that the maximum head separation will not be violated
1356  * if we initiate a reconstruction I/O on the indicated disk.
1357  * Limiting the maximum head separation between two disks eliminates
1358  * the nasty buffer-stall conditions that occur when one disk races
1359  * ahead of the others and consumes all of the floating recon buffers.
1360  * This code is complex and unpleasant but it's necessary to avoid
1361  * some very nasty, albeit fairly rare, reconstruction behavior.
1362  *
1363  * returns non-zero if and only if we have to stop working on the
1364  * indicated disk due to a head-separation delay.
1365  */
1366 static int
1367 CheckHeadSeparation(RF_Raid_t *raidPtr, RF_PerDiskReconCtrl_t *ctrl,
1368 		    RF_RowCol_t col, RF_HeadSepLimit_t hsCtr,
1369 		    RF_ReconUnitNum_t which_ru)
1370 {
1371 	RF_ReconCtrl_t *reconCtrlPtr = raidPtr->reconControl;
1372 	RF_CallbackDesc_t *cb, *p, *pt;
1373 	int     retval = 0;
1374 
1375 	/* if we're too far ahead of the slowest disk, stop working on this
1376 	 * disk until the slower ones catch up.  We do this by scheduling a
1377 	 * wakeup callback for the time when the slowest disk has caught up.
1378 	 * We define "caught up" with 20% hysteresis, i.e. the head separation
1379 	 * must have fallen to at most 80% of the max allowable head
1380 	 * separation before we'll wake up.
1381 	 *
1382 	 */
1383 	RF_LOCK_MUTEX(reconCtrlPtr->rb_mutex);
1384 	while(reconCtrlPtr->rb_lock) {
1385 		ltsleep(&reconCtrlPtr->rb_lock, PRIBIO, "reconctlchs", 0, &reconCtrlPtr->rb_mutex);
1386 	}
1387 	reconCtrlPtr->rb_lock = 1;
1388 	RF_UNLOCK_MUTEX(reconCtrlPtr->rb_mutex);
1389 	if ((raidPtr->headSepLimit >= 0) &&
1390 	    ((ctrl->headSepCounter - reconCtrlPtr->minHeadSepCounter) > raidPtr->headSepLimit)) {
1391 		Dprintf5("raid%d: RECON: head sep stall: col %d hsCtr %ld minHSCtr %ld limit %ld\n",
1392 			 raidPtr->raidid, col, ctrl->headSepCounter,
1393 			 reconCtrlPtr->minHeadSepCounter,
1394 			 raidPtr->headSepLimit);
1395 		cb = rf_AllocCallbackDesc();
1396 		/* the minHeadSepCounter value we have to get to before we'll
1397 		 * wake up.  build in 20% hysteresis. */
1398 		cb->callbackArg.v = (ctrl->headSepCounter - raidPtr->headSepLimit + raidPtr->headSepLimit / 5);
1399 		cb->col = col;
1400 		cb->next = NULL;
1401 
1402 		/* insert this callback descriptor into the sorted list of
1403 		 * pending head-sep callbacks */
1404 		p = reconCtrlPtr->headSepCBList;
1405 		if (!p)
1406 			reconCtrlPtr->headSepCBList = cb;
1407 		else
1408 			if (cb->callbackArg.v < p->callbackArg.v) {
1409 				cb->next = reconCtrlPtr->headSepCBList;
1410 				reconCtrlPtr->headSepCBList = cb;
1411 			} else {
1412 				for (pt = p, p = p->next; p && (p->callbackArg.v < cb->callbackArg.v); pt = p, p = p->next);
1413 				cb->next = p;
1414 				pt->next = cb;
1415 			}
1416 		retval = 1;
1417 #if RF_RECON_STATS > 0
1418 		ctrl->reconCtrl->reconDesc->hsStallCount++;
1419 #endif				/* RF_RECON_STATS > 0 */
1420 	}
1421 	RF_LOCK_MUTEX(reconCtrlPtr->rb_mutex);
1422 	reconCtrlPtr->rb_lock = 0;
1423 	wakeup(&reconCtrlPtr->rb_lock);
1424 	RF_UNLOCK_MUTEX(reconCtrlPtr->rb_mutex);
1425 
1426 	return (retval);
1427 }
1428 /*
1429  * checks to see if reconstruction has been either forced or blocked
1430  * by a user operation.  if forced, we skip this RU entirely.  else if
1431  * blocked, put ourselves on the wait list.  else return 0.
1432  *
1433  * ASSUMES THE PSS MUTEX IS LOCKED UPON ENTRY
1434  */
1435 static int
1436 CheckForcedOrBlockedReconstruction(RF_Raid_t *raidPtr,
1437 				   RF_ReconParityStripeStatus_t *pssPtr,
1438 				   RF_PerDiskReconCtrl_t *ctrl,
1439 				   RF_RowCol_t col, RF_StripeNum_t psid,
1440 				   RF_ReconUnitNum_t which_ru)
1441 {
1442 	RF_CallbackDesc_t *cb;
1443 	int     retcode = 0;
1444 
1445 	if ((pssPtr->flags & RF_PSS_FORCED_ON_READ) || (pssPtr->flags & RF_PSS_FORCED_ON_WRITE))
1446 		retcode = RF_PSS_FORCED_ON_WRITE;
1447 	else
1448 		if (pssPtr->flags & RF_PSS_RECON_BLOCKED) {
1449 			Dprintf3("RECON: col %d blocked at psid %ld ru %d\n", col, psid, which_ru);
1450 			cb = rf_AllocCallbackDesc();	/* append ourselves to
1451 							 * the blockage-wait
1452 							 * list */
1453 			cb->col = col;
1454 			cb->next = pssPtr->blockWaitList;
1455 			pssPtr->blockWaitList = cb;
1456 			retcode = RF_PSS_RECON_BLOCKED;
1457 		}
1458 	if (!retcode)
1459 		pssPtr->flags |= RF_PSS_UNDER_RECON;	/* mark this RU as under
1460 							 * reconstruction */
1461 
1462 	return (retcode);
1463 }
1464 /*
1465  * if reconstruction is currently ongoing for the indicated stripeID,
1466  * reconstruction is forced to completion and we return non-zero to
1467  * indicate that the caller must wait.  If not, then reconstruction is
1468  * blocked on the indicated stripe and the routine returns zero.  If
1469  * and only if we return non-zero, we'll cause the cbFunc to get
1470  * invoked with the cbArg when the reconstruction has completed.
1471  */
1472 int
1473 rf_ForceOrBlockRecon(RF_Raid_t *raidPtr, RF_AccessStripeMap_t *asmap,
1474 		     void (*cbFunc)(RF_Raid_t *, void *), void *cbArg)
1475 {
1476 	RF_StripeNum_t stripeID = asmap->stripeID;	/* the stripe ID we're
1477 							 * forcing recon on */
1478 	RF_SectorCount_t sectorsPerRU = raidPtr->Layout.sectorsPerStripeUnit * raidPtr->Layout.SUsPerRU;	/* num sects in one RU */
1479 	RF_ReconParityStripeStatus_t *pssPtr, *newpssPtr;	/* a pointer to the parity
1480 						 * stripe status structure */
1481 	RF_StripeNum_t psid;	/* parity stripe id */
1482 	RF_SectorNum_t offset, fd_offset;	/* disk offset, failed-disk
1483 						 * offset */
1484 	RF_RowCol_t *diskids;
1485 	RF_ReconUnitNum_t which_ru;	/* RU within parity stripe */
1486 	RF_RowCol_t fcol, diskno, i;
1487 	RF_ReconBuffer_t *new_rbuf;	/* ptr to newly allocated rbufs */
1488 	RF_DiskQueueData_t *req;/* disk I/O req to be enqueued */
1489 	RF_CallbackDesc_t *cb;
1490 	int     nPromoted;
1491 
1492 	psid = rf_MapStripeIDToParityStripeID(&raidPtr->Layout, stripeID, &which_ru);
1493 
1494 	/* allocate a new PSS in case we need it */
1495         newpssPtr = rf_AllocPSStatus(raidPtr);
1496 
1497 	RF_LOCK_PSS_MUTEX(raidPtr, psid);
1498 
1499 	pssPtr = rf_LookupRUStatus(raidPtr, raidPtr->reconControl->pssTable, psid, which_ru, RF_PSS_CREATE | RF_PSS_RECON_BLOCKED, newpssPtr);
1500 
1501         if (pssPtr != newpssPtr) {
1502                 rf_FreePSStatus(raidPtr, newpssPtr);
1503         }
1504 
1505 	/* if recon is not ongoing on this PS, just return */
1506 	if (!(pssPtr->flags & RF_PSS_UNDER_RECON)) {
1507 		RF_UNLOCK_PSS_MUTEX(raidPtr, psid);
1508 		return (0);
1509 	}
1510 	/* otherwise, we have to wait for reconstruction to complete on this
1511 	 * RU. */
1512 	/* In order to avoid waiting for a potentially large number of
1513 	 * low-priority accesses to complete, we force a normal-priority (i.e.
1514 	 * not low-priority) reconstruction on this RU. */
1515 	if (!(pssPtr->flags & RF_PSS_FORCED_ON_WRITE) && !(pssPtr->flags & RF_PSS_FORCED_ON_READ)) {
1516 		DDprintf1("Forcing recon on psid %ld\n", psid);
1517 		pssPtr->flags |= RF_PSS_FORCED_ON_WRITE;	/* mark this RU as under
1518 								 * forced recon */
1519 		pssPtr->flags &= ~RF_PSS_RECON_BLOCKED;	/* clear the blockage
1520 							 * that we just set */
1521 		fcol = raidPtr->reconControl->fcol;
1522 
1523 		/* get a listing of the disks comprising the indicated stripe */
1524 		(raidPtr->Layout.map->IdentifyStripe) (raidPtr, asmap->raidAddress, &diskids);
1525 
1526 		/* For previously issued reads, elevate them to normal
1527 		 * priority.  If the I/O has already completed, it won't be
1528 		 * found in the queue, and hence this will be a no-op. For
1529 		 * unissued reads, allocate buffers and issue new reads.  The
1530 		 * fact that we've set the FORCED bit means that the regular
1531 		 * recon procs will not re-issue these reqs */
1532 		for (i = 0; i < raidPtr->Layout.numDataCol + raidPtr->Layout.numParityCol; i++)
1533 			if ((diskno = diskids[i]) != fcol) {
1534 				if (pssPtr->issued[diskno]) {
1535 					nPromoted = rf_DiskIOPromote(&raidPtr->Queues[diskno], psid, which_ru);
1536 					if (rf_reconDebug && nPromoted)
1537 						printf("raid%d: promoted read from col %d\n", raidPtr->raidid, diskno);
1538 				} else {
1539 					new_rbuf = rf_MakeReconBuffer(raidPtr, diskno, RF_RBUF_TYPE_FORCED);	/* create new buf */
1540 					ComputePSDiskOffsets(raidPtr, psid, diskno, &offset, &fd_offset,
1541 					    &new_rbuf->spCol, &new_rbuf->spOffset);	/* find offsets & spare
1542 													 * location */
1543 					new_rbuf->parityStripeID = psid;	/* fill in the buffer */
1544 					new_rbuf->which_ru = which_ru;
1545 					new_rbuf->failedDiskSectorOffset = fd_offset;
1546 					new_rbuf->priority = RF_IO_NORMAL_PRIORITY;
1547 
1548 					/* use NULL b_proc b/c all addrs
1549 					 * should be in kernel space */
1550 					req = rf_CreateDiskQueueData(RF_IO_TYPE_READ, offset + which_ru * sectorsPerRU, sectorsPerRU, new_rbuf->buffer,
1551 					    psid, which_ru, (int (*) (void *, int)) ForceReconReadDoneProc, (void *) new_rbuf, NULL,
1552 					    NULL, (void *) raidPtr, 0, NULL);
1553 
1554 					RF_ASSERT(req);	/* XXX -- fix this --
1555 							 * XXX */
1556 
1557 					new_rbuf->arg = req;
1558 					rf_DiskIOEnqueue(&raidPtr->Queues[diskno], req, RF_IO_NORMAL_PRIORITY);	/* enqueue the I/O */
1559 					Dprintf2("raid%d: Issued new read req on col %d\n", raidPtr->raidid, diskno);
1560 				}
1561 			}
1562 		/* if the write is sitting in the disk queue, elevate its
1563 		 * priority */
1564 		if (rf_DiskIOPromote(&raidPtr->Queues[fcol], psid, which_ru))
1565 			printf("raid%d: promoted write to col %d\n",
1566 			       raidPtr->raidid, fcol);
1567 	}
1568 	/* install a callback descriptor to be invoked when recon completes on
1569 	 * this parity stripe. */
1570 	cb = rf_AllocCallbackDesc();
1571 	/* XXX the following is bogus.. These functions don't really match!!
1572 	 * GO */
1573 	cb->callbackFunc = (void (*) (RF_CBParam_t)) cbFunc;
1574 	cb->callbackArg.p = (void *) cbArg;
1575 	cb->next = pssPtr->procWaitList;
1576 	pssPtr->procWaitList = cb;
1577 	DDprintf2("raid%d: Waiting for forced recon on psid %ld\n",
1578 		  raidPtr->raidid, psid);
1579 
1580 	RF_UNLOCK_PSS_MUTEX(raidPtr, psid);
1581 	return (1);
1582 }
1583 /* called upon the completion of a forced reconstruction read.
1584  * all we do is schedule the FORCEDREADONE event.
1585  * called at interrupt context in the kernel, so don't do anything illegal here.
1586  */
1587 static void
1588 ForceReconReadDoneProc(void *arg, int status)
1589 {
1590 	RF_ReconBuffer_t *rbuf = arg;
1591 
1592 	if (status) {
1593 		printf("raid%d: Forced recon read failed!\n", rbuf->raidPtr->raidid);
1594 		rf_CauseReconEvent(rbuf->raidPtr, rbuf->col, (void *) rbuf, RF_REVENT_FORCEDREAD_FAILED);
1595 	}
1596 	rf_CauseReconEvent(rbuf->raidPtr, rbuf->col, (void *) rbuf, RF_REVENT_FORCEDREADDONE);
1597 }
1598 /* releases a block on the reconstruction of the indicated stripe */
1599 int
1600 rf_UnblockRecon(RF_Raid_t *raidPtr, RF_AccessStripeMap_t *asmap)
1601 {
1602 	RF_StripeNum_t stripeID = asmap->stripeID;
1603 	RF_ReconParityStripeStatus_t *pssPtr;
1604 	RF_ReconUnitNum_t which_ru;
1605 	RF_StripeNum_t psid;
1606 	RF_CallbackDesc_t *cb;
1607 
1608 	psid = rf_MapStripeIDToParityStripeID(&raidPtr->Layout, stripeID, &which_ru);
1609 	RF_LOCK_PSS_MUTEX(raidPtr, psid);
1610 	pssPtr = rf_LookupRUStatus(raidPtr, raidPtr->reconControl->pssTable, psid, which_ru, RF_PSS_NONE, NULL);
1611 
1612 	/* When recon is forced, the pss desc can get deleted before we get
1613 	 * back to unblock recon. But, this can _only_ happen when recon is
1614 	 * forced. It would be good to put some kind of sanity check here, but
1615 	 * how to decide if recon was just forced or not? */
1616 	if (!pssPtr) {
1617 		/* printf("Warning: no pss descriptor upon unblock on psid %ld
1618 		 * RU %d\n",psid,which_ru); */
1619 #if (RF_DEBUG_RECON > 0) || (RF_DEBUG_PSS > 0)
1620 		if (rf_reconDebug || rf_pssDebug)
1621 			printf("Warning: no pss descriptor upon unblock on psid %ld RU %d\n", (long) psid, which_ru);
1622 #endif
1623 		goto out;
1624 	}
1625 	pssPtr->blockCount--;
1626 	Dprintf3("raid%d: unblocking recon on psid %ld: blockcount is %d\n",
1627 		 raidPtr->raidid, psid, pssPtr->blockCount);
1628 	if (pssPtr->blockCount == 0) {	/* if recon blockage has been released */
1629 
1630 		/* unblock recon before calling CauseReconEvent in case
1631 		 * CauseReconEvent causes us to try to issue a new read before
1632 		 * returning here. */
1633 		pssPtr->flags &= ~RF_PSS_RECON_BLOCKED;
1634 
1635 
1636 		while (pssPtr->blockWaitList) {
1637 			/* spin through the block-wait list and
1638 			   release all the waiters */
1639 			cb = pssPtr->blockWaitList;
1640 			pssPtr->blockWaitList = cb->next;
1641 			cb->next = NULL;
1642 			rf_CauseReconEvent(raidPtr, cb->col, NULL, RF_REVENT_BLOCKCLEAR);
1643 			rf_FreeCallbackDesc(cb);
1644 		}
1645 		if (!(pssPtr->flags & RF_PSS_UNDER_RECON)) {
1646 			/* if no recon was requested while recon was blocked */
1647 			rf_PSStatusDelete(raidPtr, raidPtr->reconControl->pssTable, pssPtr);
1648 		}
1649 	}
1650 out:
1651 	RF_UNLOCK_PSS_MUTEX(raidPtr, psid);
1652 	return (0);
1653 }
1654