xref: /titanic_41/usr/src/cmd/lvm/rpc.metamhd/mhd_drive.c (revision db2bae3047e71d795bde12e3baa621f4b6cc8930)
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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include "mhd_local.h"
27 
28 #include <ftw.h>
29 #include <libgen.h>
30 #include <sys/mhd.h>
31 #include <sys/scsi/impl/uscsi.h>
32 #include <sys/scsi/generic/commands.h>
33 #include <sys/scsi/generic/inquiry.h>
34 
35 /*
36  * manipulate drives
37  */
38 
39 /*
40  * null list constant
41  */
42 const mhd_drive_list_t	mhd_null_list = MHD_NULL_LIST;
43 
44 /*
45  * add drive to list
46  */
47 void
48 mhd_add_drive(
49 	mhd_drive_list_t	*dlp,
50 	mhd_drive_t		*dp
51 )
52 {
53 	/* add drive to list */
54 	if (dlp->dl_ndrive >= dlp->dl_alloc) {
55 		dlp->dl_alloc += 10;
56 		dlp->dl_drives = Realloc(dlp->dl_drives,
57 		    (dlp->dl_alloc * sizeof (*dlp->dl_drives)));
58 	}
59 	dlp->dl_drives[dlp->dl_ndrive++] = dp;
60 }
61 
62 /*
63  * delete drive from list
64  */
65 void
66 mhd_del_drive(
67 	mhd_drive_list_t	*dlp,
68 	mhd_drive_t		*dp
69 )
70 {
71 	uint_t			i;
72 
73 	/* delete drive from list */
74 	for (i = 0; (i < dlp->dl_ndrive); ++i) {
75 		if (dlp->dl_drives[i] == dp)
76 			break;
77 	}
78 	assert(dlp->dl_drives[i] == dp);
79 	for (/* void */; (i < dlp->dl_ndrive); ++i)
80 		dlp->dl_drives[i] = dlp->dl_drives[i + 1];
81 	dlp->dl_ndrive--;
82 }
83 
84 /*
85  * free drive list
86  */
87 void
88 mhd_free_list(
89 	mhd_drive_list_t	*dlp
90 )
91 {
92 	if (dlp->dl_drives != NULL)
93 		Free(dlp->dl_drives);
94 	(void) memset(dlp, 0, sizeof (*dlp));
95 }
96 
97 /*
98  * manipulate drive state
99  */
100 int
101 mhd_state(
102 	mhd_drive_t	*dp,
103 	mhd_state_t	new_state,
104 	mhd_error_t	*mhep
105 )
106 {
107 	mhd_drive_set_t	*sp = dp->dr_sp;
108 	mhd_state_t	old_state = dp->dr_state;
109 
110 	/* check lock */
111 	assert(MUTEX_HELD(&sp->sr_mx));
112 
113 	/* set state and kick thread */
114 	MHDPRINTF2(("%s: state 0x%x now 0x%x\n",
115 	    dp->dr_rname, dp->dr_state, new_state));
116 	dp->dr_state = new_state;
117 	mhd_cv_broadcast(&dp->dr_cv);
118 
119 	/* if this is the last PROBING drive, disable any failfast */
120 	if ((old_state & DRIVE_PROBING) && (! (new_state & DRIVE_PROBING))) {
121 		mhd_drive_list_t	*dlp = &sp->sr_drives;
122 		uint_t			cnt, i;
123 
124 		for (cnt = 0, i = 0; (i < dlp->dl_ndrive); ++i) {
125 			if (dlp->dl_drives[i]->dr_state & DRIVE_PROBING)
126 				++cnt;
127 		}
128 		if (cnt == 0) {
129 			mhd_error_t	status = mhd_null_error;
130 
131 			if (mhep == NULL)
132 				mhep = &status;
133 			if (mhd_ff_disarm(sp, mhep) != 0) {
134 				if (mhep == &status) {
135 					mhde_perror(mhep, dp->dr_rname);
136 					mhd_clrerror(mhep);
137 				}
138 				return (-1);
139 			}
140 		}
141 	}
142 
143 	/* return success */
144 	return (0);
145 }
146 
147 int
148 mhd_state_set(
149 	mhd_drive_t	*dp,
150 	mhd_state_t	new_state,
151 	mhd_error_t	*mhep
152 )
153 {
154 	return (mhd_state(dp, (dp->dr_state | new_state), mhep));
155 }
156 
157 static int
158 mhd_state_clr(
159 	mhd_drive_t	*dp,
160 	mhd_state_t	new_state,
161 	mhd_error_t	*mhep
162 )
163 {
164 	return (mhd_state(dp, (dp->dr_state & ~new_state), mhep));
165 }
166 
167 /*
168  * idle a drive
169  */
170 int
171 mhd_idle(
172 	mhd_drive_t		*dp,
173 	mhd_error_t		*mhep
174 )
175 {
176 	mhd_drive_set_t		*sp = dp->dr_sp;
177 
178 	/* check lock */
179 	assert(MUTEX_HELD(&sp->sr_mx));
180 
181 	/* wait for thread to idle */
182 	for (;;) {
183 		if (DRIVE_IS_IDLE(dp))
184 			return (0);
185 		if (mhd_state(dp, DRIVE_IDLING, mhep) != 0)
186 			return (-1);
187 		(void) mhd_cv_wait(&sp->sr_cv, &sp->sr_mx);
188 	}
189 }
190 
191 /*
192  * reserve the drive
193  */
194 static int
195 mhd_reserve(
196 	mhd_drive_t		*dp
197 )
198 {
199 	mhd_drive_set_t		*sp = dp->dr_sp;
200 	int			serial = (sp->sr_options & MHD_SERIAL);
201 	mhd_mhioctkown_t	*tkp = &sp->sr_timeouts.mh_tk;
202 	struct mhioctkown	tkown;
203 	int			err;
204 
205 	/* check locks */
206 	assert(MUTEX_HELD(&sp->sr_mx));
207 	assert(dp->dr_fd >= 0);
208 	assert(dp->dr_state == DRIVE_RESERVING);
209 
210 	/* setup timeouts */
211 	(void) memset(&tkown, 0, sizeof (tkown));
212 	tkown.reinstate_resv_delay = tkp->reinstate_resv_delay;
213 	tkown.min_ownership_delay = tkp->min_ownership_delay;
214 	tkown.max_ownership_delay = tkp->max_ownership_delay;
215 
216 	/* reserve drive */
217 	if (! serial)
218 		mhd_mx_unlock(&sp->sr_mx);
219 	err = ioctl(dp->dr_fd, MHIOCTKOWN, &tkown);
220 	if (! serial)
221 		mhd_mx_lock(&sp->sr_mx);
222 	if (err != 0) {
223 		mhd_perror("%s: MHIOCTKOWN", dp->dr_rname);
224 		(void) mhd_state(dp, DRIVE_ERRORED, NULL);
225 		dp->dr_errnum = errno;
226 		return (-1);
227 	}
228 
229 	/* return success */
230 	MHDPRINTF(("%s: MHIOCTKOWN: succeeded\n", dp->dr_rname));
231 	(void) mhd_state(dp, DRIVE_IDLE, NULL);
232 	return (0);
233 }
234 
235 /*
236  * failfast the drive
237  */
238 static int
239 mhd_failfast(
240 	mhd_drive_t	*dp
241 )
242 {
243 	mhd_drive_set_t	*sp = dp->dr_sp;
244 	int		serial = (sp->sr_options & MHD_SERIAL);
245 	int		ff = sp->sr_timeouts.mh_ff;
246 	char		*release = ((ff == 0) ? " (release)" : "");
247 	int		err;
248 
249 	/* check locks */
250 	assert(MUTEX_HELD(&sp->sr_mx));
251 	assert(dp->dr_fd >= 0);
252 	assert(dp->dr_state == DRIVE_FAILFASTING);
253 
254 	/* failfast drive */
255 	if (! serial)
256 		mhd_mx_unlock(&sp->sr_mx);
257 	err = ioctl(dp->dr_fd, MHIOCENFAILFAST, &ff);
258 	if (! serial)
259 		mhd_mx_lock(&sp->sr_mx);
260 	if (err != 0) {
261 		mhd_perror("%s: MHIOCENFAILFAST%s", dp->dr_rname, release);
262 		(void) mhd_state(dp, DRIVE_ERRORED, NULL);
263 		dp->dr_errnum = errno;
264 		return (-1);
265 	}
266 
267 	/* return success */
268 	MHDPRINTF(("%s: MHIOCENFAILFAST%s: succeeded\n",
269 	    dp->dr_rname, release));
270 	(void) mhd_state(dp, DRIVE_IDLE, NULL);
271 	return (0);
272 }
273 
274 /*
275  * release the drive
276  */
277 static int
278 mhd_release(
279 	mhd_drive_t	*dp
280 )
281 {
282 	mhd_drive_set_t	*sp = dp->dr_sp;
283 	int		serial = (sp->sr_options & MHD_SERIAL);
284 	int		ff = 0;	/* disable failfast */
285 	int		err;
286 
287 	/* check locks */
288 	assert(MUTEX_HELD(&sp->sr_mx));
289 	assert(dp->dr_fd >= 0);
290 	assert(dp->dr_state == DRIVE_RELEASING);
291 
292 	/* disable failfast */
293 	if (! serial)
294 		mhd_mx_unlock(&sp->sr_mx);
295 	err = ioctl(dp->dr_fd, MHIOCENFAILFAST, &ff);
296 	if (! serial)
297 		mhd_mx_lock(&sp->sr_mx);
298 	if (err != 0) {
299 		mhd_perror("%s: MHIOCENFAILFAST (release)", dp->dr_rname);
300 		(void) mhd_state(dp, DRIVE_ERRORED, NULL);
301 		dp->dr_errnum = errno;
302 		return (-1);
303 	}
304 	MHDPRINTF(("%s: MHIOCENFAILFAST (release): succeeded\n",
305 	    dp->dr_rname));
306 
307 	/* release drive */
308 	if (! serial)
309 		mhd_mx_unlock(&sp->sr_mx);
310 	err = ioctl(dp->dr_fd, MHIOCRELEASE, NULL);
311 	if (! serial)
312 		mhd_mx_lock(&sp->sr_mx);
313 	if (err != 0) {
314 		mhd_perror("%s: MHIOCRELEASE", dp->dr_rname);
315 		(void) mhd_state(dp, DRIVE_ERRORED, NULL);
316 		dp->dr_errnum = errno;
317 		return (-1);
318 	}
319 
320 	/* return success */
321 	MHDPRINTF(("%s: MHIOCRELEASE: succeeded\n", dp->dr_rname));
322 	(void) mhd_state(dp, DRIVE_IDLE, NULL);
323 	return (0);
324 }
325 
326 /*
327  * probe the drive
328  */
329 static int
330 mhd_probe(
331 	mhd_drive_t	*dp
332 )
333 {
334 	mhd_drive_set_t	*sp = dp->dr_sp;
335 	int		serial = (sp->sr_options & MHD_SERIAL);
336 	int		err;
337 	mhd_msec_t	now;
338 
339 	/* check locks */
340 	assert(MUTEX_HELD(&sp->sr_mx));
341 	assert(dp->dr_fd >= 0);
342 	assert(dp->dr_state & (DRIVE_PROBING | DRIVE_STATUSING));
343 
344 	/* get status (we may get dumped from PROBING here) */
345 	if (! serial)
346 		mhd_mx_unlock(&sp->sr_mx);
347 	err = ioctl(dp->dr_fd, MHIOCSTATUS, NULL);
348 	now = mhd_time();
349 	if (! serial)
350 		mhd_mx_lock(&sp->sr_mx);
351 	if (! (dp->dr_state & (DRIVE_PROBING | DRIVE_STATUSING)))
352 		return (0);
353 
354 	/* update status */
355 	if (dp->dr_state & DRIVE_STATUSING) {
356 		if (err == 1) {
357 			MHDPRINTF(("%s: MHIOCSTATUS: reserved\n",
358 			    dp->dr_rname));
359 			dp->dr_errnum = MHD_E_RESERVED;
360 		} else if (err != 0) {
361 			mhd_perror("%s: MHIOCSTATUS", dp->dr_rname);
362 			dp->dr_errnum = errno;
363 		} else {
364 			MHDPRINTF(("%s: MHIOCSTATUS: available\n",
365 			    dp->dr_rname));
366 			dp->dr_errnum = 0;
367 		}
368 		(void) mhd_state_clr(dp, DRIVE_STATUSING, NULL);
369 	}
370 
371 	/* update time or die */
372 	if (dp->dr_state & DRIVE_PROBING) {
373 		/* check our drive */
374 		if (err == 0) {
375 			dp->dr_time = now;
376 		} else if (err == 1) {
377 			mhd_eprintf("%s: %s: reservation conflict\n",
378 			    sp->sr_name, dp->dr_rname);
379 			mhd_ff_die(sp);
380 		}
381 
382 		/* check other drives */
383 		mhd_ff_check(sp);
384 	}
385 
386 	/* return success */
387 	return (0);
388 }
389 
390 /*
391  * cached controller map
392  */
393 typedef struct {
394 	char	*regexpr1;
395 	uint_t	tray;
396 	uint_t	bus;
397 	char	*regexpr2;
398 	char	*scan;
399 } mhd_ctlrmap_t;
400 
401 static	rwlock_t	ctlr_rw = DEFAULTRWLOCK;
402 static	time_t		ctlr_mtime = 0;
403 static	size_t		ctlr_num = 0;
404 static	mhd_ctlrmap_t	*ctlr_map = NULL;
405 
406 /*
407  * free up controller map
408  */
409 static void
410 free_map()
411 {
412 	size_t		i;
413 
414 	assert(RW_WRITE_HELD(&ctlr_rw));
415 
416 	for (i = 0; (i < ctlr_num); ++i) {
417 		mhd_ctlrmap_t	*cmp  = &ctlr_map[i];
418 
419 		if (cmp->regexpr1 != NULL)
420 			Free(cmp->regexpr1);
421 		if (cmp->regexpr2 != NULL)
422 			Free(cmp->regexpr2);
423 		if (cmp->scan != NULL)
424 			Free(cmp->scan);
425 	}
426 	if (ctlr_map != NULL)
427 		Free(ctlr_map);
428 	ctlr_num = 0;
429 	ctlr_map = NULL;
430 }
431 
432 /*
433  * unlock controller map
434  */
435 static void
436 unlock_map()
437 {
438 	assert(RW_WRITE_HELD(&ctlr_rw) | RW_READ_HELD(&ctlr_rw));
439 
440 	mhd_rw_unlock(&ctlr_rw);
441 }
442 
443 /*
444  * update controller map and lock it
445  */
446 static int
447 update_map()
448 {
449 	struct stat	statbuf;
450 	FILE		*fp;
451 	char		line[256], expr1[256], expr2[256], scan[256];
452 	unsigned	tray, bus;
453 	int		rval = -1;
454 
455 	/* see if map file has changed */
456 	mhd_rw_rdlock(&ctlr_rw);
457 	if (stat(METACTLRMAP, &statbuf) != 0) {
458 		mhd_perror(METACTLRMAP);
459 		goto out;
460 	}
461 	if (statbuf.st_mtime == ctlr_mtime) {
462 		rval = 0;
463 		goto out;
464 	}
465 
466 	/* trade up to writer lock, check again */
467 	mhd_rw_unlock(&ctlr_rw);
468 	mhd_rw_wrlock(&ctlr_rw);
469 	if (statbuf.st_mtime == ctlr_mtime) {
470 		rval = 0;
471 		goto out;
472 	}
473 	if (ctlr_mtime != 0)
474 		mhd_eprintf("updating controller map\n");
475 	ctlr_mtime = statbuf.st_mtime;
476 
477 	/* toss existing cache */
478 	free_map();
479 
480 	/* parse md.ctlrmap */
481 	if ((fp = fopen(METACTLRMAP, "r")) == NULL) {
482 		mhd_perror(METACTLRMAP);
483 		goto out;
484 	}
485 	clearerr(fp);
486 	while (fgets(line, sizeof (line), fp) != NULL) {
487 		char		*regexpr1 = NULL;
488 		char		*regexpr2 = NULL;
489 		mhd_ctlrmap_t	*cmp;
490 
491 		/* skip blank lines and comments */
492 		if ((line[0] == '\0') || (line[0] == '\n') || (line[0] == '#'))
493 			continue;
494 
495 		/* parse line */
496 		if (((sscanf(line, "\"%[^\"]\" %u %u \"%[^\"]\" \"%[^\"]\"",
497 		    expr1, &tray, &bus, expr2, scan)) != 5) ||
498 		    ((regexpr1 = regcmp(expr1, 0)) == NULL) ||
499 		    ((regexpr2 = regcmp(expr2, 0)) == NULL)) {
500 			mhd_eprintf("%s: bad regex(es) '%s'\n",
501 			    METACTLRMAP, line);
502 			if (regexpr1 != NULL)
503 				Free(regexpr1);
504 			if (regexpr2 != NULL)
505 				Free(regexpr2);
506 			continue;
507 		}
508 
509 		/* add to cache */
510 		ctlr_map = Realloc(ctlr_map,
511 		    ((ctlr_num + 1) * sizeof (*ctlr_map)));
512 		cmp = &ctlr_map[ctlr_num++];
513 		cmp->regexpr1 = regexpr1;
514 		cmp->tray = tray;
515 		cmp->bus = bus;
516 		cmp->regexpr2 = regexpr2;
517 		cmp->scan = Strdup(scan);
518 	}
519 	if (ferror(fp)) {
520 		mhd_perror(METACTLRMAP);
521 		(void) fclose(fp);
522 		goto out;
523 	}
524 	if (fclose(fp) != 0) {
525 		mhd_perror(METACTLRMAP);
526 		goto out;
527 	}
528 
529 	/* success */
530 	rval = 0;
531 
532 	/* return success */
533 out:
534 	if (rval != 0) {
535 		mhd_rw_unlock(&ctlr_rw);
536 		return (-1);
537 	}
538 	return (0);
539 }
540 
541 static char *
542 get_pln_ctlr_name(
543 	char	*path
544 )
545 {
546 	char	*devicesname, *p;
547 	char	retval[MAXPATHLEN];
548 
549 	devicesname = Strdup(path);
550 	if ((p = strrchr(devicesname, '/')) == NULL) {
551 		Free(devicesname);
552 		return (NULL);
553 	}
554 
555 	/* strip off the "ssd@..." portion of the devices name */
556 	*p = '\0';
557 
558 	/* strip off the "../../" in front of "devices" */
559 	if ((p = strstr(devicesname, "/devices/")) == NULL) {
560 		Free(devicesname);
561 		return (NULL);
562 	}
563 
564 	(void) snprintf(retval, sizeof (retval), "%s:ctlr", p);
565 	Free(devicesname);
566 	return (Strdup(retval));
567 }
568 
569 struct pln_cache {
570 	char			*pln_name;
571 	enum mhd_ctlrtype_t	ctype;
572 	struct pln_cache	*next;
573 };
574 
575 static struct pln_cache	*pln_cache_anchor = NULL;
576 static mutex_t		mhd_pln_mx = DEFAULTMUTEX;
577 
578 /* singled threaded by caller */
579 static void
580 add_pln_cache(
581 	char			*pln_name,
582 	enum mhd_ctlrtype_t	ctype
583 
584 )
585 {
586 	struct pln_cache	*p;
587 
588 	p = Malloc(sizeof (*p));
589 
590 	p->pln_name = pln_name;
591 	p->ctype = ctype;
592 	p->next = pln_cache_anchor;
593 	pln_cache_anchor = p;
594 }
595 
596 /* singled threaded by caller */
597 static int
598 find_pln_cache(
599 	char 			*pln_name,
600 	enum mhd_ctlrtype_t	*ctype_ret
601 )
602 {
603 	struct pln_cache	*p;
604 
605 	for (p = pln_cache_anchor; p != NULL; p = p->next) {
606 		if (strcmp(pln_name, p->pln_name) == 0) {
607 			*ctype_ret = p->ctype;
608 			return (1);
609 		}
610 	}
611 	return (0);
612 }
613 
614 static void
615 free_pln_cache(void)
616 {
617 	struct pln_cache	*p, *n = NULL;
618 
619 	mutex_lock(&mhd_pln_mx);
620 	for (p = pln_cache_anchor; p != NULL; p = n) {
621 		n = p->next;
622 		Free(p->pln_name);
623 		Free(p);
624 	}
625 
626 	pln_cache_anchor = NULL;
627 	mutex_unlock(&mhd_pln_mx);
628 }
629 
630 /*
631  * match on SSA Model 200.
632  */
633 static void
634 match_SSA200(
635 	mhd_drive_t	*dp,
636 	char		*path
637 )
638 {
639 	mhd_cinfo_t		*cinfop = &dp->dr_drive_id.did_cinfo;
640 	struct uscsi_cmd	ucmd;
641 	union scsi_cdb		cdb;
642 	struct scsi_inquiry	inq;
643 	int			fd;
644 	char			*pln_ctlr_name;
645 	enum mhd_ctlrtype_t	ctype;
646 	char			*p;
647 
648 	if ((pln_ctlr_name = get_pln_ctlr_name(path)) == NULL)
649 		return;
650 
651 	mutex_lock(&mhd_pln_mx);
652 	if (find_pln_cache(pln_ctlr_name, &ctype) == 1) {
653 		mutex_unlock(&mhd_pln_mx);
654 		if (ctype != MHD_CTLR_SSA200)
655 			return;
656 
657 		/* over-ride for SSA200 */
658 		cinfop->mhc_ctype = ctype;
659 		cinfop->mhc_tray = cinfop->mhc_bus;
660 		return;
661 	}
662 
663 	if ((fd = open(pln_ctlr_name, (O_RDONLY|O_NDELAY), 0)) < 0) {
664 		mutex_unlock(&mhd_pln_mx);
665 		Free(pln_ctlr_name);
666 		return;
667 	}
668 
669 	(void) memset(&ucmd, 0, sizeof (ucmd));
670 	(void) memset(&cdb, 0, sizeof (cdb));
671 	(void) memset(&inq, 0, sizeof (inq));
672 	cdb.scc_cmd = SCMD_INQUIRY;
673 	cdb.g0_count0 = sizeof (inq);
674 	ucmd.uscsi_cdb = (caddr_t)&cdb;
675 	ucmd.uscsi_cdblen = CDB_GROUP0;
676 	ucmd.uscsi_bufaddr = (caddr_t)&inq;
677 	ucmd.uscsi_buflen = sizeof (inq);
678 	ucmd.uscsi_flags = USCSI_READ | USCSI_ISOLATE | USCSI_DIAGNOSE;
679 	ucmd.uscsi_timeout = 30;
680 	if (ioctl(fd, USCSICMD, &ucmd)) {
681 		mutex_unlock(&mhd_pln_mx);
682 		(void) close(fd);
683 		MHDPRINTF(("%s: USCSICMD(SCMD_INQUIRY): failed errno %d\n",
684 		    pln_ctlr_name, errno));
685 		Free(pln_ctlr_name);
686 		return;
687 	}
688 
689 	(void) close(fd);
690 	MHDPRINTF(("%s: USCSICMD(SCMD_INQUIRY): success\n", pln_ctlr_name));
691 
692 	/* Make all trailing spaces be null char */
693 	for (p = inq.inq_pid + sizeof (inq.inq_pid) - 1; p != inq.inq_pid;
694 	    p--) {
695 		if (*p == '\0')
696 			continue;
697 		if (!isspace(*p))
698 			break;
699 		*p = '\0';
700 	}
701 
702 	if (strncmp(inq.inq_pid, META_SSA200_PID, sizeof (inq.inq_pid)) != 0)
703 		goto out;
704 
705 	/* over-ride the ctype, and tray */
706 	cinfop->mhc_ctype = MHD_CTLR_SSA200;
707 	cinfop->mhc_tray = cinfop->mhc_bus;
708 
709 out:
710 	add_pln_cache(pln_ctlr_name, cinfop->mhc_ctype);
711 	mutex_unlock(&mhd_pln_mx);
712 }
713 
714 /*
715  * get controller info
716  */
717 static void
718 match_SSA100(
719 	mhd_drive_t	*dp,
720 	char		*path
721 )
722 {
723 	mhd_cinfo_t	*cinfop = &dp->dr_drive_id.did_cinfo;
724 	uint_t		i;
725 	char		*p;
726 	lloff_t		wwn;
727 	const char	*fmt;
728 
729 	/* update and lock controller map */
730 	if (update_map() != 0)
731 		return;		/* give up */
732 	assert(RW_WRITE_HELD(&ctlr_rw) || RW_READ_HELD(&ctlr_rw));
733 
734 	/* look for match in cache */
735 	for (i = 0; (i < ctlr_num); ++i) {
736 		mhd_ctlrmap_t	*cmp  = &ctlr_map[i];
737 
738 		fmt = cmp->scan;
739 		if ((regex(cmp->regexpr1, path) != NULL) &&
740 		    ((p = regex(cmp->regexpr2, path)) != NULL) &&
741 		    (sscanf(p, fmt,
742 		    (ulong_t *)&wwn._p._u, (ulong_t *)&wwn._p._l) == 2)) {
743 			cinfop->mhc_ctype = MHD_CTLR_SSA100;
744 			cinfop->mhc_tray = cmp->tray;
745 			cinfop->mhc_bus = cmp->bus;
746 			cinfop->mhc_wwn = wwn._f;
747 			match_SSA200(dp, path);
748 			break;
749 		}
750 	}
751 
752 	/* unlock controller map */
753 	unlock_map();
754 }
755 
756 /*
757  * get unique drive ID
758  */
759 static int
760 mhd_ident(
761 	mhd_drive_t		*dp
762 )
763 {
764 	mhd_drive_set_t		*sp = dp->dr_sp;
765 	int			serial = (sp->sr_options & MHD_SERIAL);
766 	struct uscsi_cmd	ucmd;
767 	union scsi_cdb		cdb;
768 	struct scsi_inquiry	inq;
769 	struct extvtoc		vtoc_buf;
770 	char			path[MAXPATHLEN + 1];
771 	int			len;
772 	int			err;
773 
774 	/* check locks */
775 	assert(MUTEX_HELD(&sp->sr_mx));
776 	assert(dp->dr_fd >= 0);
777 	assert(dp->dr_state & DRIVE_IDENTING);
778 
779 	/* reset ID */
780 	(void) memset(&dp->dr_drive_id, 0, sizeof (dp->dr_drive_id));
781 
782 	/* get serial number */
783 	if (dp->dr_state & DRIVE_SERIALING) {
784 		if (! serial)
785 			mhd_mx_unlock(&sp->sr_mx);
786 		(void) memset(&ucmd, 0, sizeof (ucmd));
787 		(void) memset(&cdb, 0, sizeof (cdb));
788 		(void) memset(&inq, 0, sizeof (inq));
789 		cdb.scc_cmd = SCMD_INQUIRY;
790 		cdb.g0_count0 = sizeof (inq);
791 		ucmd.uscsi_cdb = (caddr_t)&cdb;
792 		ucmd.uscsi_cdblen = CDB_GROUP0;
793 		ucmd.uscsi_bufaddr = (caddr_t)&inq;
794 		ucmd.uscsi_buflen = sizeof (inq);
795 		ucmd.uscsi_flags = USCSI_READ | USCSI_ISOLATE | USCSI_DIAGNOSE;
796 		ucmd.uscsi_timeout = 30;
797 		err = ioctl(dp->dr_fd, USCSICMD, &ucmd);
798 		if (! serial)
799 			mhd_mx_lock(&sp->sr_mx);
800 		if (err != 0) {
801 			MHDPRINTF((
802 			    "%s: USCSICMD(SCMD_INQUIRY): failed errno %d\n",
803 			    dp->dr_rname, errno));
804 			dp->dr_drive_id.did_flags &= ~MHD_DID_SERIAL;
805 		} else {
806 			char	*p, *e;
807 			uint_t	i;
808 
809 			MHDPRINTF(("%s: USCSICMD(SCMD_INQUIRY): success\n",
810 			    dp->dr_rname));
811 			dp->dr_drive_id.did_flags |= MHD_DID_SERIAL;
812 			p = dp->dr_drive_id.did_serial;
813 			e = p + sizeof (dp->dr_drive_id.did_serial);
814 			for (i = 0;
815 			    ((i < sizeof (inq.inq_vid)) && (p < e)); ++i)
816 				*p++ = inq.inq_vid[i];
817 			for (i = 0;
818 			    ((i < sizeof (inq.inq_pid)) && (p < e)); ++i)
819 				*p++ = inq.inq_pid[i];
820 			for (i = 0;
821 			    ((i < sizeof (inq.inq_revision)) && (p < e)); ++i)
822 				*p++ = inq.inq_revision[i];
823 			for (i = 0;
824 			    ((i < sizeof (inq.inq_serial)) && (p < e)); ++i)
825 				*p++ = inq.inq_serial[i];
826 			assert(p == e);
827 			for (p = dp->dr_drive_id.did_serial; (p < e); ++p) {
828 				if (*p == '\0')
829 					*p = ' ';
830 			}
831 		}
832 	} else {
833 		dp->dr_drive_id.did_flags &= ~MHD_DID_SERIAL;
834 	}
835 
836 	/* get VTOC */
837 	if (dp->dr_state & DRIVE_VTOCING) {
838 		if (! serial)
839 			mhd_mx_unlock(&sp->sr_mx);
840 		(void) memset(&vtoc_buf, 0, sizeof (vtoc_buf));
841 		err = read_extvtoc(dp->dr_fd, &vtoc_buf);
842 		if (! serial)
843 			mhd_mx_lock(&sp->sr_mx);
844 		if (err < 0) {
845 			MHDPRINTF(("%s: read_extvtoc: failed errno %d\n",
846 			    dp->dr_rname, errno));
847 			dp->dr_drive_id.did_flags &= ~MHD_DID_TIME;
848 		} else {
849 			MHDPRINTF(("%s: read_extvtoc: success\n",
850 			    dp->dr_rname));
851 			dp->dr_drive_id.did_flags |= MHD_DID_TIME;
852 			dp->dr_drive_id.did_time = vtoc_buf.timestamp[0];
853 		}
854 	} else {
855 		dp->dr_drive_id.did_flags &= ~MHD_DID_TIME;
856 	}
857 
858 	/* get controller info */
859 	if (dp->dr_state & DRIVE_CINFOING) {
860 		if (! serial)
861 			mhd_mx_unlock(&sp->sr_mx);
862 		len = readlink(dp->dr_rname0, path, (sizeof (path) - 1));
863 		if (! serial)
864 			mhd_mx_lock(&sp->sr_mx);
865 		if (len >= sizeof (path)) {
866 			len = -1;
867 			errno = ENAMETOOLONG;
868 		}
869 		if (len < 0) {
870 			MHDPRINTF(("%s: readlink: failed errno %d\n",
871 			    dp->dr_rname0, errno));
872 			dp->dr_drive_id.did_flags &= ~MHD_DID_CINFO;
873 		} else {
874 			MHDPRINTF(("%s: readlink: success\n",
875 			    dp->dr_rname0));
876 			dp->dr_drive_id.did_flags |= MHD_DID_CINFO;
877 			(void) memset(&dp->dr_drive_id.did_cinfo, 0,
878 			    sizeof (dp->dr_drive_id.did_cinfo));
879 			match_SSA100(dp, path);
880 		}
881 	} else {
882 		dp->dr_drive_id.did_flags &= ~MHD_DID_CINFO;
883 	}
884 
885 	/* return success */
886 	(void) mhd_state_clr(dp, DRIVE_IDENTING, NULL);
887 	return (0);
888 }
889 
890 /*
891  * disk thread
892  */
893 static void
894 mhd_drive_thread(
895 	mhd_drive_t	*dp
896 )
897 {
898 	mhd_drive_set_t	*sp = dp->dr_sp;
899 
900 	/* wait for dp->dr_thread to be filled in */
901 	assert(sp != NULL);
902 	mhd_mx_lock(&sp->sr_mx);
903 
904 	/* forever */
905 	for (;;) {
906 		/* check locks */
907 		assert(MUTEX_HELD(&sp->sr_mx));
908 		assert(dp->dr_thread == thr_self());
909 
910 		/* check for changed set */
911 		if (sp != dp->dr_sp) {
912 			MHDPRINTF2(("%s: changed from set '%s' to '%s'\n",
913 			    dp->dr_rname, sp->sr_name, dp->dr_sp->sr_name));
914 
915 			mhd_mx_unlock(&sp->sr_mx);
916 			sp = dp->dr_sp;
917 			mhd_mx_lock(&sp->sr_mx);
918 		}
919 
920 		/* open drive, if necessary */
921 		if ((dp->dr_fd < 0) && (! (DRIVE_IS_IDLE(dp) ||
922 		    (dp->dr_state == DRIVE_IDLING)))) {
923 			int	serial = (sp->sr_options & MHD_SERIAL);
924 
925 			if (! serial)
926 				mhd_mx_unlock(&sp->sr_mx);
927 			dp->dr_fd = open(dp->dr_rname0, (O_RDWR|O_NDELAY), 0);
928 			if (! serial)
929 				mhd_mx_lock(&sp->sr_mx);
930 			if (dp->dr_fd < 0) {
931 				mhd_perror("%s: open", dp->dr_rname);
932 				(void) mhd_state(dp, DRIVE_ERRORED, NULL);
933 				dp->dr_errnum = errno;
934 			}
935 			continue;
936 		}
937 
938 		/* dispatch */
939 		switch (dp->dr_state) {
940 		case DRIVE_IDLE:
941 			MHDPRINTF1(("%s: IDLE\n", dp->dr_rname));
942 			break;
943 
944 		case DRIVE_ERRORED:
945 			MHDPRINTF1(("%s: ERRORED %d\n",
946 			    dp->dr_rname, dp->dr_errnum));
947 			break;
948 
949 		case DRIVE_IDLING:
950 			(void) mhd_state(dp, DRIVE_IDLE, NULL);
951 			continue;
952 
953 		case DRIVE_RESERVING:
954 			MHDPRINTF1(("%s: RESERVING\n", dp->dr_rname));
955 			(void) mhd_reserve(dp);
956 			assert(DRIVE_IS_IDLE(dp));
957 			continue;
958 
959 		case DRIVE_FAILFASTING:
960 			MHDPRINTF1(("%s: FAILFASTING\n", dp->dr_rname));
961 			(void) mhd_failfast(dp);
962 			assert(DRIVE_IS_IDLE(dp));
963 			continue;
964 
965 		case DRIVE_RELEASING:
966 			MHDPRINTF1(("%s: RELEASING\n", dp->dr_rname));
967 			(void) mhd_release(dp);
968 			assert(DRIVE_IS_IDLE(dp));
969 			continue;
970 
971 		/* non-exclusive states */
972 		default:
973 			assert(! (dp->dr_state &
974 			    (DRIVE_EXCLUSIVE_STATES & ~DRIVE_ERRORED)));
975 			if (dp->dr_state & (DRIVE_PROBING | DRIVE_STATUSING)) {
976 				MHDPRINTF1(("%s: PROBING\n", dp->dr_rname));
977 				(void) mhd_probe(dp);
978 				assert(! (dp->dr_state & DRIVE_STATUSING));
979 			}
980 			if (dp->dr_state & DRIVE_IDENTING) {
981 				MHDPRINTF1(("%s: IDENTING\n", dp->dr_rname));
982 				(void) mhd_ident(dp);
983 				assert(! (dp->dr_state & DRIVE_IDENTING));
984 				continue;	/* in case we're probing */
985 			}
986 			break;
987 		}
988 
989 		/* close drive, if possible */
990 		if ((dp->dr_fd >= 0) && (DRIVE_IS_IDLE(dp))) {
991 			int	serial = (sp->sr_options & MHD_SERIAL);
992 
993 			if (! serial)
994 				mhd_mx_unlock(&sp->sr_mx);
995 			(void) close(dp->dr_fd);	/* sd/ssd bug */
996 			if (! serial)
997 				mhd_mx_lock(&sp->sr_mx);
998 			dp->dr_fd = -1;
999 		}
1000 
1001 		/* wake up anybody waiting */
1002 		mhd_cv_broadcast(&sp->sr_cv);
1003 
1004 		/* see if anything happened */
1005 		if (! DRIVE_IS_IDLE(dp))
1006 			continue;
1007 
1008 		/* wait for something to happen */
1009 		if (! (dp->dr_state & DRIVE_PROBING)) {
1010 			mhd_cv_wait(&dp->dr_cv, &sp->sr_mx);
1011 		} else {
1012 			mhd_cv_timedwait(&dp->dr_cv, &sp->sr_mx,
1013 			    (sp->sr_timeouts.mh_ff / 2));
1014 		}
1015 	}
1016 }
1017 
1018 /*
1019  * kick off drive thread
1020  */
1021 static int
1022 mhd_thread_create(
1023 	mhd_drive_t	*dp,
1024 	mhd_error_t	*mhep
1025 )
1026 {
1027 	mhd_drive_set_t	*sp = dp->dr_sp;
1028 	thread_t	thread = NULL;
1029 	int		rval = 0;
1030 
1031 	/* check lock and thread */
1032 	assert(MUTEX_HELD(&sp->sr_mx));
1033 	assert(dp->dr_thread == NULL);
1034 
1035 	/* create thread */
1036 	if (thr_create(NULL, 0, (void *(*)(void *))mhd_drive_thread,
1037 	    (void *)dp, (THR_DETACHED | THR_BOUND), &thread) != 0) {
1038 		rval = mhd_error(mhep, errno, "thr_create");
1039 	} else {
1040 		assert(thread != NULL);
1041 		dp->dr_thread = thread;
1042 	}
1043 
1044 	/* return success */
1045 	return (rval);
1046 }
1047 
1048 /*
1049  * peel off s%u from name
1050  */
1051 static char *
1052 diskname(
1053 	const char	*sname
1054 )
1055 {
1056 	char		*dname;
1057 	char		*p, *e;
1058 
1059 	/* duplicate name */
1060 	if ((dname = Strdup(sname)) == NULL)
1061 		return (NULL);
1062 
1063 	/* gobble number and 's' */
1064 	p = e = dname + strlen(dname) - 1;
1065 	for (; (p > dname); --p) {
1066 		if (!isdigit(*p))
1067 			break;
1068 	}
1069 	if ((p == e) || (p <= dname)) {
1070 		Free(dname);
1071 		return (NULL);
1072 	}
1073 	if (*p-- != 's') {
1074 		Free(dname);
1075 		return (NULL);
1076 	}
1077 	if ((p <= dname) || (!isdigit(*p))) {
1078 		Free(dname);
1079 		return (NULL);
1080 	}
1081 	*(++p) = '\0';
1082 	return (dname);
1083 }
1084 
1085 /*
1086  * create new drive
1087  */
1088 mhd_drive_t *
1089 mhd_create_drive(
1090 	mhd_drive_set_t	*sp,		/* new set */
1091 	char		*rname,		/* raw drive name */
1092 	int		*fdp,		/* open device or -1 */
1093 	mhd_error_t	*mhep		/* returned error */
1094 )
1095 {
1096 	mhd_drive_t	*dp = NULL;
1097 	char		*rname0 = NULL;
1098 
1099 	/* check locks */
1100 	assert(MUTEX_HELD(&sp->sr_mx));
1101 
1102 	/* if drive already exists */
1103 	if ((dp = mhd_find_drive(rname)) != NULL) {
1104 		mhd_drive_set_t	*oldsp = dp->dr_sp;
1105 
1106 		/* if set has changed, move drive */
1107 		if (oldsp != sp) {
1108 			mhd_mx_unlock(&sp->sr_mx);
1109 			mhd_mx_lock(&oldsp->sr_mx);
1110 			if (mhd_idle(dp, mhep) != 0) {
1111 				mhd_mx_unlock(&oldsp->sr_mx);
1112 				mhd_mx_lock(&sp->sr_mx);
1113 				return (NULL);
1114 			}
1115 			mhd_del_drive_from_set(dp);
1116 			mhd_mx_unlock(&oldsp->sr_mx);
1117 			mhd_mx_lock(&sp->sr_mx);
1118 			mhd_add_drive_to_set(sp, dp);
1119 		}
1120 
1121 		/* return drive */
1122 		return (dp);
1123 	}
1124 
1125 	/* build slice0 */
1126 	rname0 = Malloc(strlen(rname) + strlen("s0") + 1);
1127 	(void) strcpy(rname0, rname);
1128 	(void) strcat(rname0, "s0");
1129 
1130 	/* allocate and initialize drive */
1131 	dp = Zalloc(sizeof (*dp));
1132 	dp->dr_sp = sp;
1133 	dp->dr_rname = Strdup(rname);
1134 	dp->dr_rname0 = rname0;
1135 	mhd_cv_init(&dp->dr_cv);
1136 	dp->dr_thread = NULL;
1137 	dp->dr_fd = -1;
1138 	dp->dr_state = DRIVE_IDLE;
1139 
1140 	/* steal open drive */
1141 	if ((fdp  != NULL) && (*fdp >= 0)) {
1142 		dp->dr_fd = *fdp;
1143 		*fdp = -1;
1144 	}
1145 
1146 	/* add to set */
1147 	mhd_add_drive_to_set(sp, dp);
1148 
1149 	/* kick off drive thread */
1150 	if (mhd_thread_create(dp, mhep) != 0) {
1151 		Free(dp->dr_rname0);
1152 		Free(dp->dr_rname);
1153 		Free(dp);
1154 		return (NULL);
1155 	}
1156 
1157 	/* return drive */
1158 	return (dp);
1159 }
1160 
1161 /*
1162  * find or create drive in any set
1163  */
1164 static mhd_drive_t *
1165 mhd_create_drive_anyset(
1166 	char		*rname,
1167 	int		*fdp,
1168 	mhd_error_t	*mhep
1169 )
1170 {
1171 	mhd_drive_set_t	*null_sp = mhd_create_set(NULL, 0, NULL, NULL);
1172 	mhd_drive_t	*dp;
1173 
1174 	/* check locks */
1175 	assert(null_sp != NULL);
1176 
1177 	/* drive already exists */
1178 	if ((dp = mhd_find_drive(rname)) != NULL)
1179 		return (dp);
1180 
1181 	/* add to null set */
1182 	mhd_mx_lock(&null_sp->sr_mx);
1183 	dp = mhd_create_drive(null_sp, rname, fdp, mhep);
1184 	mhd_mx_unlock(&null_sp->sr_mx);
1185 
1186 	/* return drive */
1187 	return (dp);
1188 }
1189 
1190 /*
1191  * process a file in the tree walk
1192  */
1193 static int
1194 do_disk(
1195 	const char		*path,
1196 	const struct stat	*statp,
1197 	int			type
1198 )
1199 {
1200 	char			*dname = NULL;
1201 	int			fd = -1;
1202 	struct dk_cinfo		cinfo;
1203 	mhd_error_t		status = mhd_null_error;
1204 
1205 	/* skip all but character devices */
1206 	if ((type != FTW_F) || (! S_ISCHR(statp->st_mode)) ||
1207 	    ((dname = diskname(path)) == NULL)) {
1208 		return (0);
1209 	}
1210 
1211 	/* see if drive already exists */
1212 	if (mhd_find_drive(dname) != NULL)
1213 		return (0);
1214 
1215 	/* see if device is a disk */
1216 	if ((fd = open(path, (O_RDONLY|O_NDELAY), 0)) < 0)
1217 		goto out;
1218 	if (ioctl(fd, DKIOCINFO, &cinfo) != 0) {
1219 		switch (errno) {
1220 		case EINVAL:
1221 		case ENOTTY:
1222 			break;
1223 		default:
1224 			mhd_perror("DKIOCINFO: %s", path);
1225 			break;
1226 		}
1227 		goto out;
1228 	}
1229 
1230 	/* skip CDROMs */
1231 	if (cinfo.dki_ctype == DKC_CDROM) {
1232 		(void) close(fd);
1233 		Free(dname);
1234 		return (0);
1235 	}
1236 
1237 	/* put disk on list */
1238 	if (mhd_create_drive_anyset(dname, &fd, &status) == NULL) {
1239 		mhde_perror(&status, "");
1240 		goto out;
1241 	}
1242 
1243 	/* cleanup, return success (no matter what) */
1244 out:
1245 	if (dname != NULL)
1246 		Free(dname);
1247 	if (fd >= 0)
1248 		(void) close(fd);
1249 	mhd_clrerror(&status);
1250 	return (0);
1251 }
1252 
1253 /*
1254  * find or create all the drives under a given directory
1255  */
1256 int
1257 mhd_create_drives(
1258 	char		*path,
1259 	mhd_error_t	*mhep
1260 )
1261 {
1262 	/* default */
1263 	if ((path == NULL) || (*path == '\0'))
1264 		path = "/dev/rdsk";
1265 
1266 	free_pln_cache();
1267 
1268 	/* walk the directory, adding disks */
1269 	if (ftw(path, do_disk, 5) != 0)
1270 		return (mhd_error(mhep, errno, path));
1271 
1272 	/* return success */
1273 	return (0);
1274 }
1275