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 /* 23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 #include <sys/types.h> 27 #include <sys/stat.h> 28 #include <sys/scsi/scsi_address.h> 29 #include <sys/scsi/impl/usmp.h> 30 #include <sys/libdevid.h> 31 32 #include <unistd.h> 33 #include <fcntl.h> 34 #include <errno.h> 35 #include <string.h> 36 #include <strings.h> 37 #include <stdio.h> 38 #include <limits.h> 39 40 #include <scsi/libsmp.h> 41 #include <scsi/libsmp_plugin.h> 42 43 #include <libdevinfo.h> 44 45 struct usmp_dev { 46 int ud_fd; 47 char *ud_dev; 48 uint64_t ud_addr; 49 }; 50 51 struct di_walk_arg { 52 dev_t dev; 53 uint64_t addr; 54 }; 55 56 static int 57 di_walk(di_node_t node, di_minor_t minor, void *arg) 58 { 59 struct di_walk_arg *wp = arg; 60 char *wwn; 61 62 if (di_minor_spectype(minor) != S_IFCHR) 63 return (DI_WALK_CONTINUE); 64 65 if (di_minor_devt(minor) == wp->dev) { 66 if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, 67 SCSI_ADDR_PROP_TARGET_PORT, &wwn) != 1 && 68 di_prop_lookup_strings(DDI_DEV_T_ANY, node, 69 "smp-wwn", &wwn) != 1) 70 return (DI_WALK_CONTINUE); 71 72 if (scsi_wwnstr_to_wwn(wwn, &wp->addr) != DDI_SUCCESS) 73 return (DI_WALK_CONTINUE); 74 75 return (DI_WALK_TERMINATE); 76 } 77 78 return (DI_WALK_CONTINUE); 79 } 80 81 static void * 82 usmp_open(const void *target) 83 { 84 struct usmp_dev *dp; 85 const char *target_name = (const char *)target; 86 87 struct stat64 st; 88 di_node_t root, smp; 89 struct di_walk_arg walk; 90 91 if ((dp = smp_zalloc(sizeof (struct usmp_dev))) == NULL) 92 return (NULL); 93 94 if ((dp->ud_dev = smp_strdup(target_name)) == NULL) { 95 smp_free(dp); 96 return (NULL); 97 } 98 99 if ((dp->ud_fd = open(target_name, O_RDONLY)) < 0) { 100 (void) smp_error(ESMP_BADTARGET, 101 "failed to open %s for reading: %s", 102 target_name, strerror(errno)); 103 smp_free(dp->ud_dev); 104 smp_free(dp); 105 return (NULL); 106 } 107 108 if (fstat64(dp->ud_fd, &st) != 0) { 109 (void) smp_error(ESMP_BADTARGET, 110 "failed to stat %s: %s", target_name, strerror(errno)); 111 (void) close(dp->ud_fd); 112 smp_free(dp->ud_dev); 113 smp_free(dp); 114 return (NULL); 115 } 116 117 if ((root = di_init("/", DINFOCACHE)) != DI_NODE_NIL) { 118 for (smp = di_drv_first_node("smp", root); smp != DI_NODE_NIL; 119 smp = di_drv_next_node(smp)) { 120 bzero(&walk, sizeof (walk)); 121 walk.dev = st.st_rdev; 122 (void) di_walk_minor(smp, NULL, 0, &walk, di_walk); 123 if (walk.addr != 0) { 124 dp->ud_addr = walk.addr; 125 break; 126 } 127 } 128 di_fini(root); 129 } 130 131 return (dp); 132 } 133 134 static void 135 usmp_close(void *private) 136 { 137 struct usmp_dev *dp = (struct usmp_dev *)private; 138 139 if (dp == NULL) 140 return; 141 142 if (dp->ud_fd > 0) 143 (void) close(dp->ud_fd); 144 145 smp_free(dp->ud_dev); 146 smp_free(dp); 147 } 148 149 static int 150 usmp_exec(void *private, smp_action_t *ap) 151 { 152 struct usmp_dev *dp = (struct usmp_dev *)private; 153 struct usmp_cmd cmd; 154 void *req, *resp; 155 size_t reqlen, resplen; 156 157 bzero(&cmd, sizeof (cmd)); 158 159 smp_action_get_request_frame(ap, &req, &reqlen); 160 smp_action_get_response_frame(ap, &resp, &resplen); 161 162 ASSERT(req != NULL); 163 ASSERT(resp != NULL); 164 ASSERT(reqlen != 0); 165 ASSERT(resplen != 0); 166 167 cmd.usmp_req = req; 168 cmd.usmp_reqsize = reqlen; 169 cmd.usmp_rsp = resp; 170 cmd.usmp_rspsize = resplen; 171 cmd.usmp_timeout = (int)smp_action_get_timeout(ap); 172 173 if (ioctl(dp->ud_fd, USMPFUNC, &cmd) < 0) { 174 ASSERT(errno != EFAULT); 175 switch (errno) { 176 case EINVAL: 177 return (smp_error(ESMP_BADFUNC, "internal usmp error")); 178 case EPERM: 179 return (smp_error(ESMP_PERM, 180 "insufficient privileges")); 181 case EIO: 182 return (smp_error(ESMP_IO, "I/O error")); 183 default: 184 return (smp_error(ESMP_SYS, "usmp ioctl failed: %s", 185 strerror(errno))); 186 } 187 } 188 189 /* 190 * There is no way to determine the amount of data actually transferred 191 * so we will just place the upper bound at the allocated size. 192 */ 193 smp_action_set_response_len(ap, resplen); 194 195 return (0); 196 } 197 198 static void 199 usmp_target_name(void *private, char *buf, size_t len) 200 { 201 struct usmp_dev *dp = (struct usmp_dev *)private; 202 203 (void) strlcpy(buf, dp->ud_dev, len); 204 } 205 206 static uint64_t 207 usmp_target_addr(void *private) 208 { 209 struct usmp_dev *dp = (struct usmp_dev *)private; 210 211 return (dp->ud_addr); 212 } 213 214 static const smp_engine_ops_t usmp_ops = { 215 .seo_open = usmp_open, 216 .seo_close = usmp_close, 217 .seo_exec = usmp_exec, 218 .seo_target_name = usmp_target_name, 219 .seo_target_addr = usmp_target_addr 220 }; 221 222 int 223 _smp_init(smp_engine_t *ep) 224 { 225 smp_engine_config_t config = { 226 .sec_name = "usmp", 227 .sec_ops = &usmp_ops 228 }; 229 230 return (smp_engine_register(ep, LIBSMP_ENGINE_VERSION, &config)); 231 } 232