1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright (c) 2013, Joyent, Inc. All rights reserved. 14 */ 15 16 #include <unistd.h> 17 #include <stdio.h> 18 #include <stdlib.h> 19 #include <fcntl.h> 20 #include <string.h> 21 #include <strings.h> 22 #include <limits.h> 23 #include <alloca.h> 24 #include <errno.h> 25 #include <libnvpair.h> 26 #include <sys/types.h> 27 #include <sys/stat.h> 28 #include <sys/param.h> 29 #include <sys/fm/protocol.h> 30 #include <fm/libtopo.h> 31 #include <fm/topo_mod.h> 32 33 #include "sys/scsi/adapters/mpt_sas/mptsas_ioctl.h" 34 35 #define TOPO_METH_MPTSAS_LED_MODE_VERSION 0 36 37 static int fac_prov_mptsas_enum(topo_mod_t *, tnode_t *, const char *, 38 topo_instance_t, topo_instance_t, void *, void *); 39 40 /* 41 * mpt_sas facility provider methods 42 */ 43 static int mptsas_led_mode(topo_mod_t *, tnode_t *, topo_version_t, 44 nvlist_t *, nvlist_t **); 45 46 const topo_modops_t mptsas_ops = { fac_prov_mptsas_enum, NULL }; 47 48 const topo_modinfo_t mptsas_info = 49 { "mpt_sas facility provider", FM_FMRI_SCHEME_HC, TOPO_VERSION, 50 &mptsas_ops }; 51 52 static const topo_method_t mptsas_fac_methods[] = { 53 { "mptsas_led_mode", TOPO_PROP_METH_DESC, 54 TOPO_METH_MPTSAS_LED_MODE_VERSION, 55 TOPO_STABILITY_INTERNAL, mptsas_led_mode }, 56 { NULL } 57 }; 58 59 /*ARGSUSED*/ 60 int 61 _topo_init(topo_mod_t *mod, topo_version_t version) 62 { 63 if (getenv("TOPOFACMPTSASDEBUG") != NULL) 64 topo_mod_setdebug(mod); 65 66 return (topo_mod_register(mod, &mptsas_info, TOPO_VERSION)); 67 } 68 69 void 70 _topo_fini(topo_mod_t *mod) 71 { 72 topo_mod_unregister(mod); 73 } 74 75 /* 76 * Get or set LED state for a particular target attached to an mpt_sas 77 * instance at (Enclosure Number, Slot Number). The function returns 78 * -1 on error and sets errno to ENOENT _only_ if the /devices node 79 * (*devctl) does not exist. 80 */ 81 static int 82 do_led_control(topo_mod_t *mod, char *devctl, uint16_t enclosure, 83 uint16_t slot, uint8_t led, uint32_t *ledmode, boolean_t set) 84 { 85 int fd; 86 mptsas_led_control_t lc; 87 88 bzero(&lc, sizeof (lc)); 89 90 lc.Command = set ? MPTSAS_LEDCTL_FLAG_SET : MPTSAS_LEDCTL_FLAG_GET; 91 lc.Enclosure = enclosure; 92 lc.Slot = slot; 93 lc.Led = led; 94 lc.LedStatus = *ledmode; 95 96 if ((fd = open(devctl, (set ? O_RDWR : O_RDONLY))) == -1) { 97 int en = errno; 98 topo_mod_dprintf(mod, "devctl open failed: %s", 99 strerror(errno)); 100 errno = en; 101 return (-1); 102 } 103 104 if (ioctl(fd, MPTIOCTL_LED_CONTROL, &lc) == -1) { 105 if (errno == ENOENT) { 106 /* 107 * If there is not presently a target attached for 108 * a particular enclosure/slot pair then the driver 109 * does not track LED status for this bay. Assume 110 * all LEDs are off. 111 */ 112 lc.LedStatus = 0; 113 } else { 114 int en = errno; 115 topo_mod_dprintf(mod, "led control ioctl failed: %s", 116 strerror(errno)); 117 (void) close(fd); 118 errno = en; 119 return (-1); 120 } 121 } 122 123 *ledmode = lc.LedStatus ? TOPO_LED_STATE_ON : TOPO_LED_STATE_OFF; 124 125 (void) close(fd); 126 errno = 0; 127 return (0); 128 } 129 130 static int 131 mptsas_led_mode(topo_mod_t *mod, tnode_t *node, topo_version_t vers, 132 nvlist_t *in, nvlist_t **nvout) 133 { 134 int err, ret = 0; 135 tnode_t *pnode = topo_node_parent(node); 136 uint32_t type, ledmode = 0; 137 nvlist_t *pargs, *nvl; 138 char *driver = NULL, *devctl = NULL; 139 uint32_t enclosure, slot; 140 uint8_t mptsas_led; 141 boolean_t set, done; 142 char *elem, *lastp; 143 144 if (vers > TOPO_METH_MPTSAS_LED_MODE_VERSION) 145 return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW)); 146 147 if (topo_prop_get_string(pnode, TOPO_PGROUP_BINDING, 148 TOPO_BINDING_DRIVER, &driver, &err) != 0 || 149 strcmp("mpt_sas", driver) != 0) { 150 topo_mod_dprintf(mod, "%s: Facility driver was not mpt_sas", 151 __func__); 152 ret = topo_mod_seterrno(mod, EMOD_NVL_INVAL); 153 goto out; 154 } 155 if (topo_prop_get_uint32(node, TOPO_PGROUP_FACILITY, TOPO_FACILITY_TYPE, 156 &type, &err) != 0) { 157 topo_mod_dprintf(mod, "%s: Failed to lookup %s property " 158 "(%s)", __func__, TOPO_FACILITY_TYPE, topo_strerror(err)); 159 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 160 } 161 switch (type) { 162 case (TOPO_LED_TYPE_SERVICE): 163 mptsas_led = MPTSAS_LEDCTL_LED_FAIL; 164 break; 165 case (TOPO_LED_TYPE_LOCATE): 166 mptsas_led = MPTSAS_LEDCTL_LED_IDENT; 167 break; 168 case (TOPO_LED_TYPE_OK2RM): 169 mptsas_led = MPTSAS_LEDCTL_LED_OK2RM; 170 break; 171 default: 172 topo_mod_dprintf(mod, "%s: Invalid LED type: 0x%x\n", __func__, 173 type); 174 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 175 } 176 if (topo_prop_get_string(pnode, TOPO_PGROUP_BINDING, 177 TOPO_BINDING_DEVCTL, &devctl, &err) != 0 || 178 topo_prop_get_uint32(pnode, TOPO_PGROUP_BINDING, 179 TOPO_BINDING_ENCLOSURE, &enclosure, &err) != 0 || 180 topo_prop_get_uint32(pnode, TOPO_PGROUP_BINDING, 181 TOPO_BINDING_SLOT, &slot, &err) != 0) { 182 topo_mod_dprintf(mod, "%s: Facility was missing mpt_sas binding" 183 " properties\n", __func__); 184 ret = topo_mod_seterrno(mod, EMOD_NVL_INVAL); 185 goto out; 186 } 187 188 if ((nvlist_lookup_nvlist(in, TOPO_PROP_PARGS, &pargs) == 0) && 189 nvlist_exists(pargs, TOPO_PROP_VAL_VAL)) { 190 /* 191 * Set the LED mode 192 */ 193 set = B_TRUE; 194 if ((ret = nvlist_lookup_uint32(pargs, TOPO_PROP_VAL_VAL, 195 &ledmode)) != 0) { 196 topo_mod_dprintf(mod, "%s: Failed to lookup %s nvpair " 197 "(%s)\n", __func__, TOPO_PROP_VAL_VAL, 198 strerror(ret)); 199 ret = topo_mod_seterrno(mod, EMOD_NVL_INVAL); 200 goto out; 201 } 202 topo_mod_dprintf(mod, "%s: Setting LED mode to %s\n", __func__, 203 ledmode ? "ON" : "OFF"); 204 } else { 205 /* 206 * Get the LED mode 207 */ 208 set = B_FALSE; 209 topo_mod_dprintf(mod, "%s: Getting LED mode\n", __func__); 210 } 211 212 /* 213 * devctl is a (potentially) pipe-separated list of different device 214 * paths to try. 215 */ 216 if ((elem = topo_mod_strsplit(mod, devctl, "|", &lastp)) == NULL) { 217 topo_mod_dprintf(mod, "%s: could not parse devctl list", 218 __func__); 219 ret = topo_mod_seterrno(mod, EMOD_UNKNOWN); 220 goto out; 221 } 222 done = B_FALSE; 223 do { 224 topo_mod_dprintf(mod, "%s: trying mpt_sas instance at %s\n", 225 __func__, elem); 226 227 ret = do_led_control(mod, elem, enclosure, slot, 228 mptsas_led, &ledmode, set); 229 230 /* 231 * Only try further devctl paths from the list if this one 232 * was not found: 233 */ 234 if (ret == 0 || errno != ENOENT) { 235 done = B_TRUE; 236 } else { 237 topo_mod_dprintf(mod, "%s: instance not found\n", 238 __func__); 239 } 240 241 topo_mod_strfree(mod, elem); 242 243 } while (!done && (elem = topo_mod_strsplit(mod, NULL, "|", 244 &lastp)) != NULL); 245 246 if (ret != 0) { 247 topo_mod_dprintf(mod, "%s: do_led_control failed", __func__); 248 ret = topo_mod_seterrno(mod, EMOD_UNKNOWN); 249 goto out; 250 } 251 252 if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 || 253 nvlist_add_string(nvl, TOPO_PROP_VAL_NAME, TOPO_LED_MODE) != 0 || 254 nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_UINT32) != 0 || 255 nvlist_add_uint32(nvl, TOPO_PROP_VAL_VAL, ledmode) != 0) { 256 topo_mod_dprintf(mod, "%s: Failed to allocate 'out' nvlist\n", 257 __func__); 258 nvlist_free(nvl); 259 ret = topo_mod_seterrno(mod, EMOD_NOMEM); 260 goto out; 261 } 262 *nvout = nvl; 263 264 out: 265 if (driver != NULL) 266 topo_mod_strfree(mod, driver); 267 if (devctl != NULL) 268 topo_mod_strfree(mod, devctl); 269 return (ret); 270 } 271 272 /*ARGSUSED*/ 273 static int 274 fac_prov_mptsas_enum(topo_mod_t *mod, tnode_t *rnode, const char *name, 275 topo_instance_t min, topo_instance_t max, void *arg, void *unused) 276 { 277 if (topo_node_flags(rnode) == TOPO_NODE_FACILITY) { 278 if (topo_method_register(mod, rnode, mptsas_fac_methods) != 0) { 279 topo_mod_dprintf(mod, "%s: topo_method_register() " 280 "failed: %s", __func__, topo_mod_errmsg(mod)); 281 return (-1); 282 } 283 return (0); 284 } 285 286 topo_mod_dprintf(mod, "%s: unexpected node flags %x", __func__, 287 topo_node_flags(rnode)); 288 return (-1); 289 }