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