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 * Copyright (c) 2013, Joyent, Inc. All rights reserved. 13 */ 14 15 #include <stdlib.h> 16 #include <sys/types.h> 17 #include <sys/stat.h> 18 #include <fcntl.h> 19 #include <unistd.h> 20 #include <stropts.h> 21 #include <string.h> 22 #include <strings.h> 23 24 #include <fm/topo_mod.h> 25 #include <fm/topo_list.h> 26 27 #include <sys/scsi/adapters/mpt_sas/mpi/mpi2_type.h> 28 #include <sys/scsi/adapters/mpt_sas/mpi/mpi2.h> 29 #include <sys/scsi/adapters/mpt_sas/mpi/mpi2_init.h> 30 #include <sys/scsi/adapters/mpt_sas/mptsas_ioctl.h> 31 32 #include "disk.h" 33 #include "disk_drivers.h" 34 35 /* 36 * Request the SAS address of the disk (if any) attached to this mpt_sas 37 * instance at (Enclosure Number, Slot Number). The function returns 38 * -1 on error and sets errno to ENOENT _only_ if the /devices node 39 * (*devctl) does not exist. 40 */ 41 static int 42 get_sas_address(topo_mod_t *mod, char *devctl, uint32_t enclosure, 43 uint32_t slot, char **sas_address) 44 { 45 int ret = -1, en = ENXIO; 46 int fd, i; 47 mptsas_get_disk_info_t gdi; 48 mptsas_disk_info_t *di; 49 size_t disz; 50 51 bzero(&gdi, sizeof (gdi)); 52 53 if ((fd = open(devctl, O_RDWR)) == -1) { 54 en = errno; 55 topo_mod_dprintf(mod, "could not open '%s' for ioctl: %s\n", 56 devctl, strerror(errno)); 57 errno = en; 58 return (-1); 59 } 60 61 if (ioctl(fd, MPTIOCTL_GET_DISK_INFO, &gdi) == -1) { 62 if (errno != ENOENT) 63 en = errno; 64 topo_mod_dprintf(mod, "ioctl 1 on '%s' failed: %s\n", devctl, 65 strerror(errno)); 66 goto out; 67 } 68 69 gdi.DiskInfoArraySize = disz = sizeof (mptsas_disk_info_t) * 70 gdi.DiskCount; 71 gdi.PtrDiskInfoArray = di = topo_mod_alloc(mod, disz); 72 if (di == NULL) { 73 topo_mod_dprintf(mod, "memory allocation failed\n"); 74 en = ENOMEM; 75 goto out; 76 } 77 78 if (ioctl(fd, MPTIOCTL_GET_DISK_INFO, &gdi) == -1) { 79 if (errno != ENOENT) 80 en = errno; 81 topo_mod_dprintf(mod, "ioctl 2 on '%s' failed: %s\n", devctl, 82 strerror(errno)); 83 topo_mod_free(mod, di, disz); 84 goto out; 85 } 86 87 for (i = 0; i < gdi.DiskCount; i++) { 88 if (di[i].Enclosure == enclosure && di[i].Slot == slot) { 89 char sas[17]; /* 16 hex digits and NUL */ 90 (void) snprintf(sas, 17, "%llx", di[i].SasAddress); 91 topo_mod_dprintf(mod, "found mpt_sas disk (%d/%d) " 92 "with adddress %s\n", enclosure, slot, sas); 93 *sas_address = topo_mod_strdup(mod, sas); 94 en = ret = 0; 95 break; 96 } 97 } 98 99 topo_mod_free(mod, di, disz); 100 out: 101 (void) close(fd); 102 errno = en; 103 return (ret); 104 } 105 106 int 107 disk_mptsas_find_disk(topo_mod_t *mod, tnode_t *baynode, char **sas_address) 108 { 109 char *devctl = NULL; 110 uint32_t enclosure, slot; 111 int err; 112 char *elem, *lastp; 113 int ret = -1; 114 115 /* 116 * Get the required properties from the node. These come from 117 * the static XML mapping. 118 */ 119 if (topo_prop_get_string(baynode, TOPO_PGROUP_BINDING, 120 TOPO_BINDING_DEVCTL, &devctl, &err) != 0 || 121 topo_prop_get_uint32(baynode, TOPO_PGROUP_BINDING, 122 TOPO_BINDING_ENCLOSURE, &enclosure, &err) != 0 || 123 topo_prop_get_uint32(baynode, TOPO_PGROUP_BINDING, 124 TOPO_BINDING_SLOT, &slot, &err) != 0) { 125 if (devctl != NULL) 126 topo_mod_strfree(mod, devctl); 127 topo_mod_dprintf(mod, "bay node was missing mpt_sas binding " 128 "properties\n"); 129 return (-1); 130 } 131 132 /* 133 * devctl is a (potentially) pipe-separated list of different device 134 * paths to try. 135 */ 136 if ((elem = topo_mod_strsplit(mod, devctl, "|", &lastp)) != NULL) { 137 boolean_t done = B_FALSE; 138 do { 139 topo_mod_dprintf(mod, "trying mpt_sas instance at %s\n", 140 elem); 141 142 ret = get_sas_address(mod, elem, enclosure, 143 slot, sas_address); 144 145 /* 146 * Only try further devctl paths from the list if this 147 * one was not found: 148 */ 149 if (ret == 0 || errno != ENOENT) { 150 done = B_TRUE; 151 } else { 152 topo_mod_dprintf(mod, "instance not found\n"); 153 } 154 155 topo_mod_strfree(mod, elem); 156 157 } while (!done && (elem = topo_mod_strsplit(mod, NULL, "|", 158 &lastp)) != NULL); 159 } 160 161 topo_mod_strfree(mod, devctl); 162 return (ret); 163 }