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 2016 Nexenta Systems, Inc. All rights reserved.
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #include <devfsadm.h>
28 #include <stdio.h>
29 #include <strings.h>
30 #include <stdlib.h>
31 #include <limits.h>
32 #include <ctype.h>
33 #include <sys/int_fmtio.h>
34 #include <sys/stat.h>
35 #include <bsm/devalloc.h>
36 #include <sys/scsi/scsi_address.h>
37 #include <sys/libdevid.h>
38
39 #define DISK_SUBPATH_MAX 100
40 #define RM_STALE 0x01
41 #define DISK_LINK_RE "^r?dsk/c[0-9]+(t[0-9A-F]+)?d[0-9]+(((s|p))[0-9]+)?$"
42 #define DISK_LINK_TO_UPPER(ch)\
43 (((ch) >= 'a' && (ch) <= 'z') ? (ch - 'a' + 'A') : ch)
44
45 #define SLICE_SMI "s7"
46 #define SLICE_EFI ""
47
48 #define MN_SMI "h"
49 #define MN_EFI "wd"
50 #define ASCIIWWNSIZE 255
51 #if defined(__i386) || defined(__amd64)
52 /*
53 * The number of minor nodes per LUN is defined by the disk drivers.
54 * Currently it is set to 64. Refer CMLBUNIT_SHIFT (cmlb_impl.h)
55 */
56 #define NUM_MINORS_PER_INSTANCE 64
57 #endif
58
59
60 extern int system_labeled;
61
62 static int disk_callback_chan(di_minor_t minor, di_node_t node);
63 static int disk_callback_nchan(di_minor_t minor, di_node_t node);
64 static int disk_callback_blkdev(di_minor_t minor, di_node_t node);
65 static int disk_callback_wwn(di_minor_t minor, di_node_t node);
66 static int disk_callback_xvmd(di_minor_t minor, di_node_t node);
67 static int disk_callback_fabric(di_minor_t minor, di_node_t node);
68 static int disk_callback_sas(di_minor_t minor, di_node_t node);
69 static void disk_common(di_minor_t minor, di_node_t node, char *disk,
70 int flags);
71 static char *diskctrl(di_node_t node, di_minor_t minor);
72 static int reserved_links_exist(di_node_t node, di_minor_t minor, int nflags);
73
74
75 static devfsadm_create_t disk_cbt[] = {
76 { "disk", DDI_NT_BLOCK, NULL,
77 TYPE_EXACT, ILEVEL_0, disk_callback_nchan
78 },
79 { "disk", DDI_NT_BLOCK_CHAN, NULL,
80 TYPE_EXACT, ILEVEL_0, disk_callback_chan
81 },
82 { "disk", DDI_NT_BLOCK_BLKDEV, NULL,
83 TYPE_EXACT, ILEVEL_0, disk_callback_blkdev
84 },
85 { "disk", DDI_NT_BLOCK_FABRIC, NULL,
86 TYPE_EXACT, ILEVEL_0, disk_callback_fabric
87 },
88 { "disk", DDI_NT_BLOCK_WWN, NULL,
89 TYPE_EXACT, ILEVEL_0, disk_callback_wwn
90 },
91 { "disk", DDI_NT_BLOCK_SAS, NULL,
92 TYPE_EXACT, ILEVEL_0, disk_callback_sas
93 },
94 { "disk", DDI_NT_CD, NULL,
95 TYPE_EXACT, ILEVEL_0, disk_callback_nchan
96 },
97 { "disk", DDI_NT_CD_CHAN, NULL,
98 TYPE_EXACT, ILEVEL_0, disk_callback_chan
99 },
100 { "disk", DDI_NT_BLOCK_XVMD, NULL,
101 TYPE_EXACT, ILEVEL_0, disk_callback_xvmd
102 },
103 { "disk", DDI_NT_CD_XVMD, NULL,
104 TYPE_EXACT, ILEVEL_0, disk_callback_xvmd
105 },
106 };
107
108 DEVFSADM_CREATE_INIT_V0(disk_cbt);
109
110 /*
111 * HOT auto cleanup of disks not desired.
112 */
113 static devfsadm_remove_t disk_remove_cbt[] = {
114 { "disk", DISK_LINK_RE, RM_POST,
115 ILEVEL_0, devfsadm_rm_all
116 }
117 };
118
119 DEVFSADM_REMOVE_INIT_V0(disk_remove_cbt);
120
121 static devlink_re_t disks_re_array[] = {
122 {"^r?dsk/c([0-9]+)", 1},
123 {"^cfg/c([0-9]+)$", 1},
124 {"^scsi/.+/c([0-9]+)", 1},
125 {NULL}
126 };
127
128 static char *disk_mid = "disk_mid";
129 static char *modname = "disk_link";
130
131 int
minor_init()132 minor_init()
133 {
134 devfsadm_print(disk_mid,
135 "%s: minor_init(): Creating disks reserved ID cache\n",
136 modname);
137 return (devfsadm_reserve_id_cache(disks_re_array, NULL));
138 }
139
140 static int
disk_callback_chan(di_minor_t minor,di_node_t node)141 disk_callback_chan(di_minor_t minor, di_node_t node)
142 {
143 char *addr;
144 char disk[20];
145 uint_t targ;
146 uint_t lun;
147
148 addr = di_bus_addr(node);
149 (void) sscanf(addr, "%X,%X", &targ, &lun);
150 (void) sprintf(disk, "t%dd%d", targ, lun);
151 disk_common(minor, node, disk, 0);
152 return (DEVFSADM_CONTINUE);
153
154 }
155
156 static int
disk_callback_nchan(di_minor_t minor,di_node_t node)157 disk_callback_nchan(di_minor_t minor, di_node_t node)
158 {
159 char *addr;
160 char disk[10];
161 uint_t lun;
162
163 addr = di_bus_addr(node);
164 (void) sscanf(addr, "%X", &lun);
165 (void) sprintf(disk, "d%d", lun);
166 disk_common(minor, node, disk, 0);
167 return (DEVFSADM_CONTINUE);
168
169 }
170
171 static int
disk_callback_blkdev(di_minor_t minor,di_node_t node)172 disk_callback_blkdev(di_minor_t minor, di_node_t node)
173 {
174 char *addr;
175 char disk[DISK_SUBPATH_MAX];
176 uint64_t eui64;
177 uint_t lun = 0;
178
179 addr = di_bus_addr(node);
180 (void) sscanf(addr, "w%016"PRIx64",%X", &eui64, &lun);
181 (void) snprintf(disk, DISK_SUBPATH_MAX, "t%016"PRIX64"d%d", eui64, lun);
182 disk_common(minor, node, disk, RM_STALE);
183 return (DEVFSADM_CONTINUE);
184 }
185
186 static int
disk_callback_wwn(di_minor_t minor,di_node_t node)187 disk_callback_wwn(di_minor_t minor, di_node_t node)
188 {
189 char disk[10];
190 int lun;
191 int targ;
192 int *intp;
193
194 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, SCSI_ADDR_PROP_TARGET,
195 &intp) <= 0) {
196 return (DEVFSADM_CONTINUE);
197 }
198 targ = *intp;
199 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, SCSI_ADDR_PROP_LUN,
200 &intp) <= 0) {
201 lun = 0;
202 } else {
203 lun = *intp;
204 }
205 (void) sprintf(disk, "t%dd%d", targ, lun);
206
207 disk_common(minor, node, disk, RM_STALE);
208
209 return (DEVFSADM_CONTINUE);
210 }
211
212 static int
disk_callback_fabric(di_minor_t minor,di_node_t node)213 disk_callback_fabric(di_minor_t minor, di_node_t node)
214 {
215 char disk[DISK_SUBPATH_MAX];
216 int lun;
217 int count;
218 int *intp;
219 uchar_t *str;
220 uchar_t *wwn;
221 uchar_t ascii_wwn[ASCIIWWNSIZE];
222
223 if (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
224 "client-guid", (char **)&wwn) > 0) {
225 if (strlcpy((char *)ascii_wwn, (char *)wwn,
226 sizeof (ascii_wwn)) >= sizeof (ascii_wwn)) {
227 devfsadm_errprint("SUNW_disk_link: GUID too long:%d",
228 strlen((char *)wwn));
229 return (DEVFSADM_CONTINUE);
230 }
231 lun = 0;
232 } else if (di_prop_lookup_bytes(DDI_DEV_T_ANY, node,
233 "port-wwn", &wwn) > 0) {
234 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node,
235 SCSI_ADDR_PROP_LUN, &intp) > 0) {
236 lun = *intp;
237 } else {
238 lun = 0;
239 }
240
241 for (count = 0, str = ascii_wwn; count < 8; count++, str += 2) {
242 (void) sprintf((caddr_t)str, "%02x", wwn[count]);
243 }
244 *str = '\0';
245 } else {
246 return (DEVFSADM_CONTINUE);
247 }
248
249 for (str = ascii_wwn; *str != '\0'; str++) {
250 *str = DISK_LINK_TO_UPPER(*str);
251 }
252
253 (void) snprintf(disk, DISK_SUBPATH_MAX, "t%sd%d", ascii_wwn, lun);
254
255 disk_common(minor, node, disk, RM_STALE);
256
257 return (DEVFSADM_CONTINUE);
258 }
259
260 static int
disk_callback_sas(di_minor_t minor,di_node_t node)261 disk_callback_sas(di_minor_t minor, di_node_t node)
262 {
263 char disk[DISK_SUBPATH_MAX];
264 int lun64_found = 0;
265 scsi_lun64_t lun64, sl;
266 scsi_lun_t lun;
267 int64_t *lun64p;
268 uint64_t wwn;
269 int *intp;
270 char *tgt_port;
271 uchar_t addr_method;
272
273 /* Get lun property */
274 if (di_prop_lookup_int64(DDI_DEV_T_ANY, node,
275 SCSI_ADDR_PROP_LUN64, &lun64p) > 0) {
276 if (*lun64p != SCSI_LUN64_ILLEGAL) {
277 lun64_found = 1;
278 lun64 = (uint64_t)*lun64p;
279 }
280 }
281 if ((!lun64_found) && (di_prop_lookup_ints(DDI_DEV_T_ANY, node,
282 SCSI_ADDR_PROP_LUN, &intp) > 0)) {
283 lun64 = (uint64_t)*intp;
284 }
285
286 lun = scsi_lun64_to_lun(lun64);
287
288 addr_method = (lun.sl_lun1_msb & SCSI_LUN_AM_MASK);
289
290 if (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
291 SCSI_ADDR_PROP_TARGET_PORT, &tgt_port) > 0) {
292 (void) scsi_wwnstr_to_wwn(tgt_port, &wwn);
293 if ((addr_method == SCSI_LUN_AM_PDEV) &&
294 (lun.sl_lun2_msb == 0) && (lun.sl_lun2_lsb == 0) &&
295 (lun.sl_lun3_msb == 0) && (lun.sl_lun3_lsb == 0) &&
296 (lun.sl_lun4_msb == 0) && (lun.sl_lun4_lsb == 0)) {
297 (void) snprintf(disk, DISK_SUBPATH_MAX,
298 "t%"PRIX64"d%"PRId64, wwn, lun64);
299 } else if ((addr_method == SCSI_LUN_AM_FLAT) &&
300 (lun.sl_lun2_msb == 0) && (lun.sl_lun2_lsb == 0) &&
301 (lun.sl_lun3_msb == 0) && (lun.sl_lun3_lsb == 0) &&
302 (lun.sl_lun4_msb == 0) && (lun.sl_lun4_lsb == 0)) {
303 sl = (lun.sl_lun1_msb << 8) | lun.sl_lun1_lsb;
304 (void) snprintf(disk, DISK_SUBPATH_MAX,
305 "t%"PRIX64"d%"PRIX16, wwn, sl);
306 } else {
307 (void) snprintf(disk, DISK_SUBPATH_MAX,
308 "t%"PRIX64"d%"PRIX64, wwn, lun64);
309 }
310 } else if (di_prop_lookup_ints(DDI_DEV_T_ANY, node,
311 SCSI_ADDR_PROP_SATA_PHY, &intp) > 0) {
312 /* Use phy format naming, for SATA devices without wwn */
313 if ((addr_method == SCSI_LUN_AM_PDEV) &&
314 (lun.sl_lun2_msb == 0) && (lun.sl_lun2_lsb == 0) &&
315 (lun.sl_lun3_msb == 0) && (lun.sl_lun3_lsb == 0) &&
316 (lun.sl_lun4_msb == 0) && (lun.sl_lun4_lsb == 0)) {
317 (void) snprintf(disk, DISK_SUBPATH_MAX,
318 "t%dd%"PRId64, *intp, lun64);
319 } else if ((addr_method == SCSI_LUN_AM_FLAT) &&
320 (lun.sl_lun2_msb == 0) && (lun.sl_lun2_lsb == 0) &&
321 (lun.sl_lun3_msb == 0) && (lun.sl_lun3_lsb == 0) &&
322 (lun.sl_lun4_msb == 0) && (lun.sl_lun4_lsb == 0)) {
323 sl = (lun.sl_lun1_msb << 8) | lun.sl_lun1_lsb;
324 (void) snprintf(disk, DISK_SUBPATH_MAX,
325 "t%dd%"PRIX16, *intp, sl);
326 } else {
327 (void) snprintf(disk, DISK_SUBPATH_MAX,
328 "t%dd%"PRIX64, *intp, lun64);
329 }
330 } else {
331 return (DEVFSADM_CONTINUE);
332 }
333
334 disk_common(minor, node, disk, RM_STALE);
335
336 return (DEVFSADM_CONTINUE);
337 }
338
339 /*
340 * xVM virtual block device
341 *
342 * Xen passes device number in next format:
343 *
344 * 1 << 28 | disk << 8 | partition xvd, disks or partitions 16 onwards
345 * 202 << 8 | disk << 4 | partition xvd, disks and partitions up to 15
346 * 8 << 8 | disk << 4 | partition sd, disks and partitions up to 15
347 * 3 << 8 | disk << 6 | partition hd, disks 0..1, partitions 0..63
348 * 22 << 8 | (disk-2) << 6 | partition hd, disks 2..3, partitions 0..63
349 * 2 << 28 onwards reserved for future use
350 * other values less than 1 << 28 deprecated / reserved
351 *
352 * The corresponding Solaris /dev/dsk name can be:
353 *
354 * c0tYdXsN
355 *
356 * where Y,X >= 0.
357 *
358 * For PV guests using the legacy naming (0, 1, 2, ...)
359 * the Solaris disk names created will be c0d[0..767]sN
360 */
361
362 #define HD_BASE (3 << 8)
363 #define XEN_EXT_SHIFT (28)
364
365 /*
366 * Return: Number of parsed and written parameters
367 */
368 static int
decode_xen_device(uint_t device,uint_t * disk,uint_t * plun)369 decode_xen_device(uint_t device, uint_t *disk, uint_t *plun)
370 {
371 uint_t dsk, lun = 0;
372 int ret = 1;
373
374 if ((device >> XEN_EXT_SHIFT) > 1)
375 return (0);
376
377 if (device < HD_BASE) {
378 /* legacy device address */
379 dsk = device;
380 goto end;
381 }
382
383 ret = 2;
384 if (device & (1 << XEN_EXT_SHIFT)) {
385 /* extended */
386 dsk = device & (~0xff);
387 lun = device & 0xff;
388 goto end;
389 }
390
391 switch (device >> 8) {
392 case 202: /* xvd */
393 dsk = (device >> 4) & 0xf;
394 lun = device & 0xf;
395 break;
396 case 8: /* sd */
397 dsk = device & (~0xf);
398 lun = device & 0xf;
399 break;
400 case 3: /* hd, disk 0..1 */
401 dsk = device & (~0x3f);
402 lun = device & 0x3f;
403 break;
404 case 22: /* hd, disk 2..3 */
405 dsk = device & (~0x3f);
406 lun = device & 0x3f;
407 break;
408 default:
409 return (0);
410 }
411 end:
412 *disk = dsk;
413 *plun = lun;
414 return (ret);
415 }
416
417 static int
disk_callback_xvmd(di_minor_t minor,di_node_t node)418 disk_callback_xvmd(di_minor_t minor, di_node_t node)
419 {
420 char *addr;
421 char disk[16];
422 uint_t targ;
423 uint_t dsk, lun;
424 int res;
425
426 addr = di_bus_addr(node);
427 targ = strtol(addr, (char **)NULL, 10);
428
429 res = decode_xen_device(targ, &dsk, &lun);
430
431 /* HVM device names are generated using the standard generator */
432
433 if (res == 1)
434 (void) snprintf(disk, sizeof (disk), "d%d", dsk);
435 else if (res == 2)
436 (void) snprintf(disk, sizeof (disk), "t%dd%d", dsk, lun);
437 else {
438 devfsadm_errprint("%s: invalid disk device number (%s)\n",
439 modname, addr);
440 return (DEVFSADM_CONTINUE);
441 }
442 disk_common(minor, node, disk, 0);
443 return (DEVFSADM_CONTINUE);
444
445 }
446
447 /*
448 * This function is called for every disk minor node.
449 * Calls enumerate to assign a logical controller number, and
450 * then devfsadm_mklink to make the link.
451 */
452 static void
disk_common(di_minor_t minor,di_node_t node,char * disk,int flags)453 disk_common(di_minor_t minor, di_node_t node, char *disk, int flags)
454 {
455 char l_path[PATH_MAX + 1];
456 char sec_path[PATH_MAX + 1];
457 char stale_re[DISK_SUBPATH_MAX];
458 char *dir;
459 char slice[4];
460 char *mn;
461 char *ctrl;
462 char *nt = NULL;
463 int *int_prop;
464 int nflags = 0;
465 #if defined(__i386) || defined(__amd64)
466 char mn_copy[4];
467 char *part;
468 int part_num;
469 #endif
470
471 mn = di_minor_name(minor);
472 if (strstr(mn, ",raw")) {
473 dir = "rdsk";
474 #if defined(__i386) || defined(__amd64)
475 (void) strncpy(mn_copy, mn, 4);
476 part = strtok(mn_copy, ",");
477 #endif
478 } else {
479 dir = "dsk";
480 #if defined(__i386) || defined(__amd64)
481 part = mn;
482 #endif
483 }
484
485 #if defined(__i386) || defined(__amd64)
486 /*
487 * The following is a table describing the allocation of
488 * minor numbers, minor names and /dev/dsk names for partitions
489 * and slices on x86 systems.
490 *
491 * Minor Number Minor Name /dev/dsk name
492 * ---------------------------------------------
493 * 0 to 15 "a" to "p" s0 to s15
494 * 16 "q" p0
495 * 17 to 20 "r" to "u" p1 to p4
496 * 21 to 52 "p5" to "p36" p5 to p36
497 *
498 */
499 part_num = atoi(part + 1);
500
501 if ((mn[0] == 'p') && (part_num >= 5)) {
502 /* logical drive */
503 (void) snprintf(slice, 4, "%s", part);
504 } else {
505 #endif
506 if (mn[0] < 'q') {
507 (void) sprintf(slice, "s%d", mn[0] - 'a');
508 } else if (strncmp(mn, MN_EFI, 2) != 0) {
509 (void) sprintf(slice, "p%d", mn[0] - 'q');
510 } else {
511 /* For EFI label */
512 (void) sprintf(slice, SLICE_EFI);
513 }
514 #if defined(__i386) || defined(__amd64)
515 }
516 #endif
517
518 nflags = 0;
519 if (system_labeled) {
520 nt = di_minor_nodetype(minor);
521 if ((nt != NULL) &&
522 ((strcmp(nt, DDI_NT_CD) == 0) ||
523 (strcmp(nt, DDI_NT_CD_CHAN) == 0) ||
524 (strcmp(nt, DDI_NT_BLOCK_CHAN) == 0))) {
525 nflags = DA_ADD|DA_CD;
526 }
527 }
528
529 if (reserved_links_exist(node, minor, nflags) == DEVFSADM_SUCCESS) {
530 devfsadm_print(disk_mid, "Reserved link exists. Not "
531 "creating links for slice %s\n", slice);
532 return;
533 }
534
535 if (NULL == (ctrl = diskctrl(node, minor)))
536 return;
537
538 (void) strcpy(l_path, dir);
539 (void) strcat(l_path, "/c");
540 (void) strcat(l_path, ctrl);
541 (void) strcat(l_path, disk);
542
543 /*
544 * If switching between SMI and EFI label or vice versa
545 * cleanup the previous label's devlinks.
546 */
547 if (*mn == *(MN_SMI) || (strncmp(mn, MN_EFI, 2) == 0)) {
548 char *s, tpath[PATH_MAX + 1];
549 struct stat sb;
550
551 s = l_path + strlen(l_path);
552 (void) strcat(l_path, (*mn == *(MN_SMI))
553 ? SLICE_EFI : SLICE_SMI);
554 /*
555 * Attempt the remove only if the stale link exists
556 */
557 (void) snprintf(tpath, sizeof (tpath), "%s/dev/%s",
558 devfsadm_root_path(), l_path);
559 if (lstat(tpath, &sb) != -1)
560 devfsadm_rm_all(l_path);
561 *s = '\0';
562 }
563 (void) strcat(l_path, slice);
564
565 (void) devfsadm_mklink(l_path, node, minor, nflags);
566
567 /* secondary links for removable and hotpluggable devices */
568 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "removable-media",
569 &int_prop) >= 0) {
570 (void) strcpy(sec_path, "removable-media/");
571 (void) strcat(sec_path, l_path);
572 (void) devfsadm_secondary_link(sec_path, l_path, 0);
573 }
574 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "hotpluggable",
575 &int_prop) >= 0) {
576 (void) strcpy(sec_path, "hotpluggable/");
577 (void) strcat(sec_path, l_path);
578 (void) devfsadm_secondary_link(sec_path, l_path, 0);
579 }
580
581 if ((flags & RM_STALE) == RM_STALE) {
582 (void) strcpy(stale_re, "^");
583 (void) strcat(stale_re, dir);
584 (void) strcat(stale_re, "/c");
585 (void) strcat(stale_re, ctrl);
586 (void) strcat(stale_re, "t[0-9A-F]+d[0-9]+(s[0-9]+)?$");
587 /*
588 * optimizations are made inside of devfsadm_rm_stale_links
589 * instead of before calling the function, as it always
590 * needs to add the valid link to the cache.
591 */
592 devfsadm_rm_stale_links(stale_re, l_path, node, minor);
593 }
594
595 free(ctrl);
596 }
597
598
599 /* index of enumeration rule applicable to this module */
600 #define RULE_INDEX 0
601
602 static char *
diskctrl(di_node_t node,di_minor_t minor)603 diskctrl(di_node_t node, di_minor_t minor)
604 {
605 char path[PATH_MAX + 1];
606 char *devfspath;
607 char *buf, *mn;
608
609 devfsadm_enumerate_t rules[3] = {
610 {"^r?dsk$/^c([0-9]+)", 1, MATCH_PARENT},
611 {"^cfg$/^c([0-9]+)$", 1, MATCH_ADDR},
612 {"^scsi$/^.+$/^c([0-9]+)", 1, MATCH_PARENT}
613 };
614
615 mn = di_minor_name(minor);
616
617 if ((devfspath = di_devfs_path(node)) == NULL) {
618 return (NULL);
619 }
620 (void) strcpy(path, devfspath);
621 (void) strcat(path, ":");
622 (void) strcat(path, mn);
623 di_devfs_path_free(devfspath);
624
625 /*
626 * Use controller component of disk path
627 */
628 if (disk_enumerate_int(path, RULE_INDEX, &buf, rules, 3) ==
629 DEVFSADM_MULTIPLE) {
630
631 /*
632 * We failed because there are multiple logical controller
633 * numbers for a single physical controller. If we use node
634 * name also in the match it should fix this and only find one
635 * logical controller. (See 4045879).
636 * NOTE: Rules for controllers are not changed, as there is
637 * no unique controller number for them in this case.
638 *
639 * MATCH_UNCACHED flag is private to the "disks" and "sgen"
640 * modules. NOT to be used by other modules.
641 */
642
643 rules[0].flags = MATCH_NODE | MATCH_UNCACHED; /* disks */
644 rules[2].flags = MATCH_NODE | MATCH_UNCACHED; /* generic scsi */
645 if (devfsadm_enumerate_int(path, RULE_INDEX, &buf, rules, 3)) {
646 return (NULL);
647 }
648 }
649
650 return (buf);
651 }
652
653 typedef struct dvlist {
654 char *dv_link;
655 struct dvlist *dv_next;
656 } dvlist_t;
657
658 static void
free_dvlist(dvlist_t ** pp)659 free_dvlist(dvlist_t **pp)
660 {
661 dvlist_t *entry;
662
663 while (*pp) {
664 entry = *pp;
665 *pp = entry->dv_next;
666 assert(entry->dv_link);
667 free(entry->dv_link);
668 free(entry);
669 }
670 }
671 static int
dvlink_cb(di_devlink_t devlink,void * arg)672 dvlink_cb(di_devlink_t devlink, void *arg)
673 {
674 char *path;
675 char *can_path;
676 dvlist_t **pp = (dvlist_t **)arg;
677 dvlist_t *entry = NULL;
678
679 entry = calloc(1, sizeof (dvlist_t));
680 if (entry == NULL) {
681 devfsadm_errprint("%s: calloc failed\n", modname);
682 goto error;
683 }
684
685 path = (char *)di_devlink_path(devlink);
686 assert(path);
687 if (path == NULL) {
688 devfsadm_errprint("%s: di_devlink_path() returned NULL\n",
689 modname);
690 goto error;
691 }
692
693 devfsadm_print(disk_mid, "%s: found link %s in reverse link cache\n",
694 modname, path);
695
696 /*
697 * Return linkname in canonical form i.e. without the
698 * "/dev/" prefix
699 */
700 can_path = strstr(path, "/dev/");
701 if (can_path == NULL) {
702 devfsadm_errprint("%s: devlink path %s has no /dev/\n",
703 modname, path);
704 goto error;
705 }
706
707 entry->dv_link = s_strdup(can_path + strlen("/dev/"));
708 entry->dv_next = *pp;
709 *pp = entry;
710
711 return (DI_WALK_CONTINUE);
712
713 error:
714 free(entry);
715 free_dvlist(pp);
716 *pp = NULL;
717 return (DI_WALK_TERMINATE);
718 }
719
720 /*
721 * Returns success only if all goes well. If there is no matching reserved link
722 * or if there is an error, we assume no match. It is better to err on the side
723 * of caution by creating extra links than to miss out creating a required link.
724 */
725 static int
reserved_links_exist(di_node_t node,di_minor_t minor,int nflags)726 reserved_links_exist(di_node_t node, di_minor_t minor, int nflags)
727 {
728 di_devlink_handle_t dvlink_cache = devfsadm_devlink_cache();
729 char phys_path[PATH_MAX];
730 char *minor_path;
731 dvlist_t *head;
732 dvlist_t *entry;
733 char *s;
734 char l[PATH_MAX];
735 int switch_link = 0;
736 char *mn = di_minor_name(minor);
737
738 if (dvlink_cache == NULL || mn == NULL) {
739 devfsadm_errprint("%s: No minor or devlink cache\n", modname);
740 return (DEVFSADM_FAILURE);
741 }
742
743 if (!devfsadm_have_reserved()) {
744 devfsadm_print(disk_mid, "%s: No reserved links\n", modname);
745 return (DEVFSADM_FAILURE);
746 }
747
748 minor_path = di_devfs_minor_path(minor);
749 if (minor_path == NULL) {
750 devfsadm_errprint("%s: di_devfs_minor_path failed\n", modname);
751 return (DEVFSADM_FAILURE);
752 }
753
754 (void) strlcpy(phys_path, minor_path, sizeof (phys_path));
755
756 di_devfs_path_free(minor_path);
757
758 head = NULL;
759 (void) di_devlink_cache_walk(dvlink_cache, DISK_LINK_RE, phys_path,
760 DI_PRIMARY_LINK, &head, dvlink_cb);
761
762 /*
763 * We may be switching between EFI label and SMI label in which case
764 * we only have minors of the other type.
765 */
766 if (head == NULL && (*mn == *(MN_SMI) ||
767 (strncmp(mn, MN_EFI, 2) == 0))) {
768 devfsadm_print(disk_mid, "%s: No links for minor %s in /dev. "
769 "Trying another label\n", modname, mn);
770 s = strrchr(phys_path, ':');
771 if (s == NULL) {
772 devfsadm_errprint("%s: invalid minor path: %s\n",
773 modname, phys_path);
774 return (DEVFSADM_FAILURE);
775 }
776 (void) snprintf(s+1, sizeof (phys_path) - (s + 1 - phys_path),
777 "%s%s", *mn == *(MN_SMI) ? MN_EFI : MN_SMI,
778 strstr(s, ",raw") ? ",raw" : "");
779 (void) di_devlink_cache_walk(dvlink_cache, DISK_LINK_RE,
780 phys_path, DI_PRIMARY_LINK, &head, dvlink_cb);
781 }
782
783 if (head == NULL) {
784 devfsadm_print(disk_mid, "%s: minor %s has no links in /dev\n",
785 modname, phys_path);
786 /* no links on disk */
787 return (DEVFSADM_FAILURE);
788 }
789
790 /*
791 * It suffices to use 1 link to this minor, since
792 * we are matching with reserved IDs on the basis of
793 * the controller number which will be the same for
794 * all links to this minor.
795 */
796 if (!devfsadm_is_reserved(disks_re_array, head->dv_link)) {
797 /* not reserved links */
798 devfsadm_print(disk_mid, "%s: devlink %s and its minor "
799 "are NOT reserved\n", modname, head->dv_link);
800 free_dvlist(&head);
801 return (DEVFSADM_FAILURE);
802 }
803
804 devfsadm_print(disk_mid, "%s: devlink %s and its minor are on "
805 "reserved list\n", modname, head->dv_link);
806
807 /*
808 * Switch between SMI and EFI labels if required
809 */
810 switch_link = 0;
811 if (*mn == *(MN_SMI) || (strncmp(mn, MN_EFI, 2) == 0)) {
812 for (entry = head; entry; entry = entry->dv_next) {
813 s = strrchr(entry->dv_link, '/');
814 assert(s);
815 if (s == NULL) {
816 devfsadm_errprint("%s: disk link %s has no "
817 "directory\n", modname, entry->dv_link);
818 continue;
819 }
820 if (*mn == *(MN_SMI) && strchr(s, 's') == NULL) {
821 (void) snprintf(l, sizeof (l), "%s%s",
822 entry->dv_link, SLICE_SMI);
823 switch_link = 1;
824 devfsadm_print(disk_mid, "%s: switching "
825 "reserved link from EFI to SMI label. "
826 "New link is %s\n", modname, l);
827 } else if (strncmp(mn, MN_EFI, 2) == 0 &&
828 (s = strchr(s, 's'))) {
829 *s = '\0';
830 (void) snprintf(l, sizeof (l), "%s",
831 entry->dv_link);
832 *s = 's';
833 switch_link = 1;
834 devfsadm_print(disk_mid, "%s: switching "
835 "reserved link from SMI to EFI label. "
836 "New link is %s\n", modname, l);
837 }
838 if (switch_link) {
839 devfsadm_print(disk_mid, "%s: switching "
840 "link: deleting %s and creating %s\n",
841 modname, entry->dv_link, l);
842 devfsadm_rm_link(entry->dv_link);
843 (void) devfsadm_mklink(l, node, minor, nflags);
844 }
845 }
846 }
847 free_dvlist(&head);
848
849 /*
850 * return SUCCESS to indicate that new links to this minor should not
851 * be created so that only compatibility links to this minor remain.
852 */
853 return (DEVFSADM_SUCCESS);
854 }
855