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