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 2016 Nexenta Systems, Inc. All rights reserved. 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <devfsadm.h> 28 #include <stdio.h> 29 #include <strings.h> 30 #include <stdlib.h> 31 #include <limits.h> 32 #include <ctype.h> 33 #include <sys/int_fmtio.h> 34 #include <sys/scsi/scsi_address.h> 35 #include <sys/libdevid.h> 36 37 #define SGEN_LINK_RE "^scsi/.+/c[0-9]+t[0-9A-F]+d[0-9]+$" 38 #define SGEN_DIR "scsi" 39 #define SGEN_CLASS "generic-scsi" 40 41 static int sgen_callback(di_minor_t minor, di_node_t node); 42 static char *find_ctrlr(di_node_t node, di_minor_t minor); 43 44 45 static devfsadm_create_t sgen_create_cbt[] = { 46 { SGEN_CLASS, "ddi_generic:scsi", NULL, 47 TYPE_EXACT | CREATE_DEFER, ILEVEL_0, sgen_callback 48 } 49 }; 50 51 DEVFSADM_CREATE_INIT_V0(sgen_create_cbt); 52 53 /* 54 * HOT auto cleanup of sgen links not desired. 55 */ 56 static devfsadm_remove_t sgen_remove_cbt[] = { 57 { SGEN_CLASS, SGEN_LINK_RE, RM_POST, 58 ILEVEL_0, devfsadm_rm_all 59 } 60 }; 61 62 DEVFSADM_REMOVE_INIT_V0(sgen_remove_cbt); 63 64 static int 65 sgen_callback(di_minor_t minor, di_node_t node) 66 { 67 char *baddr, *cnum, *tstr; 68 char lpath[PATH_MAX], buf[PATH_MAX]; 69 uchar_t *wwnstr; 70 char *tgt_port; 71 72 73 if ((cnum = find_ctrlr(node, minor)) == NULL) 74 goto done; 75 76 /* 77 * SCSAv3 attached devices. 78 */ 79 if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, 80 SCSI_ADDR_PROP_TARGET_PORT, &tgt_port) > 0) { 81 uint64_t wwn; 82 scsi_lun64_t sl; 83 scsi_lun_t lun; 84 int64_t lun64; 85 int64_t *lun64p; 86 int *intp; 87 uchar_t addr_method; 88 89 /* Get lun property */ 90 if ((di_prop_lookup_int64(DDI_DEV_T_ANY, node, 91 SCSI_ADDR_PROP_LUN64, &lun64p) > 0) && 92 (*lun64p != SCSI_LUN64_ILLEGAL)) { 93 lun64 = *lun64p; 94 } else if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, 95 SCSI_ADDR_PROP_LUN, &intp) > 0) { 96 lun64 = (uint64_t)*intp; 97 } 98 99 lun = scsi_lun64_to_lun(lun64); 100 101 addr_method = (lun.sl_lun1_msb & SCSI_LUN_AM_MASK); 102 103 (void) scsi_wwnstr_to_wwn(tgt_port, &wwn); 104 if ((addr_method == SCSI_LUN_AM_PDEV) && 105 (lun.sl_lun2_msb == 0) && (lun.sl_lun2_lsb == 0) && 106 (lun.sl_lun3_msb == 0) && (lun.sl_lun3_lsb == 0) && 107 (lun.sl_lun4_msb == 0) && (lun.sl_lun4_lsb == 0)) { 108 (void) snprintf(lpath, PATH_MAX, 109 "%s/%s/c%st%"PRIX64"d%"PRId64, SGEN_DIR, 110 di_minor_name(minor), cnum, wwn, lun64); 111 } else if ((addr_method == SCSI_LUN_AM_FLAT) && 112 (lun.sl_lun2_msb == 0) && (lun.sl_lun2_lsb == 0) && 113 (lun.sl_lun3_msb == 0) && (lun.sl_lun3_lsb == 0) && 114 (lun.sl_lun4_msb == 0) && (lun.sl_lun4_lsb == 0)) { 115 sl = (lun.sl_lun1_msb << 8) | lun.sl_lun1_lsb; 116 (void) snprintf(lpath, PATH_MAX, 117 "%s/%s/c%st%"PRIX64"d%"PRIX16, SGEN_DIR, 118 di_minor_name(minor), cnum, wwn, sl); 119 } else { 120 (void) snprintf(lpath, PATH_MAX, 121 "%s/%s/c%st%"PRIX64"d%"PRIX64, SGEN_DIR, 122 di_minor_name(minor), cnum, wwn, lun64); 123 } 124 } else if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, 125 "client-guid", (char **)&wwnstr) > 0) { 126 /* 127 * MPXIO-enabled devices; lun is always 0. 128 */ 129 if (strlcpy((char *)buf, (char *)wwnstr, sizeof (buf)) >= 130 sizeof (buf)) 131 goto done; 132 133 for (tstr = buf; *tstr != '\0'; tstr++) { 134 *tstr = toupper(*tstr); 135 } 136 if (snprintf(lpath, sizeof (lpath), "%s/%s/c%st%sd0", SGEN_DIR, 137 di_minor_name(minor), cnum, buf) >= sizeof (lpath)) 138 goto done; 139 140 } else if (di_prop_lookup_bytes(DDI_DEV_T_ANY, node, 141 "port-wwn", &wwnstr) == 8) { 142 /* 143 * "normal" fibre channel devices 144 */ 145 int lun, *lunp, count; 146 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "lun", &lunp) > 0) 147 lun = *lunp; 148 else 149 lun = 0; 150 151 for (count = 0, tstr = buf; count < 8; count++, tstr += 2) 152 (void) sprintf(tstr, "%02X", wwnstr[count]); 153 154 *tstr = '\0'; 155 if (snprintf(lpath, sizeof (lpath), "%s/%s/c%st%sd%d", SGEN_DIR, 156 di_minor_name(minor), cnum, buf, lun) >= sizeof (lpath)) 157 goto done; 158 } else { 159 /* 160 * Parallel SCSI devices 161 */ 162 uint_t targ, lun; 163 164 if ((baddr = di_bus_addr(node)) == NULL) 165 goto done; 166 167 if (sscanf(baddr, "%X,%X", &targ, &lun) != 2) 168 goto done; 169 170 if (snprintf(lpath, sizeof (lpath), "%s/%s/c%st%dd%d", SGEN_DIR, 171 di_minor_name(minor), cnum, targ, lun) >= sizeof (lpath)) 172 goto done; 173 } 174 175 (void) devfsadm_mklink(lpath, node, minor, 0); 176 done: 177 free(cnum); 178 return (DEVFSADM_CONTINUE); 179 } 180 181 /* index of enumeration rule applicable to this module */ 182 #define RULE_INDEX 2 183 184 static char * 185 find_ctrlr(di_node_t node, di_minor_t minor) 186 { 187 char path[PATH_MAX + 1]; 188 char *devfspath; 189 char *buf, *mn; 190 boolean_t is_vhci; 191 192 devfsadm_enumerate_t rules[3] = { 193 {"^r?dsk$/^c([0-9]+)", 1, MATCH_PARENT}, 194 {"^cfg$/^c([0-9]+)$", 1, MATCH_ADDR}, 195 {"^scsi$/^.+$/^c([0-9]+)", 1, MATCH_PARENT} 196 }; 197 198 mn = di_minor_name(minor); 199 200 if ((devfspath = di_devfs_path(node)) == NULL) { 201 return (NULL); 202 } 203 (void) strcpy(path, devfspath); 204 (void) strcat(path, ":"); 205 (void) strcat(path, mn); 206 di_devfs_path_free(devfspath); 207 208 /* 209 * Use controller (parent) component of device path 210 */ 211 is_vhci = (strncmp(path, "/scsi_vhci/", 11) == 0); 212 213 if (ctrl_enumerate_int(path, RULE_INDEX, &buf, rules, 3, 1, is_vhci) == 214 DEVFSADM_MULTIPLE) { 215 /* 216 * We failed because there are multiple logical controller 217 * numbers for a single physical controller. If we use node 218 * name also for DEVICE paths in the match it should fix this 219 * and only find one logical controller. (See 4045879). 220 * NOTE: Rules for controllers are not changed, as there is 221 * no unique controller number for them in this case. 222 * 223 * MATCH_UNCACHED flag is private to the "disks" and "sgen" 224 * modules. NOT to be used by other modules. 225 */ 226 rules[0].flags = MATCH_NODE | MATCH_UNCACHED; /* disks */ 227 rules[2].flags = MATCH_NODE | MATCH_UNCACHED; /* generic scsi */ 228 if (ctrl_enumerate_int(path, RULE_INDEX, &buf, rules, 3, 0, 229 is_vhci)) { 230 return (NULL); 231 } 232 } 233 234 return (buf); 235 } 236