1255570Strasz/*-
2255570Strasz * Copyright (c) 2012 The FreeBSD Foundation
3255570Strasz * All rights reserved.
4255570Strasz *
5255570Strasz * This software was developed by Edward Tomasz Napierala under sponsorship
6255570Strasz * from the FreeBSD Foundation.
7255570Strasz *
8255570Strasz * Redistribution and use in source and binary forms, with or without
9255570Strasz * modification, are permitted provided that the following conditions
10255570Strasz * are met:
11255570Strasz * 1. Redistributions of source code must retain the above copyright
12255570Strasz *    notice, this list of conditions and the following disclaimer.
13255570Strasz * 2. Redistributions in binary form must reproduce the above copyright
14255570Strasz *    notice, this list of conditions and the following disclaimer in the
15255570Strasz *    documentation and/or other materials provided with the distribution.
16255570Strasz *
17255570Strasz * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18255570Strasz * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19255570Strasz * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20255570Strasz * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21255570Strasz * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22255570Strasz * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23255570Strasz * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24255570Strasz * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25255570Strasz * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26255570Strasz * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27255570Strasz * SUCH DAMAGE.
28255570Strasz *
29255570Strasz */
30255570Strasz/*-
31255570Strasz * Copyright (c) 1982, 1986, 1989, 1990, 1993
32255570Strasz *	The Regents of the University of California.  All rights reserved.
33255570Strasz *
34255570Strasz * sendfile(2) and related extensions:
35255570Strasz * Copyright (c) 1998, David Greenman. All rights reserved.
36255570Strasz *
37255570Strasz * Redistribution and use in source and binary forms, with or without
38255570Strasz * modification, are permitted provided that the following conditions
39255570Strasz * are met:
40255570Strasz * 1. Redistributions of source code must retain the above copyright
41255570Strasz *    notice, this list of conditions and the following disclaimer.
42255570Strasz * 2. Redistributions in binary form must reproduce the above copyright
43255570Strasz *    notice, this list of conditions and the following disclaimer in the
44255570Strasz *    documentation and/or other materials provided with the distribution.
45255570Strasz * 4. Neither the name of the University nor the names of its contributors
46255570Strasz *    may be used to endorse or promote products derived from this software
47255570Strasz *    without specific prior written permission.
48255570Strasz *
49255570Strasz * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
50255570Strasz * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
51255570Strasz * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
52255570Strasz * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
53255570Strasz * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
54255570Strasz * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
55255570Strasz * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
56255570Strasz * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
57255570Strasz * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
58255570Strasz * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
59255570Strasz * SUCH DAMAGE.
60255570Strasz *
61255570Strasz *	@(#)uipc_syscalls.c	8.4 (Berkeley) 2/21/94
62255570Strasz */
63255570Strasz
64255570Strasz/*
65255570Strasz * iSCSI Common Layer, kernel proxy part.
66255570Strasz */
67255570Strasz
68255570Strasz#ifdef ICL_KERNEL_PROXY
69255570Strasz
70270888Strasz#include <sys/cdefs.h>
71270888Strasz__FBSDID("$FreeBSD$");
72270888Strasz
73255570Strasz#include <sys/param.h>
74255570Strasz#include <sys/capability.h>
75255570Strasz#include <sys/condvar.h>
76255570Strasz#include <sys/conf.h>
77255570Strasz#include <sys/kernel.h>
78255570Strasz#include <sys/kthread.h>
79255570Strasz#include <sys/malloc.h>
80255570Strasz#include <sys/proc.h>
81255570Strasz#include <sys/socket.h>
82255570Strasz#include <sys/socketvar.h>
83255570Strasz#include <sys/sx.h>
84255570Strasz#include <sys/systm.h>
85255570Strasz#include <netinet/in.h>
86255570Strasz#include <netinet/tcp.h>
87255570Strasz#include <linux/types.h>
88255570Strasz#include <rdma/rdma_cm.h>
89255570Strasz
90270891Strasz#include <dev/iscsi/icl.h>
91255570Strasz
92255570Straszstatic int debug = 1;
93255570Strasz
94255570Strasz#define	ICL_DEBUG(X, ...)					\
95255570Strasz	if (debug > 1) {					\
96255570Strasz		printf("%s: " X "\n", __func__, ## __VA_ARGS__);\
97255570Strasz	} while (0)
98255570Strasz
99255570Strasz#define	ICL_WARN(X, ...)					\
100255570Strasz	if (debug > 0) {					\
101255570Strasz		printf("WARNING: %s: " X "\n",			\
102255570Strasz		    __func__, ## __VA_ARGS__);			\
103255570Strasz	} while (0)
104255570Strasz
105255570Straszstatic MALLOC_DEFINE(M_ICL_PROXY, "ICL_PROXY", "iSCSI common layer proxy");
106255570Strasz
107255570Strasz#ifdef ICL_RDMA
108255570Straszstatic int	icl_conn_connect_rdma(struct icl_conn *ic, int domain, int socktype,
109255570Strasz    int protocol, struct sockaddr *from_sa, struct sockaddr *to_sa);
110255570Straszstatic int	icl_listen_add_rdma(struct icl_listen *il, int domain, int socktype, int protocol,
111255570Strasz    struct sockaddr *sa);
112255570Strasz#endif /* ICL_RDMA */
113255570Strasz
114255570Straszstatic int
115255570Straszicl_conn_connect_tcp(struct icl_conn *ic, int domain, int socktype,
116255570Strasz    int protocol, struct sockaddr *from_sa, struct sockaddr *to_sa)
117255570Strasz{
118255570Strasz	struct socket *so;
119255570Strasz	int error;
120255570Strasz	int interrupted = 0;
121255570Strasz
122255570Strasz	error = socreate(domain, &so, socktype, protocol,
123255570Strasz	    curthread->td_ucred, curthread);
124255570Strasz	if (error != 0)
125255570Strasz		return (error);
126255570Strasz
127255570Strasz	if (from_sa != NULL) {
128255570Strasz		error = sobind(so, from_sa, curthread);
129255570Strasz		if (error != 0) {
130255570Strasz			soclose(so);
131255570Strasz			return (error);
132255570Strasz		}
133255570Strasz	}
134255570Strasz
135255570Strasz	error = soconnect(so, to_sa, curthread);
136255570Strasz	if (error != 0) {
137255570Strasz		soclose(so);
138255570Strasz		return (error);
139255570Strasz	}
140255570Strasz
141255570Strasz	SOCK_LOCK(so);
142255570Strasz	while ((so->so_state & SS_ISCONNECTING) && so->so_error == 0) {
143255570Strasz		error = msleep(&so->so_timeo, SOCK_MTX(so), PSOCK | PCATCH,
144255570Strasz		    "icl_connect", 0);
145255570Strasz		if (error) {
146255570Strasz			if (error == EINTR || error == ERESTART)
147255570Strasz				interrupted = 1;
148255570Strasz			break;
149255570Strasz		}
150255570Strasz	}
151255570Strasz	if (error == 0) {
152255570Strasz		error = so->so_error;
153255570Strasz		so->so_error = 0;
154255570Strasz	}
155255570Strasz	SOCK_UNLOCK(so);
156255570Strasz
157255570Strasz	if (error != 0) {
158255570Strasz		soclose(so);
159255570Strasz		return (error);
160255570Strasz	}
161255570Strasz
162255570Strasz	error = icl_conn_handoff_sock(ic, so);
163255570Strasz	if (error != 0)
164255570Strasz		soclose(so);
165255570Strasz
166255570Strasz	return (error);
167255570Strasz}
168255570Strasz
169255570Straszint
170255570Straszicl_conn_connect(struct icl_conn *ic, bool rdma, int domain, int socktype,
171255570Strasz    int protocol, struct sockaddr *from_sa, struct sockaddr *to_sa)
172255570Strasz{
173255570Strasz
174255570Strasz	if (rdma) {
175255570Strasz#ifdef ICL_RDMA
176255570Strasz		return (icl_conn_connect_rdma(ic, domain, socktype, protocol, from_sa, to_sa));
177255570Strasz#else
178255570Strasz		ICL_DEBUG("RDMA not supported");
179255570Strasz		return (EOPNOTSUPP);
180255570Strasz#endif
181255570Strasz	}
182255570Strasz
183255570Strasz	return (icl_conn_connect_tcp(ic, domain, socktype, protocol, from_sa, to_sa));
184255570Strasz}
185255570Strasz
186255570Straszstruct icl_listen *
187265513Straszicl_listen_new(void (*accept_cb)(struct socket *, struct sockaddr *, int))
188255570Strasz{
189255570Strasz	struct icl_listen *il;
190255570Strasz
191255570Strasz	il = malloc(sizeof(*il), M_ICL_PROXY, M_ZERO | M_WAITOK);
192255570Strasz	TAILQ_INIT(&il->il_sockets);
193255570Strasz	sx_init(&il->il_lock, "icl_listen");
194255570Strasz	il->il_accept = accept_cb;
195255570Strasz
196255570Strasz	return (il);
197255570Strasz}
198255570Strasz
199255570Straszvoid
200255570Straszicl_listen_free(struct icl_listen *il)
201255570Strasz{
202255570Strasz	struct icl_listen_sock *ils;
203255570Strasz
204255570Strasz	sx_xlock(&il->il_lock);
205255570Strasz	while (!TAILQ_EMPTY(&il->il_sockets)) {
206255570Strasz		ils = TAILQ_FIRST(&il->il_sockets);
207255570Strasz		while (ils->ils_running) {
208255570Strasz			ICL_DEBUG("waiting for accept thread to terminate");
209255570Strasz			sx_xunlock(&il->il_lock);
210255570Strasz			ils->ils_disconnecting = true;
211255570Strasz			wakeup(&ils->ils_socket->so_timeo);
212255570Strasz			pause("icl_unlisten", 1 * hz);
213255570Strasz			sx_xlock(&il->il_lock);
214255570Strasz		}
215255570Strasz
216255570Strasz		TAILQ_REMOVE(&il->il_sockets, ils, ils_next);
217255570Strasz		soclose(ils->ils_socket);
218255570Strasz		free(ils, M_ICL_PROXY);
219255570Strasz	}
220255570Strasz	sx_xunlock(&il->il_lock);
221255570Strasz
222255570Strasz	free(il, M_ICL_PROXY);
223255570Strasz}
224255570Strasz
225255570Strasz/*
226255570Strasz * XXX: Doing accept in a separate thread in each socket might not be the best way
227255570Strasz * 	to do stuff, but it's pretty clean and debuggable - and you probably won't
228255570Strasz * 	have hundreds of listening sockets anyway.
229255570Strasz */
230255570Straszstatic void
231255570Straszicl_accept_thread(void *arg)
232255570Strasz{
233255570Strasz	struct icl_listen_sock *ils;
234255570Strasz	struct socket *head, *so;
235255570Strasz	struct sockaddr *sa;
236255570Strasz	int error;
237255570Strasz
238255570Strasz	ils = arg;
239255570Strasz	head = ils->ils_socket;
240255570Strasz
241255570Strasz	ils->ils_running = true;
242255570Strasz
243255570Strasz	for (;;) {
244255570Strasz		ACCEPT_LOCK();
245255570Strasz		while (TAILQ_EMPTY(&head->so_comp) && head->so_error == 0 && ils->ils_disconnecting == false) {
246255570Strasz			if (head->so_rcv.sb_state & SBS_CANTRCVMORE) {
247255570Strasz				head->so_error = ECONNABORTED;
248255570Strasz				break;
249255570Strasz			}
250255570Strasz			error = msleep(&head->so_timeo, &accept_mtx, PSOCK | PCATCH,
251255570Strasz			    "accept", 0);
252255570Strasz			if (error) {
253255570Strasz				ACCEPT_UNLOCK();
254255570Strasz				ICL_WARN("msleep failed with error %d", error);
255255570Strasz				continue;
256255570Strasz			}
257255570Strasz			if (ils->ils_disconnecting) {
258255570Strasz				ACCEPT_UNLOCK();
259255570Strasz				ICL_DEBUG("terminating");
260255570Strasz				ils->ils_running = false;
261255570Strasz				kthread_exit();
262255570Strasz				return;
263255570Strasz			}
264255570Strasz		}
265255570Strasz		if (head->so_error) {
266255570Strasz			error = head->so_error;
267255570Strasz			head->so_error = 0;
268255570Strasz			ACCEPT_UNLOCK();
269255570Strasz			ICL_WARN("socket error %d", error);
270255570Strasz			continue;
271255570Strasz		}
272255570Strasz		so = TAILQ_FIRST(&head->so_comp);
273255570Strasz		KASSERT(so != NULL, ("NULL so"));
274255570Strasz		KASSERT(!(so->so_qstate & SQ_INCOMP), ("accept1: so SQ_INCOMP"));
275255570Strasz		KASSERT(so->so_qstate & SQ_COMP, ("accept1: so not SQ_COMP"));
276255570Strasz
277255570Strasz		/*
278255570Strasz		 * Before changing the flags on the socket, we have to bump the
279255570Strasz		 * reference count.  Otherwise, if the protocol calls sofree(),
280255570Strasz		 * the socket will be released due to a zero refcount.
281255570Strasz		 */
282255570Strasz		SOCK_LOCK(so);			/* soref() and so_state update */
283255570Strasz		soref(so);			/* file descriptor reference */
284255570Strasz
285255570Strasz		TAILQ_REMOVE(&head->so_comp, so, so_list);
286255570Strasz		head->so_qlen--;
287255570Strasz		so->so_state |= (head->so_state & SS_NBIO);
288255570Strasz		so->so_qstate &= ~SQ_COMP;
289255570Strasz		so->so_head = NULL;
290255570Strasz
291255570Strasz		SOCK_UNLOCK(so);
292255570Strasz		ACCEPT_UNLOCK();
293255570Strasz
294255570Strasz		sa = NULL;
295255570Strasz		error = soaccept(so, &sa);
296255570Strasz		if (error != 0) {
297255570Strasz			ICL_WARN("soaccept error %d", error);
298255570Strasz			if (sa != NULL)
299255570Strasz				free(sa, M_SONAME);
300255570Strasz			soclose(so);
301265513Strasz			continue;
302255570Strasz		}
303255570Strasz
304265513Strasz		(ils->ils_listen->il_accept)(so, sa, ils->ils_id);
305255570Strasz	}
306255570Strasz}
307255570Strasz
308255570Straszstatic int
309265509Straszicl_listen_add_tcp(struct icl_listen *il, int domain, int socktype,
310265509Strasz    int protocol, struct sockaddr *sa, int portal_id)
311255570Strasz{
312255570Strasz	struct icl_listen_sock *ils;
313255570Strasz	struct socket *so;
314255570Strasz	struct sockopt sopt;
315255570Strasz	int error, one = 1;
316255570Strasz
317255570Strasz	error = socreate(domain, &so, socktype, protocol,
318255570Strasz	    curthread->td_ucred, curthread);
319255570Strasz	if (error != 0) {
320255570Strasz		ICL_WARN("socreate failed with error %d", error);
321255570Strasz		return (error);
322255570Strasz	}
323255570Strasz
324255570Strasz	sopt.sopt_dir = SOPT_SET;
325255570Strasz	sopt.sopt_level = SOL_SOCKET;
326255570Strasz	sopt.sopt_name = SO_REUSEADDR;
327255570Strasz	sopt.sopt_val = &one;
328255570Strasz	sopt.sopt_valsize = sizeof(one);
329255570Strasz	sopt.sopt_td = NULL;
330255570Strasz	error = sosetopt(so, &sopt);
331255570Strasz	if (error != 0) {
332255570Strasz		ICL_WARN("failed to set SO_REUSEADDR with error %d", error);
333255570Strasz		soclose(so);
334255570Strasz		return (error);
335255570Strasz	}
336255570Strasz
337255570Strasz	error = sobind(so, sa, curthread);
338255570Strasz	if (error != 0) {
339255570Strasz		ICL_WARN("sobind failed with error %d", error);
340255570Strasz		soclose(so);
341255570Strasz		return (error);
342255570Strasz	}
343255570Strasz
344255570Strasz	error = solisten(so, -1, curthread);
345255570Strasz	if (error != 0) {
346255570Strasz		ICL_WARN("solisten failed with error %d", error);
347255570Strasz		soclose(so);
348255570Strasz		return (error);
349255570Strasz	}
350255570Strasz
351255570Strasz	ils = malloc(sizeof(*ils), M_ICL_PROXY, M_ZERO | M_WAITOK);
352255570Strasz	ils->ils_listen = il;
353255570Strasz	ils->ils_socket = so;
354265509Strasz	ils->ils_id = portal_id;
355255570Strasz
356255570Strasz	error = kthread_add(icl_accept_thread, ils, NULL, NULL, 0, 0, "iclacc");
357255570Strasz	if (error != 0) {
358255570Strasz		ICL_WARN("kthread_add failed with error %d", error);
359255570Strasz		soclose(so);
360255570Strasz		free(ils, M_ICL_PROXY);
361255570Strasz
362255570Strasz		return (error);
363255570Strasz	}
364255570Strasz
365255570Strasz	sx_xlock(&il->il_lock);
366255570Strasz	TAILQ_INSERT_TAIL(&il->il_sockets, ils, ils_next);
367255570Strasz	sx_xunlock(&il->il_lock);
368255570Strasz
369255570Strasz	return (0);
370255570Strasz}
371255570Strasz
372255570Straszint
373265509Straszicl_listen_add(struct icl_listen *il, bool rdma, int domain, int socktype,
374265509Strasz    int protocol, struct sockaddr *sa, int portal_id)
375255570Strasz{
376255570Strasz
377255570Strasz	if (rdma) {
378255570Strasz#ifndef ICL_RDMA
379255570Strasz		ICL_DEBUG("RDMA not supported");
380255570Strasz		return (EOPNOTSUPP);
381255570Strasz#else
382265509Strasz		return (icl_listen_add_rdma(il, domain, socktype, protocol,
383265509Strasz		    sa, portal_id));
384255570Strasz#endif
385255570Strasz	}
386255570Strasz
387255570Strasz
388265509Strasz	return (icl_listen_add_tcp(il, domain, socktype, protocol, sa,
389265509Strasz	    portal_id));
390255570Strasz}
391255570Strasz
392255570Straszint
393255570Straszicl_listen_remove(struct icl_listen *il, struct sockaddr *sa)
394255570Strasz{
395255570Strasz
396255570Strasz	/*
397255570Strasz	 * XXX
398255570Strasz	 */
399255570Strasz
400255570Strasz	return (EOPNOTSUPP);
401255570Strasz}
402255570Strasz
403255570Strasz#endif /* ICL_KERNEL_PROXY */
404