xref: /titanic_51/usr/src/uts/common/io/scsi/adapters/iscsi/iscsi_doorclt.c (revision 59927d313a821b7f3822314ed16fc0a44c128431)
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 2000 by Cisco Systems, Inc.  All rights reserved.
24  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
26  * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
27  */
28 
29 /*
30  * iSCSI Software Initiator
31  */
32 
33 #include <sys/types.h>
34 #include <sys/errno.h>
35 #include <sys/conf.h>
36 #include <sys/cmn_err.h>
37 #include <sys/stat.h>
38 #include <sys/pathname.h>
39 #include <sys/door.h>
40 #include <sys/kmem.h>
41 #include <sys/socket.h>
42 #include <sys/fs/snode.h>
43 #include <netinet/in.h>
44 
45 #include <sys/scsi/adapters/iscsi_door.h>
46 #include "iscsi.h"
47 
48 #define	ISCSI_DOOR_MAX_SEMA_VALUE	16
49 
50 static boolean_t	iscsi_door_init = B_FALSE;
51 static ksema_t		iscsi_door_sema;
52 static krwlock_t	iscsi_door_lock;
53 static door_handle_t	iscsi_door_handle;
54 
55 typedef struct _mybuffer {
56 	size_t		signature;
57 	size_t		size;
58 } mybuffer_t;
59 
60 /*
61  * iscsi_door_ini
62  *
63  * This function initializes the variables needed to handle the door upcall.
64  */
65 boolean_t
66 iscsi_door_ini(void)
67 {
68 	ASSERT(!iscsi_door_init);
69 	if (!iscsi_door_init) {
70 		rw_init(
71 		    &iscsi_door_lock,
72 		    NULL,
73 		    RW_DRIVER,
74 		    NULL);
75 
76 		sema_init(
77 		    &iscsi_door_sema,
78 		    ISCSI_DOOR_MAX_SEMA_VALUE,
79 		    NULL,
80 		    SEMA_DRIVER,
81 		    NULL);
82 
83 		iscsi_door_handle = NULL;
84 		iscsi_door_init = B_TRUE;
85 		return (B_TRUE);
86 	}
87 	return (B_FALSE);
88 }
89 
90 /*
91  * iscsi_door_term
92  *
93  * This function releases the resources allocated to handle the door
94  * upcall.  It disconnects from the door if currently connected.
95  */
96 boolean_t
97 iscsi_door_term(void)
98 {
99 	ASSERT(iscsi_door_init);
100 	if (iscsi_door_init) {
101 		iscsi_door_init = B_FALSE;
102 		iscsi_door_unbind();
103 		rw_destroy(&iscsi_door_lock);
104 		sema_destroy(&iscsi_door_sema);
105 		return (B_TRUE);
106 	}
107 	return (B_FALSE);
108 }
109 
110 /*
111  * iscsi_door_bind
112  *
113  * This function tries to connect the iscsi_door.  If it succeeds
114  * it keeps the vnode.
115  */
116 boolean_t
117 iscsi_door_bind(
118 	int		did
119 )
120 {
121 	door_handle_t	new_handle;
122 
123 	new_handle = door_ki_lookup(did);
124 	if (new_handle == NULL) {
125 		/* The lookup failed. */
126 		return (B_FALSE);
127 	}
128 
129 	/* The new handle is stored.  If we had one, it is released. */
130 	rw_enter(&iscsi_door_lock, RW_WRITER);
131 	if (iscsi_door_handle != NULL) {
132 		door_ki_rele(iscsi_door_handle);
133 	}
134 	iscsi_door_handle = new_handle;
135 	rw_exit(&iscsi_door_lock);
136 
137 	return (B_TRUE);
138 }
139 
140 /*
141  * iscsi_door_unbind
142  *
143  * This function releases the current door handle.
144  */
145 void
146 iscsi_door_unbind(void)
147 {
148 	rw_enter(&iscsi_door_lock, RW_WRITER);
149 	if (iscsi_door_handle != NULL) {
150 		door_ki_rele(iscsi_door_handle);
151 		iscsi_door_handle = NULL;
152 	}
153 	rw_exit(&iscsi_door_lock);
154 }
155 
156 /*
157  * iscsi_door_upcall
158  *
159  * This function tries to call the iscsi_door.
160  */
161 static
162 boolean_t
163 iscsi_door_upcall(door_arg_t *arg)
164 {
165 	int	error;
166 
167 	/*
168 	 * This semaphore limits the number of simultaneous calls
169 	 * to the door.
170 	 */
171 	sema_p(&iscsi_door_sema);
172 	/*
173 	 * The mutex protecting the iscsi_door_handle is entered.
174 	 */
175 	rw_enter(&iscsi_door_lock, RW_READER);
176 
177 	if (iscsi_door_handle == NULL) {
178 		/* There's no door handle. */
179 		rw_exit(&iscsi_door_lock);
180 		sema_v(&iscsi_door_sema);
181 		return (B_FALSE);
182 	}
183 	error = door_ki_upcall(iscsi_door_handle, arg);
184 
185 	rw_exit(&iscsi_door_lock);
186 	sema_v(&iscsi_door_sema);
187 
188 	if (error != 0) {
189 		return (B_FALSE);
190 	} else {
191 		return (B_TRUE);
192 	}
193 }
194 
195 /*
196  * kfreehostent
197  *
198  * This function frees the memory returned by kgetipnodebyname.
199  */
200 void
201 kfreehostent(
202 	struct hostent		*hptr
203 )
204 {
205 	mybuffer_t		*buffer;
206 
207 	ASSERT(hptr != NULL);
208 	if (hptr) {
209 		buffer = (mybuffer_t *)((char *)hptr - sizeof (mybuffer_t));
210 		ASSERT(buffer->signature == ISCSI_DOOR_REQ_SIGNATURE);
211 		if (buffer->signature == ISCSI_DOOR_REQ_SIGNATURE) {
212 			kmem_free((void *)buffer, buffer->size);
213 			return;
214 		}
215 	}
216 	/* A message should be logged here. */
217 }
218 
219 /*
220  * kgetipnodebyname
221  *
222  * This function builds a request that will be sent to the iscsi_door.
223  * The iSCSI door after receiving the request calls getipnodebyaddr().
224  * for more information on the input, output parameter and return value,
225  * consult the man page for getipnodebyname().
226  *
227  * Before calling the iscsi door this function tries to do the conversion
228  * locally.  If a name resolution is needed the iscsi door is called.
229  *
230  * There's some limitations to the information returned by this function.
231  * Only one address of the address list returned by getipnodebyname() is
232  * returned.  The other parameters of the structure should be ignored.
233  */
234 struct hostent *
235 kgetipnodebyname(
236 	const char	*name,
237 	int		af,
238 	int		flags,
239 	int		*error_num
240 )
241 {
242 	door_arg_t		arg;
243 	mybuffer_t		*buffer;
244 	size_t			msg_size = ISCSI_DOOR_MAX_DATA_SIZE;
245 	size_t			hostent_size = ISCSI_DOOR_MAX_DATA_SIZE;
246 	size_t			buffer_size;
247 	getipnodebyname_req_t	*req;
248 	getipnodebyname_cnf_t	*cnf;
249 	struct hostent		*hptr;
250 
251 
252 	buffer_size = msg_size + hostent_size + sizeof (mybuffer_t);
253 	buffer = (mybuffer_t *)kmem_zalloc(buffer_size, KM_SLEEP);
254 
255 	if (buffer) {
256 
257 		/*
258 		 * The buffer was successfully allocated.
259 		 *
260 		 *	  Buffer
261 		 *
262 		 * +--------------------+ <--- buffer
263 		 * |	mybuffer_t	|
264 		 * +--------------------+ <--- hptr
265 		 * |			|
266 		 * |			|
267 		 * |	hostent_size	|
268 		 * |			|
269 		 * |			|
270 		 * |			|
271 		 * +--------------------+ <--- req, cnf
272 		 * |			|
273 		 * |			|
274 		 * |			|
275 		 * |	msg_size	|
276 		 * |			|
277 		 * |			|
278 		 * |			|
279 		 * +--------------------+
280 		 */
281 		buffer->signature = ISCSI_DOOR_REQ_SIGNATURE;
282 		buffer->size = buffer_size;
283 
284 		hptr = (struct hostent *)((char *)buffer + sizeof (mybuffer_t));
285 		req = (getipnodebyname_req_t *)((char *)hptr + hostent_size);
286 		cnf = (getipnodebyname_cnf_t *)((char *)hptr + hostent_size);
287 
288 		hostent_size -= sizeof (struct hostent);
289 
290 		/*
291 		 * We try first locally.  If the conversion cannot be done
292 		 * by inet_pton the door is called.
293 		 * The cnf address is used as output buffer.
294 		 * inet_pton returns '1' if the conversion was successful.
295 		 */
296 		switch (af) {
297 		case AF_INET:
298 			hptr->h_length = sizeof (struct in_addr);
299 			break;
300 		case AF_INET6:
301 			hptr->h_length = sizeof (struct in6_addr);
302 			break;
303 		default:
304 			kfreehostent(hptr);
305 			*error_num = NO_RECOVERY;
306 			return (NULL);
307 		}
308 		if ((msg_size < hptr->h_length) ||
309 		    (hostent_size < sizeof (char *))) {
310 			kfreehostent(hptr);
311 			*error_num = NO_RECOVERY;
312 			return (NULL);
313 		}
314 		if (inet_pton(af, (char *)name, cnf) == 1) {
315 			/*
316 			 * inet_pton converted the string successfully.
317 			 */
318 			hptr->h_addrtype = af;
319 			hptr->h_addr_list = (char **)((char *)hptr +
320 			    sizeof (struct hostent));
321 			*hptr->h_addr_list = (char *)cnf;
322 			return (hptr);
323 		}
324 
325 		/*
326 		 * The name couldn't ne converted by inet_pton.  The door is
327 		 * called.
328 		 */
329 
330 		/* Header initialization. */
331 		req->hdr.signature = ISCSI_DOOR_REQ_SIGNATURE;
332 		req->hdr.version = ISCSI_DOOR_REQ_VERSION_1;
333 		req->hdr.opcode = ISCSI_DOOR_GETIPNODEBYNAME_REQ;
334 
335 		/* Body initialization. */
336 		req->name_length = strlen(name);
337 		if (req->name_length >
338 		    (msg_size - sizeof (getipnodebyname_req_t) - 1)) {
339 			kfreehostent(hptr);
340 			*error_num = NO_RECOVERY;
341 			return (NULL);
342 		}
343 
344 		req->name_offset = sizeof (getipnodebyname_req_t);
345 		req->af = af;
346 		req->flags = flags;
347 		bcopy(
348 		    name,
349 		    ((char *)req + req->name_offset),
350 		    req->name_length);
351 
352 		/* Door argument initialization. */
353 		arg.data_ptr = (char *)req;
354 		arg.data_size = msg_size;
355 		arg.desc_num = 0;
356 		arg.desc_ptr = NULL;
357 		arg.rbuf = (char *)cnf;
358 		arg.rsize = msg_size;
359 
360 		if (iscsi_door_upcall(&arg) == B_FALSE) {
361 			/* The door call failed */
362 			kfreehostent(hptr);
363 			*error_num = NO_RECOVERY;
364 			return (NULL);
365 		}
366 
367 		/*
368 		 * The door call itself was successful.  The value returned
369 		 * in arg.rbuf should be cnf, but we never know.
370 		 */
371 		cnf = (getipnodebyname_cnf_t *)arg.rbuf;
372 
373 		if ((cnf == NULL) ||
374 		    (arg.rsize < sizeof (getipnodebyname_cnf_t)) ||
375 		    (cnf->hdr.signature != ISCSI_DOOR_REQ_SIGNATURE) ||
376 		    (cnf->hdr.version != ISCSI_DOOR_REQ_VERSION_1) ||
377 		    (cnf->hdr.opcode != ISCSI_DOOR_GETIPNODEBYNAME_CNF) ||
378 		    ((cnf->hdr.status != ISCSI_DOOR_STATUS_SUCCESS) &&
379 		    (cnf->hdr.status != ISCSI_DOOR_STATUS_MORE))) {
380 			/* The door didn't like the request */
381 			kfreehostent(hptr);
382 			*error_num = NO_RECOVERY;
383 			return (NULL);
384 		}
385 
386 		if (cnf->h_addr_list_length == 0) {
387 			kfreehostent(hptr);
388 			*error_num = HOST_NOT_FOUND;
389 			return (NULL);
390 		}
391 
392 		hptr->h_addrtype = cnf->h_addrtype;
393 		hptr->h_length = cnf->h_addrlen;
394 		hptr->h_addr_list = (char **)((char *)hptr +
395 		    sizeof (struct hostent));
396 		*hptr->h_addr_list = ((char *)cnf + cnf->h_addr_list_offset);
397 		return (hptr);
398 	} else {
399 		*error_num = NO_RECOVERY;
400 		return (NULL);
401 	}
402 }
403