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
di_walk(di_node_t node,di_minor_t minor,void * arg)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 *
usmp_open(const void * target)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
usmp_close(void * private)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
usmp_exec(void * private,smp_action_t * ap)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
usmp_target_name(void * private,char * buf,size_t len)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
usmp_target_addr(void * private)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
_smp_init(smp_engine_t * ep)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