]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/iscsi/icl_soft_proxy.c
sys/{x86,amd64}: remove one of doubled ;s
[FreeBSD/FreeBSD.git] / sys / dev / iscsi / icl_soft_proxy.c
1 /*-
2  * Copyright (c) 2012 The FreeBSD Foundation
3  * All rights reserved.
4  *
5  * This software was developed by Edward Tomasz Napierala under sponsorship
6  * from the FreeBSD Foundation.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  */
30 /*-
31  * Copyright (c) 1982, 1986, 1989, 1990, 1993
32  *      The Regents of the University of California.  All rights reserved.
33  *
34  * sendfile(2) and related extensions:
35  * Copyright (c) 1998, David Greenman. All rights reserved.
36  *
37  * Redistribution and use in source and binary forms, with or without
38  * modification, are permitted provided that the following conditions
39  * are met:
40  * 1. Redistributions of source code must retain the above copyright
41  *    notice, this list of conditions and the following disclaimer.
42  * 2. Redistributions in binary form must reproduce the above copyright
43  *    notice, this list of conditions and the following disclaimer in the
44  *    documentation and/or other materials provided with the distribution.
45  * 3. Neither the name of the University nor the names of its contributors
46  *    may be used to endorse or promote products derived from this software
47  *    without specific prior written permission.
48  *
49  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
50  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
51  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
52  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
53  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
54  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
55  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
56  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
57  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
58  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
59  * SUCH DAMAGE.
60  *
61  *      @(#)uipc_syscalls.c     8.4 (Berkeley) 2/21/94
62  */
63
64 /*
65  * iSCSI Common Layer, kernel proxy part.
66  */
67
68 #ifdef ICL_KERNEL_PROXY
69
70 #include <sys/cdefs.h>
71 __FBSDID("$FreeBSD$");
72
73 #include <sys/param.h>
74 #include <sys/capsicum.h>
75 #include <sys/condvar.h>
76 #include <sys/conf.h>
77 #include <sys/lock.h>
78 #include <sys/kernel.h>
79 #include <sys/kthread.h>
80 #include <sys/malloc.h>
81 #include <sys/mutex.h>
82 #include <sys/proc.h>
83 #include <sys/socket.h>
84 #include <sys/socketvar.h>
85 #include <sys/sx.h>
86 #include <sys/systm.h>
87 #include <netinet/in.h>
88 #include <netinet/tcp.h>
89
90 #include <dev/iscsi/icl.h>
91
92 struct icl_listen_sock {
93         TAILQ_ENTRY(icl_listen_sock)    ils_next;
94         struct icl_listen               *ils_listen;
95         struct socket                   *ils_socket;
96         bool                            ils_running;
97         int                             ils_id;
98 };
99
100 struct icl_listen       {
101         TAILQ_HEAD(, icl_listen_sock)   il_sockets;
102         struct sx                       il_lock;
103         void                            (*il_accept)(struct socket *,
104                                             struct sockaddr *, int);
105 };
106
107 static MALLOC_DEFINE(M_ICL_PROXY, "ICL_PROXY", "iSCSI common layer proxy");
108
109 int
110 icl_soft_proxy_connect(struct icl_conn *ic, int domain, int socktype,
111     int protocol, struct sockaddr *from_sa, struct sockaddr *to_sa)
112 {
113         struct socket *so;
114         int error;
115         int interrupted = 0;
116
117         error = socreate(domain, &so, socktype, protocol,
118             curthread->td_ucred, curthread);
119         if (error != 0)
120                 return (error);
121
122         if (from_sa != NULL) {
123                 error = sobind(so, from_sa, curthread);
124                 if (error != 0) {
125                         soclose(so);
126                         return (error);
127                 }
128         }
129
130         error = soconnect(so, to_sa, curthread);
131         if (error != 0) {
132                 soclose(so);
133                 return (error);
134         }
135
136         SOCK_LOCK(so);
137         while ((so->so_state & SS_ISCONNECTING) && so->so_error == 0) {
138                 error = msleep(&so->so_timeo, SOCK_MTX(so), PSOCK | PCATCH,
139                     "icl_connect", 0);
140                 if (error) {
141                         if (error == EINTR || error == ERESTART)
142                                 interrupted = 1;
143                         break;
144                 }
145         }
146         if (error == 0) {
147                 error = so->so_error;
148                 so->so_error = 0;
149         }
150         SOCK_UNLOCK(so);
151
152         if (error != 0) {
153                 soclose(so);
154                 return (error);
155         }
156
157         error = icl_soft_handoff_sock(ic, so);
158         if (error != 0)
159                 soclose(so);
160
161         return (error);
162 }
163
164 struct icl_listen *
165 icl_listen_new(void (*accept_cb)(struct socket *, struct sockaddr *, int))
166 {
167         struct icl_listen *il;
168
169         il = malloc(sizeof(*il), M_ICL_PROXY, M_ZERO | M_WAITOK);
170         TAILQ_INIT(&il->il_sockets);
171         sx_init(&il->il_lock, "icl_listen");
172         il->il_accept = accept_cb;
173
174         return (il);
175 }
176
177 void
178 icl_listen_free(struct icl_listen *il)
179 {
180         struct icl_listen_sock *ils;
181
182         sx_xlock(&il->il_lock);
183         while (!TAILQ_EMPTY(&il->il_sockets)) {
184                 ils = TAILQ_FIRST(&il->il_sockets);
185                 while (ils->ils_running) {
186                         ICL_DEBUG("waiting for accept thread to terminate");
187                         sx_xunlock(&il->il_lock);
188                         SOLISTEN_LOCK(ils->ils_socket);
189                         ils->ils_socket->so_error = ENOTCONN;
190                         SOLISTEN_UNLOCK(ils->ils_socket);
191                         wakeup(&ils->ils_socket->so_timeo);
192                         pause("icl_unlisten", 1 * hz);
193                         sx_xlock(&il->il_lock);
194                 }
195         
196                 TAILQ_REMOVE(&il->il_sockets, ils, ils_next);
197                 soclose(ils->ils_socket);
198                 free(ils, M_ICL_PROXY);
199         }
200         sx_xunlock(&il->il_lock);
201
202         free(il, M_ICL_PROXY);
203 }
204
205 /*
206  * XXX: Doing accept in a separate thread in each socket might not be the
207  * best way to do stuff, but it's pretty clean and debuggable - and you
208  * probably won't have hundreds of listening sockets anyway.
209  */
210 static void
211 icl_accept_thread(void *arg)
212 {
213         struct icl_listen_sock *ils;
214         struct socket *head, *so;
215         struct sockaddr *sa;
216         int error;
217
218         ils = arg;
219         head = ils->ils_socket;
220
221         ils->ils_running = true;
222
223         for (;;) {
224                 SOLISTEN_LOCK(head);
225                 error = solisten_dequeue(head, &so, 0);
226                 if (error == ENOTCONN) {
227                         /*
228                          * XXXGL: ENOTCONN is our mark from icl_listen_free().
229                          * Neither socket code, nor msleep(9) may return it.
230                          */
231                         ICL_DEBUG("terminating");
232                         ils->ils_running = false;
233                         kthread_exit();
234                         return;
235                 }
236                 if (error) {
237                         ICL_WARN("solisten_dequeue error %d", error);
238                         continue;
239                 }
240
241                 sa = NULL;
242                 error = soaccept(so, &sa);
243                 if (error != 0) {
244                         ICL_WARN("soaccept error %d", error);
245                         if (sa != NULL)
246                                 free(sa, M_SONAME);
247                         soclose(so);
248                         continue;
249                 }
250
251                 (ils->ils_listen->il_accept)(so, sa, ils->ils_id);
252         }
253 }
254
255 static int
256 icl_listen_add_tcp(struct icl_listen *il, int domain, int socktype,
257     int protocol, struct sockaddr *sa, int portal_id)
258 {
259         struct icl_listen_sock *ils;
260         struct socket *so;
261         struct sockopt sopt;
262         int error, one = 1;
263
264         error = socreate(domain, &so, socktype, protocol,
265             curthread->td_ucred, curthread);
266         if (error != 0) {
267                 ICL_WARN("socreate failed with error %d", error);
268                 return (error);
269         }
270
271         sopt.sopt_dir = SOPT_SET;
272         sopt.sopt_level = SOL_SOCKET;
273         sopt.sopt_name = SO_REUSEADDR;
274         sopt.sopt_val = &one;
275         sopt.sopt_valsize = sizeof(one);
276         sopt.sopt_td = NULL;
277         error = sosetopt(so, &sopt);
278         if (error != 0) {
279                 ICL_WARN("failed to set SO_REUSEADDR with error %d", error);
280                 soclose(so);
281                 return (error);
282         }
283
284         error = sobind(so, sa, curthread);
285         if (error != 0) {
286                 ICL_WARN("sobind failed with error %d", error);
287                 soclose(so);
288                 return (error);
289         }
290
291         error = solisten(so, -1, curthread);
292         if (error != 0) {
293                 ICL_WARN("solisten failed with error %d", error);
294                 soclose(so);
295                 return (error);
296         }
297
298         ils = malloc(sizeof(*ils), M_ICL_PROXY, M_ZERO | M_WAITOK);
299         ils->ils_listen = il;
300         ils->ils_socket = so;
301         ils->ils_id = portal_id;
302
303         error = kthread_add(icl_accept_thread, ils, NULL, NULL, 0, 0, "iclacc");
304         if (error != 0) {
305                 ICL_WARN("kthread_add failed with error %d", error);
306                 soclose(so);
307                 free(ils, M_ICL_PROXY);
308
309                 return (error);
310         }
311
312         sx_xlock(&il->il_lock);
313         TAILQ_INSERT_TAIL(&il->il_sockets, ils, ils_next);
314         sx_xunlock(&il->il_lock);
315
316         return (0);
317 }
318
319 int
320 icl_listen_add(struct icl_listen *il, bool rdma, int domain, int socktype,
321     int protocol, struct sockaddr *sa, int portal_id)
322 {
323
324         if (rdma) {
325                 ICL_DEBUG("RDMA not supported");
326                 return (EOPNOTSUPP);
327         }
328
329
330         return (icl_listen_add_tcp(il, domain, socktype, protocol, sa,
331             portal_id));
332 }
333
334 int
335 icl_listen_remove(struct icl_listen *il, struct sockaddr *sa)
336 {
337
338         /*
339          * XXX
340          */
341
342         return (EOPNOTSUPP);
343 }
344
345 #endif /* ICL_KERNEL_PROXY */