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 sec_path[PATH_MAX + 1]; 210 char stale_re[DISK_SUBPATH_MAX]; 211 char *dir; 212 char slice[4]; 213 char *mn; 214 char *ctrl; 215 char *nt = NULL; 216 int *int_prop; 217 int nflags = 0; 218 219 if (strstr(mn = di_minor_name(minor), ",raw")) { 220 dir = "rdsk"; 221 } else { 222 dir = "dsk"; 223 } 224 225 if (mn[0] < 113) { 226 (void) sprintf(slice, "s%d", mn[0] - 'a'); 227 } else if (strncmp(mn, MN_EFI, 2) != 0) { 228 (void) sprintf(slice, "p%d", mn[0] - 'q'); 229 } else { 230 /* For EFI label */ 231 (void) sprintf(slice, SLICE_EFI); 232 } 233 234 if (NULL == (ctrl = diskctrl(node, minor))) 235 return; 236 237 (void) strcpy(l_path, dir); 238 (void) strcat(l_path, "/c"); 239 (void) strcat(l_path, ctrl); 240 (void) strcat(l_path, disk); 241 242 /* 243 * If switching between SMI and EFI label or vice versa 244 * cleanup the previous label's devlinks. 245 */ 246 if (*mn == *(MN_SMI) || (strncmp(mn, MN_EFI, 2) == 0)) { 247 char *s, tpath[PATH_MAX + 1]; 248 struct stat sb; 249 250 s = l_path + strlen(l_path); 251 (void) strcat(l_path, (*mn == *(MN_SMI)) 252 ? SLICE_EFI : SLICE_SMI); 253 /* 254 * Attempt the remove only if the stale link exists 255 */ 256 (void) snprintf(tpath, sizeof (tpath), "%s/dev/%s", 257 devfsadm_root_path(), l_path); 258 if (lstat(tpath, &sb) != -1) 259 devfsadm_rm_all(l_path); 260 *s = '\0'; 261 } 262 (void) strcat(l_path, slice); 263 264 if (system_labeled) { 265 nt = di_minor_nodetype(minor); 266 if ((nt != NULL) && 267 ((strcmp(nt, DDI_NT_CD) == 0) || 268 (strcmp(nt, DDI_NT_CD_CHAN) == 0) || 269 (strcmp(nt, DDI_NT_BLOCK_CHAN) == 0))) { 270 nflags = DA_ADD|DA_CD; 271 } 272 } 273 274 (void) devfsadm_mklink(l_path, node, minor, nflags); 275 276 /* secondary links for removable and hotpluggable devices */ 277 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "removable-media", 278 &int_prop) >= 0) { 279 (void) strcpy(sec_path, "removable-media/"); 280 (void) strcat(sec_path, l_path); 281 (void) devfsadm_secondary_link(sec_path, l_path, 0); 282 } else if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "hotpluggable", 283 &int_prop) >= 0) { 284 (void) strcpy(sec_path, "hotpluggable/"); 285 (void) strcat(sec_path, l_path); 286 (void) devfsadm_secondary_link(sec_path, l_path, 0); 287 } 288 289 if ((flags & RM_STALE) == RM_STALE) { 290 (void) strcpy(stale_re, "^"); 291 (void) strcat(stale_re, dir); 292 (void) strcat(stale_re, "/c"); 293 (void) strcat(stale_re, ctrl); 294 (void) strcat(stale_re, "t[0-9A-F]+d[0-9]+(s[0-9]+)?$"); 295 /* 296 * optimizations are made inside of devfsadm_rm_stale_links 297 * instead of before calling the function, as it always 298 * needs to add the valid link to the cache. 299 */ 300 devfsadm_rm_stale_links(stale_re, l_path, node, minor); 301 } 302 303 free(ctrl); 304 } 305 306 307 /* index of enumeration rule applicable to this module */ 308 #define RULE_INDEX 0 309 310 static char * 311 diskctrl(di_node_t node, di_minor_t minor) 312 { 313 char path[PATH_MAX + 1]; 314 char *devfspath; 315 char *buf, *mn; 316 317 devfsadm_enumerate_t rules[3] = { 318 {"^r?dsk$/^c([0-9]+)", 1, MATCH_PARENT}, 319 {"^cfg$/^c([0-9]+)$", 1, MATCH_ADDR}, 320 {"^scsi$/^.+$/^c([0-9]+)", 1, MATCH_PARENT} 321 }; 322 323 mn = di_minor_name(minor); 324 325 if ((devfspath = di_devfs_path(node)) == NULL) { 326 return (NULL); 327 } 328 (void) strcpy(path, devfspath); 329 (void) strcat(path, ":"); 330 (void) strcat(path, mn); 331 di_devfs_path_free(devfspath); 332 333 /* 334 * Use controller component of disk path 335 */ 336 if (disk_enumerate_int(path, RULE_INDEX, &buf, rules, 3) == 337 DEVFSADM_MULTIPLE) { 338 339 /* 340 * We failed because there are multiple logical controller 341 * numbers for a single physical controller. If we use node 342 * name also in the match it should fix this and only find one 343 * logical controller. (See 4045879). 344 * NOTE: Rules for controllers are not changed, as there is 345 * no unique controller number for them in this case. 346 * 347 * MATCH_UNCACHED flag is private to the "disks" and "sgen" 348 * modules. NOT to be used by other modules. 349 */ 350 351 rules[0].flags = MATCH_NODE | MATCH_UNCACHED; /* disks */ 352 rules[2].flags = MATCH_NODE | MATCH_UNCACHED; /* generic scsi */ 353 if (devfsadm_enumerate_int(path, RULE_INDEX, &buf, rules, 3)) { 354 return (NULL); 355 } 356 } 357 358 return (buf); 359 } 360