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 2009 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
mhd_add_drive(mhd_drive_list_t * dlp,mhd_drive_t * dp)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
mhd_del_drive(mhd_drive_list_t * dlp,mhd_drive_t * dp)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
mhd_free_list(mhd_drive_list_t * dlp)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
mhd_state(mhd_drive_t * dp,mhd_state_t new_state,mhd_error_t * mhep)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
mhd_state_set(mhd_drive_t * dp,mhd_state_t new_state,mhd_error_t * mhep)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
mhd_state_clr(mhd_drive_t * dp,mhd_state_t new_state,mhd_error_t * mhep)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
mhd_idle(mhd_drive_t * dp,mhd_error_t * mhep)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
mhd_reserve(mhd_drive_t * dp)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
mhd_failfast(mhd_drive_t * dp)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
mhd_release(mhd_drive_t * dp)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
mhd_probe(mhd_drive_t * dp)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
free_map()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
unlock_map()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
update_map()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 *
get_pln_ctlr_name(char * path)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
add_pln_cache(char * pln_name,enum mhd_ctlrtype_t ctype)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
find_pln_cache(char * pln_name,enum mhd_ctlrtype_t * ctype_ret)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
free_pln_cache(void)615 free_pln_cache(void)
616 {
617 struct pln_cache *p, *n = NULL;
618
619 (void) 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 (void) mutex_unlock(&mhd_pln_mx);
628 }
629
630 /*
631 * match on SSA Model 200.
632 */
633 static void
match_SSA200(mhd_drive_t * dp,char * path)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 (void) mutex_lock(&mhd_pln_mx);
652 if (find_pln_cache(pln_ctlr_name, &ctype) == 1) {
653 (void) 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 (void) 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 (void) 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 (void) mutex_unlock(&mhd_pln_mx);
712 }
713
714 /*
715 * get controller info
716 */
717 static void
match_SSA100(mhd_drive_t * dp,char * path)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
mhd_ident(mhd_drive_t * dp)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
mhd_drive_thread(mhd_drive_t * dp)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
mhd_thread_create(mhd_drive_t * dp,mhd_error_t * mhep)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 *
diskname(const char * sname)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 *
mhd_create_drive(mhd_drive_set_t * sp,char * rname,int * fdp,mhd_error_t * mhep)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 *
mhd_create_drive_anyset(char * rname,int * fdp,mhd_error_t * mhep)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
do_disk(const char * path,const struct stat * statp,int type)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
mhd_create_drives(char * path,mhd_error_t * mhep)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