xref: /titanic_41/usr/src/cmd/lvm/rpc.metamhd/mhd_set.c (revision d58fda4376e4bf67072ce2e69f6f47036f9dbb68)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include "mhd_local.h"
30 
31 /*
32  * manipulate set list
33  */
34 
35 /*
36  * global set list
37  */
38 static	mutex_t		mhd_set_mx = DEFAULTMUTEX;
39 static	uint_t		mhd_nset = 0;
40 static	mhd_drive_set_t	**mhd_sets = NULL;
41 
42 /*
43  * add drive to set
44  */
45 void
46 mhd_add_drive_to_set(
47 	mhd_drive_set_t		*sp,
48 	mhd_drive_t		*dp
49 )
50 {
51 	mhd_drive_list_t	*dlp = &sp->sr_drives;
52 
53 	/* check locks */
54 	assert(MUTEX_HELD(&mhd_set_mx));
55 	assert(MUTEX_HELD(&sp->sr_mx));
56 	assert(DRIVE_IS_IDLE(dp));
57 
58 	/* add to set */
59 	mhd_add_drive(dlp, dp);
60 
61 	/* adjust backlink */
62 	dp->dr_sp = sp;
63 }
64 
65 /*
66  * delete drive from set
67  */
68 void
69 mhd_del_drive_from_set(
70 	mhd_drive_t		*dp
71 )
72 {
73 	mhd_drive_set_t		*sp = dp->dr_sp;
74 	mhd_drive_list_t	*dlp = &sp->sr_drives;
75 
76 	/* check locks */
77 	assert(MUTEX_HELD(&mhd_set_mx));
78 	assert(MUTEX_HELD(&sp->sr_mx));
79 	assert(DRIVE_IS_IDLE(dp));
80 
81 	/* delete from set */
82 	mhd_del_drive(dlp, dp);
83 
84 	/* adjust backlink */
85 	dp->dr_sp = NULL;
86 }
87 
88 /*
89  * find set in list
90  */
91 static mhd_drive_set_t *
92 mhd_find_set(
93 	char	*setname
94 )
95 {
96 	uint_t	i;
97 
98 	/* check lock */
99 	assert(MUTEX_HELD(&mhd_set_mx));
100 
101 	/* look for set */
102 	for (i = 0; (i < mhd_nset); ++i) {
103 		mhd_drive_set_t	*sp = mhd_sets[i];
104 
105 		if (strcmp(setname, sp->sr_name) == 0)
106 			return (sp);
107 	}
108 
109 	/* not found */
110 	return (NULL);
111 }
112 
113 /*
114  * wait for operation to complete
115  */
116 static void
117 mhd_wait_set(
118 	mhd_drive_set_t		*sp,
119 	mhd_drive_list_t	*dlp,
120 	mhd_state_t		state
121 )
122 {
123 	/* check lock */
124 	assert(MUTEX_HELD(&mhd_set_mx));
125 	assert(MUTEX_HELD(&sp->sr_mx));
126 
127 	/* wait for complete */
128 	for (;;) {
129 		uint_t	cnt = 0;
130 		uint_t	i;
131 
132 		/* kick threads */
133 		for (i = 0; (i < dlp->dl_ndrive); ++i) {
134 			mhd_drive_t	*dp = dlp->dl_drives[i];
135 
136 			/* IDLE or ERRORED */
137 			if (state == DRIVE_IDLE) {
138 				if (DRIVE_IS_IDLE(dp))
139 					continue;
140 			}
141 
142 			/* operation complete */
143 			else {
144 				if (! (dp->dr_state & state))
145 					continue;
146 			}
147 
148 			/* kick thread */
149 			mhd_cv_broadcast(&dp->dr_cv);
150 			++cnt;
151 		}
152 
153 		/* if complete, quit */
154 		if (cnt == 0)
155 			break;
156 
157 		/* wait for something to happen */
158 		(void) mhd_cv_wait(&sp->sr_cv, &sp->sr_mx);
159 	}
160 }
161 
162 /*
163  * idle set
164  */
165 static int
166 mhd_idle_set(
167 	mhd_drive_set_t		*sp,
168 	mhd_drive_list_t	*dlp,
169 	mhd_error_t		*mhep
170 )
171 {
172 	uint_t			i;
173 
174 	/* check lock */
175 	assert(MUTEX_HELD(&mhd_set_mx));
176 	assert(MUTEX_HELD(&sp->sr_mx));
177 
178 	/* disarm any failfast */
179 	if (dlp->dl_ndrive >= sp->sr_drives.dl_ndrive) {
180 		if (mhd_ff_disarm(sp, mhep) != 0)
181 			return (-1);
182 	}
183 
184 	/* set IDLING */
185 	for (i = 0; (i < dlp->dl_ndrive); ++i) {
186 		mhd_drive_t	*dp = dlp->dl_drives[i];
187 
188 		if (! DRIVE_IS_IDLE(dp)) {
189 			if (mhd_state(dp, DRIVE_IDLING, mhep) != 0)
190 				return (-1);
191 		}
192 	}
193 
194 	/* wait for IDLE */
195 	mhd_wait_set(sp, dlp, DRIVE_IDLE);
196 
197 	/* return success */
198 	return (0);
199 }
200 
201 /*
202  * create or update new set
203  */
204 mhd_drive_set_t *
205 mhd_create_set(
206 	mhd_set_t		*mhsp,
207 	mhd_opts_t		options,
208 	mhd_drive_list_t	*dlp,
209 	mhd_error_t		*mhep
210 )
211 {
212 	char			*setname;
213 	mhd_drive_set_t		*sp;
214 	mhd_drive_list_t	*sp_dlp;
215 	mhd_drive_set_t		*null_sp;
216 	uint_t			i;
217 
218 	/* check locks */
219 	assert(MUTEX_HELD(&mhd_set_mx));
220 
221 	/* get setname */
222 	if (mhsp == NULL)
223 		setname = "";
224 	else
225 		setname = mhsp->setname;
226 
227 	/* find or create set */
228 	if ((sp = mhd_find_set(setname)) == NULL) {
229 		/* allocate and initialize set */
230 		sp = Zalloc(sizeof (*sp));
231 		sp->sr_name = Strdup(setname);
232 		mhd_mx_init(&sp->sr_mx);
233 		mhd_cv_init(&sp->sr_cv);
234 		sp->sr_ff = -1;
235 
236 		/* append to set list */
237 		++mhd_nset;
238 		mhd_sets = Realloc(mhd_sets, (mhd_nset * sizeof (*mhd_sets)));
239 		mhd_sets[mhd_nset - 1] = sp;
240 	}
241 	sp_dlp = &sp->sr_drives;
242 
243 	/* if just grabbing null set, return */
244 	if (mhsp == NULL)
245 		return (sp);
246 	assert(strcmp(setname, "") != 0);
247 	assert(mhep != NULL);
248 
249 	/* get null set */
250 	null_sp = mhd_create_set(NULL, 0, NULL, NULL);
251 	assert(null_sp != NULL);
252 	assert(sp != null_sp);
253 
254 	/* grab set lock */
255 	mhd_mx_lock(&sp->sr_mx);
256 
257 	/* save options */
258 	if (options & MHD_SERIAL)
259 		sp->sr_options |= MHD_SERIAL;
260 	else
261 		sp->sr_options &= ~MHD_SERIAL;
262 
263 	/* move drives no longer in set to null set */
264 	if (! (options & MHD_PARTIAL_SET)) {
265 		for (i = 0; (i < sp_dlp->dl_ndrive); /* void */) {
266 			mhd_drive_t	*dp = sp_dlp->dl_drives[i];
267 			uint_t		j;
268 
269 			/* check still there */
270 			for (j = 0; (j < mhsp->drives.drives_len); ++j) {
271 				mhd_drivename_t	mhdp;
272 
273 				mhdp = mhsp->drives.drives_val[j];
274 				if (strcmp(dp->dr_rname, mhdp) == 0)
275 					break;
276 			}
277 			if (j < mhsp->drives.drives_len) {
278 				++i;
279 				continue;
280 			}
281 
282 			/* idle the drive */
283 			if (mhd_idle(dp, mhep) != 0)
284 				mhd_clrerror(mhep);
285 
286 			/* move to null set */
287 			mhd_del_drive_from_set(dp);
288 			mhd_mx_unlock(&sp->sr_mx);
289 			mhd_mx_lock(&null_sp->sr_mx);
290 			mhd_add_drive_to_set(null_sp, dp);
291 			mhd_mx_unlock(&null_sp->sr_mx);
292 			mhd_mx_lock(&sp->sr_mx);
293 		}
294 	}
295 
296 	/* add new drives to lists */
297 	for (i = 0; (i < mhsp->drives.drives_len); ++i) {
298 		mhd_drivename_t	mhdp = mhsp->drives.drives_val[i];
299 		uint_t		j;
300 		mhd_drive_t	*dp;
301 
302 		/* check already there */
303 		for (j = 0; (j < dlp->dl_ndrive); ++j) {
304 			dp = dlp->dl_drives[j];
305 			if (strcmp(mhdp, dp->dr_rname) == 0)
306 				break;
307 		}
308 		if (j < dlp->dl_ndrive) {
309 			mhd_add_drive(dlp, dp);
310 			continue;
311 		}
312 
313 		/* add drive to set */
314 		if ((dp = mhd_create_drive(sp, mhdp, NULL, mhep)) == NULL) {
315 			mhde_perror(mhep, "mhd_create_drive: %s", mhdp);
316 			continue;
317 		}
318 		mhd_add_drive(dlp, dp);
319 	}
320 
321 	/* debug */
322 #ifdef	MHD_DEBUG
323 	if (mhd_debug > 0) {
324 		for (i = 0; (i < mhd_nset); ++i) {
325 			mhd_drive_set_t		*sp = mhd_sets[i];
326 			mhd_drive_list_t	*dlp = &sp->sr_drives;
327 			char			buf[10240];
328 			uint_t			j;
329 
330 			(void) snprintf(buf, sizeof (buf), "set '%s':",
331 			    sp->sr_name);
332 			for (j = 0; (j < dlp->dl_ndrive); ++j) {
333 				mhd_drive_t	*dp = dlp->dl_drives[j];
334 				char		*p;
335 
336 				if ((p = strrchr(dp->dr_rname, '/')) != NULL)
337 					++p;
338 				else
339 					p = dp->dr_rname;
340 				(void) strncat(buf, " ", sizeof (buf));
341 				(void) strncat(buf, p, sizeof (buf));
342 			}
343 			buf[sizeof (buf) - 1] = '\0';
344 			mhd_eprintf("%s\n", buf);
345 		}
346 	}
347 #endif	/* MHD_DEBUG */
348 
349 	/* unlock, return set */
350 	mhd_mx_unlock(&sp->sr_mx);
351 	return (sp);
352 }
353 
354 /*
355  * find drive
356  */
357 mhd_drive_t *
358 mhd_find_drive(
359 	char		*rname
360 )
361 {
362 	uint_t		i;
363 
364 	/* check locks */
365 	assert(MUTEX_HELD(&mhd_set_mx));
366 
367 	/* for each set */
368 	for (i = 0; (i < mhd_nset); ++i) {
369 		mhd_drive_set_t		*sp = mhd_sets[i];
370 		mhd_drive_list_t	*dlp = &sp->sr_drives;
371 		uint_t			j;
372 
373 		/* for each drive */
374 		for (j = 0; (j < dlp->dl_ndrive); ++j) {
375 			mhd_drive_t	*dp = dlp->dl_drives[j];
376 
377 			if (strcmp(rname, dp->dr_rname) == 0)
378 				return (dp);
379 		}
380 	}
381 
382 	/* not found */
383 	return (NULL);
384 }
385 
386 /*
387  * list all the drives
388  */
389 int
390 mhd_list_drives(
391 	char		*path,
392 	mhd_did_flags_t	flags,
393 	mhd_list_res_t	*resultsp,
394 	mhd_error_t	*mhep
395 )
396 {
397 	mhd_state_t	state;
398 	uint_t		ndrive, i, j, c;
399 
400 	/* grab lock */
401 	mhd_mx_lock(&mhd_set_mx);
402 
403 	/* add path to list */
404 	if (mhd_create_drives(path, mhep) != 0) {
405 		mhd_mx_unlock(&mhd_set_mx);
406 		return (-1);
407 	}
408 
409 	/* get what we want */
410 	state = 0;
411 	if (flags & MHD_DID_SERIAL)
412 		state |= DRIVE_SERIALING;
413 	if (flags & MHD_DID_TIME)
414 		state |= DRIVE_VTOCING;
415 	if (flags & MHD_DID_CINFO)
416 		state |= DRIVE_CINFOING;
417 
418 	/* ident and count drives */
419 	for (ndrive = 0, i = 0; (i < mhd_nset); ++i) {
420 		mhd_drive_set_t		*sp = mhd_sets[i];
421 		mhd_drive_list_t	*dlp = &sp->sr_drives;
422 
423 		/* count drives */
424 		ndrive += dlp->dl_ndrive;
425 
426 		/* ident drives */
427 		if (state != 0) {
428 			mhd_mx_lock(&sp->sr_mx);
429 			for (j = 0; (j < dlp->dl_ndrive); ++j) {
430 				mhd_drive_t	*dp = dlp->dl_drives[j];
431 
432 				if (mhd_state_set(dp, state, mhep) != 0) {
433 					mhd_mx_unlock(&sp->sr_mx);
434 					mhd_mx_unlock(&mhd_set_mx);
435 					return (-1);
436 				}
437 			}
438 			mhd_wait_set(sp, dlp, state);
439 			mhd_mx_unlock(&sp->sr_mx);
440 		}
441 	}
442 
443 	/* build list */
444 	assert(resultsp->results.mhd_drive_info_list_t_len == 0);
445 	assert(resultsp->results.mhd_drive_info_list_t_val == NULL);
446 	resultsp->results.mhd_drive_info_list_t_len = ndrive;
447 	resultsp->results.mhd_drive_info_list_t_val = Zalloc(
448 	    ndrive * sizeof (*resultsp->results.mhd_drive_info_list_t_val));
449 	for (c = 0, i = 0; (i < mhd_nset); ++i) {
450 		mhd_drive_set_t		*sp = mhd_sets[i];
451 		mhd_drive_list_t	*dlp = &sp->sr_drives;
452 
453 		mhd_mx_lock(&sp->sr_mx);
454 		for (j = 0; (j < dlp->dl_ndrive); ++j) {
455 			mhd_drive_t	*dp = dlp->dl_drives[j];
456 			mhd_drive_info_t *ip =
457 			    &resultsp->results.mhd_drive_info_list_t_val[c++];
458 
459 			ip->dif_name = Strdup(dp->dr_rname);
460 			ip->dif_id = dp->dr_drive_id;
461 		}
462 		mhd_mx_unlock(&sp->sr_mx);
463 	}
464 	assert(c == ndrive);
465 
466 	/* unlock, return count */
467 	mhd_mx_unlock(&mhd_set_mx);
468 	return (ndrive);
469 }
470 
471 /*
472  * release drives
473  */
474 static int
475 mhd_release_set(
476 	mhd_drive_set_t		*sp,
477 	mhd_drive_list_t	*dlp,
478 	mhd_error_t		*mhep
479 )
480 {
481 	uint_t			i;
482 
483 	/* check locks */
484 	assert(MUTEX_HELD(&mhd_set_mx));
485 	assert(MUTEX_HELD(&sp->sr_mx));
486 
487 	/* idle set */
488 	if (mhd_idle_set(sp, dlp, mhep) != 0)
489 		return (-1);
490 
491 	/* release drives */
492 	for (i = 0; (i < dlp->dl_ndrive); i++) {
493 		mhd_drive_t	*dp = dlp->dl_drives[i];
494 
495 		if (mhd_state(dp, DRIVE_RELEASING, mhep) != 0)
496 			return (-1);
497 	}
498 	mhd_wait_set(sp, dlp, DRIVE_IDLE);
499 
500 	/* return success */
501 	return (0);
502 }
503 
504 /*
505  * release drives in set
506  */
507 int
508 mhd_release_drives(
509 	mhd_set_t		*mhsp,
510 	mhd_opts_t		options,
511 	mhd_error_t		*mhep
512 )
513 {
514 	mhd_drive_list_t	dl = mhd_null_list;
515 	mhd_drive_set_t		*sp;
516 	int			rval;
517 
518 	/* grab global lock */
519 	mhd_mx_lock(&mhd_set_mx);
520 
521 	/* create or update set */
522 	if ((sp = mhd_create_set(mhsp, options, &dl, mhep)) == NULL) {
523 		mhd_mx_unlock(&mhd_set_mx);
524 		mhd_free_list(&dl);
525 		return (-1);
526 	}
527 
528 	/* lock set */
529 	mhd_mx_lock(&sp->sr_mx);
530 
531 	/* release drives */
532 	rval = mhd_release_set(sp, &dl, mhep);
533 
534 	/* unlock, return success */
535 out:
536 	mhd_mx_unlock(&sp->sr_mx);
537 	mhd_mx_unlock(&mhd_set_mx);
538 	mhd_free_list(&dl);
539 	return (rval);
540 }
541 
542 /*
543  * reserve drives
544  */
545 static int
546 mhd_reserve_set(
547 	mhd_drive_set_t		*sp,
548 	mhd_drive_list_t	*dlp,
549 	mhd_error_t		*mhep
550 )
551 {
552 	mhd_msec_t		ff = sp->sr_timeouts.mh_ff;
553 	uint_t			retry, i, ok;
554 	int			rval = 0;
555 
556 	/* check locks */
557 	assert(MUTEX_HELD(&mhd_set_mx));
558 	assert(MUTEX_HELD(&sp->sr_mx));
559 
560 	/* idle set, idle everyone if cancelling failfast */
561 	if (ff == 0) {
562 		if (mhd_idle_set(sp, &sp->sr_drives, mhep) != 0)
563 			return (-1);
564 	} else {
565 		if (mhd_idle_set(sp, dlp, mhep) != 0)
566 			return (-1);
567 	}
568 
569 	/*
570 	 * Try to take ownership of the drives twice. This helps
571 	 * to avoid the situation where the other machine retakes
572 	 * ownership of a majority drives back, but then kills itself
573 	 * leaving no owners.
574 	 */
575 	for (retry = 0; (retry < 2); ++retry) {
576 		for (i = 0; (i < dlp->dl_ndrive); i++) {
577 			mhd_drive_t	*dp = dlp->dl_drives[i];
578 
579 			if ((retry == 0) ||
580 			    ((dp->dr_state == DRIVE_ERRORED) &&
581 			    (dp->dr_errnum == EACCES))) {
582 				if (mhd_state(dp, DRIVE_RESERVING, mhep) != 0)
583 					return (-1);
584 			}
585 		}
586 		mhd_wait_set(sp, dlp, DRIVE_IDLE);
587 	}
588 
589 	/*
590 	 * Did the take ownership succeed on a majority of the drives?
591 	 */
592 	ok = 0;
593 	for (i = 0; (i < dlp->dl_ndrive); ++i) {
594 		mhd_drive_t	*dp = dlp->dl_drives[i];
595 
596 		if (dp->dr_state == DRIVE_IDLE)
597 			++ok;
598 	}
599 
600 	/*
601 	 * Let the replica majority be the deciding factor, if able to get
602 	 * at least a single drive reserved.
603 	 */
604 	if (ok == 0) {
605 		rval = mhd_error(mhep, MHD_E_MAJORITY, sp->sr_name);
606 		goto out;
607 	}
608 
609 	/*
610 	 * Enable the failfast probes if we haven't given up yet.
611 	 */
612 	switch (sp->sr_ff_mode) {
613 
614 	/* do nothing */
615 	default:
616 		assert(0);
617 		/* FALLTHROUGH */
618 	case MHD_FF_NONE:
619 		goto out;
620 
621 	/* old style per drive failfast */
622 	case MHD_FF_DRIVER:
623 		for (i = 0; (i < dlp->dl_ndrive); i++) {
624 			mhd_drive_t	*dp = dlp->dl_drives[i];
625 
626 			if (dp->dr_state != DRIVE_ERRORED) {
627 				if (mhd_state(dp, DRIVE_FAILFASTING,
628 				    mhep) != 0) {
629 					rval = -1;
630 					goto out;
631 				}
632 			}
633 		}
634 		mhd_wait_set(sp, dlp, DRIVE_IDLE);
635 		break;
636 
637 	/* failfast probe threads */
638 	case MHD_FF_DEBUG:
639 	case MHD_FF_HALT:
640 	case MHD_FF_PANIC:
641 		if (ff != 0) {
642 			if (mhd_ff_open(sp, mhep) != 0) {
643 				rval = -1;
644 				goto out;
645 			}
646 			for (i = 0; (i < dlp->dl_ndrive); i++) {
647 				mhd_drive_t	*dp = dlp->dl_drives[i];
648 
649 				if (mhd_state_set(dp, DRIVE_PROBING,
650 				    mhep) != 0) {
651 					rval = -1;
652 					goto out;
653 				}
654 				dp->dr_time = mhd_time();
655 			}
656 			(void) mhd_ff_rearm(sp, mhep);
657 		}
658 		break;
659 	}
660 
661 	/* cleanup, return success */
662 out:
663 	if (rval != 0) {
664 		mhd_error_t	status = mhd_null_error;
665 
666 		(void) mhd_release_set(sp, dlp, &status);
667 		mhd_clrerror(&status);
668 	}
669 	return (rval);
670 }
671 
672 /*
673  * reserve drives in set
674  */
675 int
676 mhd_reserve_drives(
677 	mhd_set_t		*mhsp,
678 	mhd_mhiargs_t		*timeoutp,
679 	mhd_ff_mode_t		ff_mode,
680 	mhd_opts_t		options,
681 	mhd_error_t		*mhep
682 )
683 {
684 	mhd_drive_list_t	dl = mhd_null_list;
685 	mhd_drive_set_t		*sp;
686 	int			rval;
687 
688 	/* grab global lock */
689 	mhd_mx_lock(&mhd_set_mx);
690 
691 	/* create or update set */
692 	if ((sp = mhd_create_set(mhsp, options, &dl, mhep)) == NULL) {
693 		mhd_mx_unlock(&mhd_set_mx);
694 		mhd_free_list(&dl);
695 		return (-1);
696 	}
697 
698 	/* lock set */
699 	mhd_mx_lock(&sp->sr_mx);
700 
701 	/* can't change mode or timeouts of partial set */
702 	if ((dl.dl_ndrive != sp->sr_drives.dl_ndrive) &&
703 	    (options & MHD_PARTIAL_SET)) {
704 		if (ff_mode != sp->sr_ff_mode) {
705 			mhd_eprintf("%s: invalid ff_mode %d now %d\n",
706 			    sp->sr_name, ff_mode, sp->sr_ff_mode);
707 			ff_mode = sp->sr_ff_mode;
708 		}
709 		if (timeoutp->mh_ff < sp->sr_timeouts.mh_ff) {
710 			mhd_eprintf("%s: invalid mh_ff %d now %d\n",
711 			    sp->sr_name, timeoutp->mh_ff,
712 			    sp->sr_timeouts.mh_ff);
713 			timeoutp->mh_ff = sp->sr_timeouts.mh_ff;
714 		}
715 	}
716 
717 	/* save timouts and mode */
718 	sp->sr_timeouts = *timeoutp;
719 	sp->sr_ff_mode = ff_mode;
720 
721 	/* reserve drives */
722 	rval = mhd_reserve_set(sp, &dl, mhep);
723 
724 	/* unlock, return success */
725 out:
726 	mhd_mx_unlock(&sp->sr_mx);
727 	mhd_mx_unlock(&mhd_set_mx);
728 	mhd_free_list(&dl);
729 	return (rval);
730 }
731 
732 /*
733  * status drives
734  */
735 static int
736 mhd_status_set(
737 	mhd_drive_set_t		*sp,
738 	mhd_drive_list_t	*dlp,
739 	mhd_error_t		*mhep
740 )
741 {
742 	uint_t			i;
743 
744 	/* check locks */
745 	assert(MUTEX_HELD(&mhd_set_mx));
746 	assert(MUTEX_HELD(&sp->sr_mx));
747 
748 	/* status drives */
749 	for (i = 0; (i < dlp->dl_ndrive); i++) {
750 		mhd_drive_t	*dp = dlp->dl_drives[i];
751 
752 		if (mhd_state_set(dp, DRIVE_STATUSING, mhep) != 0)
753 			return (-1);
754 	}
755 	mhd_wait_set(sp, dlp, DRIVE_STATUSING);
756 
757 	/* return success */
758 	return (0);
759 }
760 
761 /*
762  * status drives in set
763  */
764 int
765 mhd_status_drives(
766 	mhd_set_t		*mhsp,
767 	mhd_opts_t		options,
768 	mhd_drive_status_t	**status,
769 	mhd_error_t		*mhep
770 )
771 {
772 	mhd_drive_list_t	dl = mhd_null_list;
773 	mhd_drive_list_t	*dlp = &dl;
774 	mhd_drive_set_t		*sp;
775 	uint_t			i;
776 	int			rval = 0;
777 
778 	/* grab global lock */
779 	mhd_mx_lock(&mhd_set_mx);
780 
781 	/* create or update set */
782 	if ((sp = mhd_create_set(mhsp, options, &dl, mhep)) == NULL) {
783 		mhd_mx_unlock(&mhd_set_mx);
784 		mhd_free_list(&dl);
785 		return (-1);
786 	}
787 
788 	/* lock set */
789 	mhd_mx_lock(&sp->sr_mx);
790 
791 	/* status drives */
792 	if (mhd_status_set(sp, &dl, mhep) != 0) {
793 		rval = -1;
794 		goto out;
795 	}
796 
797 	/* build list */
798 	*status = Zalloc(dlp->dl_ndrive * sizeof (**status));
799 	for (i = 0; (i < dlp->dl_ndrive); ++i) {
800 		mhd_drive_t		*dp = dlp->dl_drives[i];
801 		mhd_drive_status_t	*statusp = &(*status)[i];
802 
803 		statusp->drive = Strdup(dp->dr_rname);
804 		statusp->errnum = dp->dr_errnum;
805 	}
806 	assert(i == dlp->dl_ndrive);
807 	rval = dlp->dl_ndrive;
808 
809 	/* unlock, return count */
810 out:
811 	mhd_mx_unlock(&sp->sr_mx);
812 	mhd_mx_unlock(&mhd_set_mx);
813 	mhd_free_list(&dl);
814 	return (rval);
815 }
816