xref: /illumos-gate/usr/src/cmd/devfsadm/sgen_link.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <devfsadm.h>
30 #include <stdio.h>
31 #include <strings.h>
32 #include <stdlib.h>
33 #include <limits.h>
34 #include <ctype.h>
35 
36 #define	SGEN_LINK_RE	"^scsi/.+/c[0-9]+t[0-9A-F]+d[0-9]+$"
37 #define	SGEN_DIR	"scsi"
38 #define	SGEN_CLASS	"generic-scsi"
39 
40 static int sgen_callback(di_minor_t minor, di_node_t node);
41 static char *find_ctrlr(di_node_t node, di_minor_t minor);
42 
43 
44 static devfsadm_create_t sgen_create_cbt[] = {
45 	{ SGEN_CLASS, "ddi_generic:scsi", NULL,
46 	    TYPE_EXACT | CREATE_DEFER, ILEVEL_0, sgen_callback
47 	}
48 };
49 
50 DEVFSADM_CREATE_INIT_V0(sgen_create_cbt);
51 
52 /*
53  * HOT auto cleanup of sgen links not desired.
54  */
55 static devfsadm_remove_t sgen_remove_cbt[] = {
56 	{ SGEN_CLASS, SGEN_LINK_RE, RM_POST,
57 		ILEVEL_0, devfsadm_rm_all
58 	}
59 };
60 
61 DEVFSADM_REMOVE_INIT_V0(sgen_remove_cbt);
62 
63 static int
64 sgen_callback(di_minor_t minor, di_node_t node)
65 {
66 	char *baddr, *cnum, *tstr;
67 	char lpath[PATH_MAX], buf[PATH_MAX];
68 	uchar_t *wwn;
69 
70 	if ((cnum = find_ctrlr(node, minor)) == NULL)
71 		goto done;
72 
73 	if (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
74 	    "client-guid", (char **)&wwn) > 0) {
75 		/*
76 		 * MPXIO-enabled devices; lun is always 0.
77 		 */
78 		if (strlcpy((char *)buf, (char *)wwn, sizeof (buf)) >=
79 		    sizeof (buf))
80 			goto done;
81 
82 		for (tstr = buf; *tstr != '\0'; tstr++) {
83 			*tstr = toupper(*tstr);
84 		}
85 		if (snprintf(lpath, sizeof (lpath), "%s/%s/c%st%sd0", SGEN_DIR,
86 		    di_minor_name(minor), cnum, buf) >= sizeof (lpath))
87 			goto done;
88 
89 	} else if (di_prop_lookup_bytes(DDI_DEV_T_ANY, node,
90 	    "port-wwn", &wwn) == 8) {
91 		/*
92 		 * "normal" fibre channel devices
93 		 */
94 		int lun, *lunp, count;
95 		if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "lun", &lunp) > 0)
96 			lun = *lunp;
97 		else
98 			lun = 0;
99 
100 		for (count = 0, tstr = buf; count < 8; count++, tstr += 2)
101 			(void) sprintf(tstr, "%02X", wwn[count]);
102 
103 		*tstr = '\0';
104 		if (snprintf(lpath, sizeof (lpath), "%s/%s/c%st%sd%d", SGEN_DIR,
105 		    di_minor_name(minor), cnum, buf, lun) >= sizeof (lpath))
106 			goto done;
107 	} else {
108 		/*
109 		 * Parallel SCSI devices
110 		 */
111 		uint_t targ, lun;
112 
113 		if ((baddr = di_bus_addr(node)) == NULL)
114 			goto done;
115 
116 		if (sscanf(baddr, "%X,%X", &targ, &lun) != 2)
117 			goto done;
118 
119 		if (snprintf(lpath, sizeof (lpath), "%s/%s/c%st%dd%d", SGEN_DIR,
120 		    di_minor_name(minor), cnum, targ, lun) >= sizeof (lpath))
121 			goto done;
122 	}
123 
124 	(void) devfsadm_mklink(lpath, node, minor, 0);
125 done:
126 	free(cnum);
127 	return (DEVFSADM_CONTINUE);
128 }
129 
130 /* index of enumeration rule applicable to this module */
131 #define	RULE_INDEX	2
132 
133 static char *
134 find_ctrlr(di_node_t node, di_minor_t minor)
135 {
136 	char path[PATH_MAX + 1];
137 	char *devfspath;
138 	char *buf, *mn;
139 
140 	devfsadm_enumerate_t rules[3] = {
141 	    {"^r?dsk$/^c([0-9]+)", 1, MATCH_PARENT},
142 	    {"^cfg$/^c([0-9]+)$", 1, MATCH_ADDR},
143 	    {"^scsi$/^.+$/^c([0-9]+)", 1, MATCH_PARENT}
144 	};
145 
146 	mn = di_minor_name(minor);
147 
148 	if ((devfspath = di_devfs_path(node)) == NULL) {
149 		return (NULL);
150 	}
151 	(void) strcpy(path, devfspath);
152 	(void) strcat(path, ":");
153 	(void) strcat(path, mn);
154 	di_devfs_path_free(devfspath);
155 
156 	/*
157 	 * Use controller (parent) component of device path
158 	 */
159 	if (disk_enumerate_int(path, RULE_INDEX, &buf, rules, 3) ==
160 	    DEVFSADM_MULTIPLE) {
161 
162 		/*
163 		 * We failed because there are multiple logical controller
164 		 * numbers for a single physical controller.  If we use node
165 		 * name also for DEVICE paths in the match it should fix this
166 		 * and only find one logical controller. (See 4045879).
167 		 * NOTE: Rules for controllers are not changed, as there is
168 		 * no unique controller number for them in this case.
169 		 *
170 		 * MATCH_UNCACHED flag is private to the "disks" and "sgen"
171 		 * modules. NOT to be used by other modules.
172 		 */
173 		rules[0].flags = MATCH_NODE | MATCH_UNCACHED; /* disks */
174 		rules[2].flags = MATCH_NODE | MATCH_UNCACHED; /* generic scsi */
175 		if (devfsadm_enumerate_int(path, RULE_INDEX, &buf, rules, 3)) {
176 			return (NULL);
177 		}
178 	}
179 
180 	return (buf);
181 }
182