xref: /titanic_50/usr/src/cmd/devfsadm/disk_link.c (revision da5577f07f6199b51ea374581248790c288e827b)
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
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
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
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
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
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
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
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
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
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
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 *
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
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
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
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