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