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/scsi/scsi_address.h> 34 #include <sys/libdevid.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 *wwnstr; 69 char *tgt_port; 70 71 72 if ((cnum = find_ctrlr(node, minor)) == NULL) 73 goto done; 74 75 /* 76 * SCSAv3 attached devices. 77 */ 78 if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, 79 SCSI_ADDR_PROP_TARGET_PORT, &tgt_port) > 0) { 80 uint64_t wwn; 81 scsi_lun64_t sl; 82 scsi_lun_t lun; 83 int64_t lun64; 84 int64_t *lun64p; 85 int *intp; 86 uchar_t addr_method; 87 88 /* Get lun property */ 89 if ((di_prop_lookup_int64(DDI_DEV_T_ANY, node, 90 SCSI_ADDR_PROP_LUN64, &lun64p) > 0) && 91 (*lun64p != SCSI_LUN64_ILLEGAL)) { 92 lun64 = *lun64p; 93 } else if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, 94 SCSI_ADDR_PROP_LUN, &intp) > 0) { 95 lun64 = (uint64_t)*intp; 96 } 97 98 lun = scsi_lun64_to_lun(lun64); 99 100 addr_method = (lun.sl_lun1_msb & SCSI_LUN_AM_MASK); 101 102 (void) scsi_wwnstr_to_wwn(tgt_port, &wwn); 103 if ((addr_method == SCSI_LUN_AM_PDEV) && 104 (lun.sl_lun2_msb == 0) && (lun.sl_lun2_lsb == 0) && 105 (lun.sl_lun3_msb == 0) && (lun.sl_lun3_lsb == 0) && 106 (lun.sl_lun4_msb == 0) && (lun.sl_lun4_lsb == 0)) { 107 (void) snprintf(lpath, PATH_MAX, 108 "%s/%s/c%st%"PRIX64"d%"PRId64, SGEN_DIR, 109 di_minor_name(minor), cnum, wwn, lun64); 110 } else if ((addr_method == SCSI_LUN_AM_FLAT) && 111 (lun.sl_lun2_msb == 0) && (lun.sl_lun2_lsb == 0) && 112 (lun.sl_lun3_msb == 0) && (lun.sl_lun3_lsb == 0) && 113 (lun.sl_lun4_msb == 0) && (lun.sl_lun4_lsb == 0)) { 114 sl = (lun.sl_lun1_msb << 8) | lun.sl_lun1_lsb; 115 (void) snprintf(lpath, PATH_MAX, 116 "%s/%s/c%st%"PRIX64"d%"PRIX16, SGEN_DIR, 117 di_minor_name(minor), cnum, wwn, sl); 118 } else { 119 (void) snprintf(lpath, PATH_MAX, 120 "%s/%s/c%st%"PRIX64"d%"PRIX64, SGEN_DIR, 121 di_minor_name(minor), cnum, wwn, lun64); 122 } 123 } else if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, 124 "client-guid", (char **)&wwnstr) > 0) { 125 /* 126 * MPXIO-enabled devices; lun is always 0. 127 */ 128 if (strlcpy((char *)buf, (char *)wwnstr, sizeof (buf)) >= 129 sizeof (buf)) 130 goto done; 131 132 for (tstr = buf; *tstr != '\0'; tstr++) { 133 *tstr = toupper(*tstr); 134 } 135 if (snprintf(lpath, sizeof (lpath), "%s/%s/c%st%sd0", SGEN_DIR, 136 di_minor_name(minor), cnum, buf) >= sizeof (lpath)) 137 goto done; 138 139 } else if (di_prop_lookup_bytes(DDI_DEV_T_ANY, node, 140 "port-wwn", &wwnstr) == 8) { 141 /* 142 * "normal" fibre channel devices 143 */ 144 int lun, *lunp, count; 145 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "lun", &lunp) > 0) 146 lun = *lunp; 147 else 148 lun = 0; 149 150 for (count = 0, tstr = buf; count < 8; count++, tstr += 2) 151 (void) sprintf(tstr, "%02X", wwnstr[count]); 152 153 *tstr = '\0'; 154 if (snprintf(lpath, sizeof (lpath), "%s/%s/c%st%sd%d", SGEN_DIR, 155 di_minor_name(minor), cnum, buf, lun) >= sizeof (lpath)) 156 goto done; 157 } else { 158 /* 159 * Parallel SCSI devices 160 */ 161 uint_t targ, lun; 162 163 if ((baddr = di_bus_addr(node)) == NULL) 164 goto done; 165 166 if (sscanf(baddr, "%X,%X", &targ, &lun) != 2) 167 goto done; 168 169 if (snprintf(lpath, sizeof (lpath), "%s/%s/c%st%dd%d", SGEN_DIR, 170 di_minor_name(minor), cnum, targ, lun) >= sizeof (lpath)) 171 goto done; 172 } 173 174 (void) devfsadm_mklink(lpath, node, minor, 0); 175 done: 176 free(cnum); 177 return (DEVFSADM_CONTINUE); 178 } 179 180 /* index of enumeration rule applicable to this module */ 181 #define RULE_INDEX 2 182 183 static char * 184 find_ctrlr(di_node_t node, di_minor_t minor) 185 { 186 char path[PATH_MAX + 1]; 187 char *devfspath; 188 char *buf, *mn; 189 190 devfsadm_enumerate_t rules[3] = { 191 {"^r?dsk$/^c([0-9]+)", 1, MATCH_PARENT}, 192 {"^cfg$/^c([0-9]+)$", 1, MATCH_ADDR}, 193 {"^scsi$/^.+$/^c([0-9]+)", 1, MATCH_PARENT} 194 }; 195 196 mn = di_minor_name(minor); 197 198 if ((devfspath = di_devfs_path(node)) == NULL) { 199 return (NULL); 200 } 201 (void) strcpy(path, devfspath); 202 (void) strcat(path, ":"); 203 (void) strcat(path, mn); 204 di_devfs_path_free(devfspath); 205 206 /* 207 * Use controller (parent) component of device path 208 */ 209 if (disk_enumerate_int(path, RULE_INDEX, &buf, rules, 3) == 210 DEVFSADM_MULTIPLE) { 211 212 /* 213 * We failed because there are multiple logical controller 214 * numbers for a single physical controller. If we use node 215 * name also for DEVICE paths in the match it should fix this 216 * and only find one logical controller. (See 4045879). 217 * NOTE: Rules for controllers are not changed, as there is 218 * no unique controller number for them in this case. 219 * 220 * MATCH_UNCACHED flag is private to the "disks" and "sgen" 221 * modules. NOT to be used by other modules. 222 */ 223 rules[0].flags = MATCH_NODE | MATCH_UNCACHED; /* disks */ 224 rules[2].flags = MATCH_NODE | MATCH_UNCACHED; /* generic scsi */ 225 if (devfsadm_enumerate_int(path, RULE_INDEX, &buf, rules, 3)) { 226 return (NULL); 227 } 228 } 229 230 return (buf); 231 } 232