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 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <devfsadm.h> 29 #include <stdio.h> 30 #include <strings.h> 31 #include <stdlib.h> 32 #include <limits.h> 33 #include <sys/stat.h> 34 #include <bsm/devalloc.h> 35 36 #define DISK_SUBPATH_MAX 100 37 #define RM_STALE 0x01 38 #define DISK_LINK_RE "^r?dsk/c[0-9]+(t[0-9A-F]+)?d[0-9]+(((s|p))[0-9]+)?$" 39 #define DISK_LINK_TO_UPPER(ch)\ 40 (((ch) >= 'a' && (ch) <= 'z') ? (ch - 'a' + 'A') : ch) 41 42 #define SLICE_SMI "s7" 43 #define SLICE_EFI "" 44 45 #define MN_SMI "h" 46 #define MN_EFI "wd" 47 #define ASCIIWWNSIZE 255 48 49 extern int system_labeled; 50 51 static int disk_callback_chan(di_minor_t minor, di_node_t node); 52 static int disk_callback_nchan(di_minor_t minor, di_node_t node); 53 static int disk_callback_wwn(di_minor_t minor, di_node_t node); 54 static int disk_callback_fabric(di_minor_t minor, di_node_t node); 55 static void disk_common(di_minor_t minor, di_node_t node, char *disk, 56 int flags); 57 static char *diskctrl(di_node_t node, di_minor_t minor); 58 extern void rm_link_from_cache(char *devlink, char *physpath); 59 60 61 static devfsadm_create_t disk_cbt[] = { 62 { "disk", "ddi_block", NULL, 63 TYPE_EXACT, ILEVEL_0, disk_callback_nchan 64 }, 65 { "disk", "ddi_block:channel", NULL, 66 TYPE_EXACT, ILEVEL_0, disk_callback_chan 67 }, 68 { "disk", "ddi_block:fabric", NULL, 69 TYPE_EXACT, ILEVEL_0, disk_callback_fabric 70 }, 71 { "disk", "ddi_block:wwn", NULL, 72 TYPE_EXACT, ILEVEL_0, disk_callback_wwn 73 }, 74 { "disk", "ddi_block:cdrom", NULL, 75 TYPE_EXACT, ILEVEL_0, disk_callback_nchan 76 }, 77 { "disk", "ddi_block:cdrom:channel", NULL, 78 TYPE_EXACT, ILEVEL_0, disk_callback_chan 79 }, 80 }; 81 82 DEVFSADM_CREATE_INIT_V0(disk_cbt); 83 84 /* 85 * HOT auto cleanup of disks not desired. 86 */ 87 static devfsadm_remove_t disk_remove_cbt[] = { 88 { "disk", DISK_LINK_RE, RM_POST, 89 ILEVEL_0, devfsadm_rm_all 90 } 91 }; 92 93 DEVFSADM_REMOVE_INIT_V0(disk_remove_cbt); 94 95 static int 96 disk_callback_chan(di_minor_t minor, di_node_t node) 97 { 98 char *addr; 99 char disk[20]; 100 uint_t targ; 101 uint_t lun; 102 103 addr = di_bus_addr(node); 104 (void) sscanf(addr, "%X,%X", &targ, &lun); 105 (void) sprintf(disk, "t%dd%d", targ, lun); 106 disk_common(minor, node, disk, 0); 107 return (DEVFSADM_CONTINUE); 108 109 } 110 111 static int 112 disk_callback_nchan(di_minor_t minor, di_node_t node) 113 { 114 char *addr; 115 char disk[10]; 116 uint_t lun; 117 118 addr = di_bus_addr(node); 119 (void) sscanf(addr, "%X", &lun); 120 (void) sprintf(disk, "d%d", lun); 121 disk_common(minor, node, disk, 0); 122 return (DEVFSADM_CONTINUE); 123 124 } 125 126 static int 127 disk_callback_wwn(di_minor_t minor, di_node_t node) 128 { 129 char disk[10]; 130 int lun; 131 int targ; 132 int *intp; 133 134 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, 135 "target", &intp) <= 0) { 136 return (DEVFSADM_CONTINUE); 137 } 138 targ = *intp; 139 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, 140 "lun", &intp) <= 0) { 141 lun = 0; 142 } else { 143 lun = *intp; 144 } 145 (void) sprintf(disk, "t%dd%d", targ, lun); 146 147 disk_common(minor, node, disk, RM_STALE); 148 149 return (DEVFSADM_CONTINUE); 150 } 151 152 static int 153 disk_callback_fabric(di_minor_t minor, di_node_t node) 154 { 155 char disk[DISK_SUBPATH_MAX]; 156 int lun; 157 int count; 158 int *intp; 159 uchar_t *str; 160 uchar_t *wwn; 161 uchar_t ascii_wwn[ASCIIWWNSIZE]; 162 163 if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, 164 "client-guid", (char **)&wwn) > 0) { 165 if (strlcpy((char *)ascii_wwn, (char *)wwn, sizeof (ascii_wwn)) 166 >= sizeof (ascii_wwn)) { 167 devfsadm_errprint("SUNW_disk_link: GUID too long:%d", 168 strlen((char *)wwn)); 169 return (DEVFSADM_CONTINUE); 170 } 171 lun = 0; 172 } else if (di_prop_lookup_bytes(DDI_DEV_T_ANY, node, 173 "port-wwn", &wwn) > 0) { 174 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, 175 "lun", &intp) > 0) { 176 lun = *intp; 177 } else { 178 lun = 0; 179 } 180 181 for (count = 0, str = ascii_wwn; count < 8; count++, str += 2) { 182 (void) sprintf((caddr_t)str, "%02x", wwn[count]); 183 } 184 *str = '\0'; 185 } else { 186 return (DEVFSADM_CONTINUE); 187 } 188 189 for (str = ascii_wwn; *str != '\0'; str++) { 190 *str = DISK_LINK_TO_UPPER(*str); 191 } 192 193 (void) snprintf(disk, DISK_SUBPATH_MAX, "t%sd%d", ascii_wwn, lun); 194 195 disk_common(minor, node, disk, RM_STALE); 196 197 return (DEVFSADM_CONTINUE); 198 } 199 200 /* 201 * This function is called for every disk minor node. 202 * Calls enumerate to assign a logical controller number, and 203 * then devfsadm_mklink to make the link. 204 */ 205 static void 206 disk_common(di_minor_t minor, di_node_t node, char *disk, int flags) 207 { 208 char l_path[PATH_MAX + 1]; 209 char stale_re[DISK_SUBPATH_MAX]; 210 char *dir; 211 char slice[4]; 212 char *mn; 213 char *ctrl; 214 char *nt = NULL; 215 int nflags = 0; 216 217 if (strstr(mn = di_minor_name(minor), ",raw")) { 218 dir = "rdsk"; 219 } else { 220 dir = "dsk"; 221 } 222 223 if (mn[0] < 113) { 224 (void) sprintf(slice, "s%d", mn[0] - 'a'); 225 } else if (strncmp(mn, MN_EFI, 2) != 0) { 226 (void) sprintf(slice, "p%d", mn[0] - 'q'); 227 } else { 228 /* For EFI label */ 229 (void) sprintf(slice, SLICE_EFI); 230 } 231 232 if (NULL == (ctrl = diskctrl(node, minor))) 233 return; 234 235 (void) strcpy(l_path, dir); 236 (void) strcat(l_path, "/c"); 237 (void) strcat(l_path, ctrl); 238 (void) strcat(l_path, disk); 239 240 /* 241 * If switching between SMI and EFI label or vice versa 242 * cleanup the previous label's devlinks. 243 */ 244 if (*mn == *(MN_SMI) || (strncmp(mn, MN_EFI, 2) == 0)) { 245 char *s, tpath[PATH_MAX + 1]; 246 struct stat sb; 247 248 s = l_path + strlen(l_path); 249 (void) strcat(l_path, (*mn == *(MN_SMI)) 250 ? SLICE_EFI : SLICE_SMI); 251 /* 252 * Attempt the remove only if the stale link exists 253 */ 254 (void) snprintf(tpath, sizeof (tpath), "%s/dev/%s", 255 devfsadm_root_path(), l_path); 256 if (lstat(tpath, &sb) != -1) 257 devfsadm_rm_all(l_path); 258 *s = '\0'; 259 } 260 (void) strcat(l_path, slice); 261 262 if (system_labeled) { 263 nt = di_minor_nodetype(minor); 264 if ((nt != NULL) && 265 ((strcmp(nt, DDI_NT_CD) == 0) || 266 (strcmp(nt, DDI_NT_CD_CHAN) == 0) || 267 (strcmp(nt, DDI_NT_BLOCK_CHAN) == 0))) { 268 nflags = DA_ADD|DA_CD; 269 } 270 } 271 272 (void) devfsadm_mklink(l_path, node, minor, nflags); 273 274 if ((flags & RM_STALE) == RM_STALE) { 275 (void) strcpy(stale_re, "^"); 276 (void) strcat(stale_re, dir); 277 (void) strcat(stale_re, "/c"); 278 (void) strcat(stale_re, ctrl); 279 (void) strcat(stale_re, "t[0-9A-F]+d[0-9]+(s[0-9]+)?$"); 280 /* 281 * optimizations are made inside of devfsadm_rm_stale_links 282 * instead of before calling the function, as it always 283 * needs to add the valid link to the cache. 284 */ 285 devfsadm_rm_stale_links(stale_re, l_path, node, minor); 286 } 287 288 free(ctrl); 289 } 290 291 292 /* index of enumeration rule applicable to this module */ 293 #define RULE_INDEX 0 294 295 static char * 296 diskctrl(di_node_t node, di_minor_t minor) 297 { 298 char path[PATH_MAX + 1]; 299 char *devfspath; 300 char *buf, *mn; 301 302 devfsadm_enumerate_t rules[3] = { 303 {"^r?dsk$/^c([0-9]+)", 1, MATCH_PARENT}, 304 {"^cfg$/^c([0-9]+)$", 1, MATCH_ADDR}, 305 {"^scsi$/^.+$/^c([0-9]+)", 1, MATCH_PARENT} 306 }; 307 308 mn = di_minor_name(minor); 309 310 if ((devfspath = di_devfs_path(node)) == NULL) { 311 return (NULL); 312 } 313 (void) strcpy(path, devfspath); 314 (void) strcat(path, ":"); 315 (void) strcat(path, mn); 316 di_devfs_path_free(devfspath); 317 318 /* 319 * Use controller component of disk path 320 */ 321 if (disk_enumerate_int(path, RULE_INDEX, &buf, rules, 3) == 322 DEVFSADM_MULTIPLE) { 323 324 /* 325 * We failed because there are multiple logical controller 326 * numbers for a single physical controller. If we use node 327 * name also in the match it should fix this and only find one 328 * logical controller. (See 4045879). 329 * NOTE: Rules for controllers are not changed, as there is 330 * no unique controller number for them in this case. 331 * 332 * MATCH_UNCACHED flag is private to the "disks" and "sgen" 333 * modules. NOT to be used by other modules. 334 */ 335 336 rules[0].flags = MATCH_NODE | MATCH_UNCACHED; /* disks */ 337 rules[2].flags = MATCH_NODE | MATCH_UNCACHED; /* generic scsi */ 338 if (devfsadm_enumerate_int(path, RULE_INDEX, &buf, rules, 3)) { 339 return (NULL); 340 } 341 } 342 343 return (buf); 344 } 345