xref: /illumos-gate/usr/src/cmd/devfsadm/disk_link.c (revision ccd81fdd)
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%dd%"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%dd%"PRIX16, *intp, sl);
306 		} else {
307 			(void) snprintf(disk, DISK_SUBPATH_MAX,
308 			    "t%dd%"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