xref: /freebsd/sys/dev/iscsi/icl_soft.c (revision 314cb279959b08811543612a715e47266f685c7b)
1321b17ecSEdward Tomasz Napierala /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
343ee6e9dSEdward Tomasz Napierala  *
4321b17ecSEdward Tomasz Napierala  * Copyright (c) 2012 The FreeBSD Foundation
5321b17ecSEdward Tomasz Napierala  *
6321b17ecSEdward Tomasz Napierala  * This software was developed by Edward Tomasz Napierala under sponsorship
7321b17ecSEdward Tomasz Napierala  * from the FreeBSD Foundation.
8321b17ecSEdward Tomasz Napierala  *
9321b17ecSEdward Tomasz Napierala  * Redistribution and use in source and binary forms, with or without
10321b17ecSEdward Tomasz Napierala  * modification, are permitted provided that the following conditions
11321b17ecSEdward Tomasz Napierala  * are met:
12321b17ecSEdward Tomasz Napierala  * 1. Redistributions of source code must retain the above copyright
13321b17ecSEdward Tomasz Napierala  *    notice, this list of conditions and the following disclaimer.
14321b17ecSEdward Tomasz Napierala  * 2. Redistributions in binary form must reproduce the above copyright
15321b17ecSEdward Tomasz Napierala  *    notice, this list of conditions and the following disclaimer in the
16321b17ecSEdward Tomasz Napierala  *    documentation and/or other materials provided with the distribution.
17321b17ecSEdward Tomasz Napierala  *
18321b17ecSEdward Tomasz Napierala  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19321b17ecSEdward Tomasz Napierala  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20321b17ecSEdward Tomasz Napierala  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21321b17ecSEdward Tomasz Napierala  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22321b17ecSEdward Tomasz Napierala  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23321b17ecSEdward Tomasz Napierala  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24321b17ecSEdward Tomasz Napierala  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25321b17ecSEdward Tomasz Napierala  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26321b17ecSEdward Tomasz Napierala  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27321b17ecSEdward Tomasz Napierala  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28321b17ecSEdward Tomasz Napierala  * SUCH DAMAGE.
29321b17ecSEdward Tomasz Napierala  *
30321b17ecSEdward Tomasz Napierala  */
31321b17ecSEdward Tomasz Napierala 
32321b17ecSEdward Tomasz Napierala /*
335aabcd7cSEdward Tomasz Napierala  * Software implementation of iSCSI Common Layer kobj(9) interface.
34321b17ecSEdward Tomasz Napierala  */
35321b17ecSEdward Tomasz Napierala 
36321b17ecSEdward Tomasz Napierala #include <sys/param.h>
37530e725dSJohn Baldwin #include <sys/bio.h>
38321b17ecSEdward Tomasz Napierala #include <sys/capsicum.h>
39321b17ecSEdward Tomasz Napierala #include <sys/condvar.h>
40321b17ecSEdward Tomasz Napierala #include <sys/conf.h>
41f89d2072SXin LI #include <sys/gsb_crc32.h>
42321b17ecSEdward Tomasz Napierala #include <sys/file.h>
43321b17ecSEdward Tomasz Napierala #include <sys/kernel.h>
44321b17ecSEdward Tomasz Napierala #include <sys/kthread.h>
45321b17ecSEdward Tomasz Napierala #include <sys/lock.h>
46321b17ecSEdward Tomasz Napierala #include <sys/mbuf.h>
47321b17ecSEdward Tomasz Napierala #include <sys/mutex.h>
48321b17ecSEdward Tomasz Napierala #include <sys/module.h>
49321b17ecSEdward Tomasz Napierala #include <sys/protosw.h>
50321b17ecSEdward Tomasz Napierala #include <sys/socket.h>
51321b17ecSEdward Tomasz Napierala #include <sys/socketvar.h>
52321b17ecSEdward Tomasz Napierala #include <sys/sysctl.h>
53321b17ecSEdward Tomasz Napierala #include <sys/systm.h>
54321b17ecSEdward Tomasz Napierala #include <sys/sx.h>
55321b17ecSEdward Tomasz Napierala #include <sys/uio.h>
56321b17ecSEdward Tomasz Napierala #include <vm/uma.h>
57530e725dSJohn Baldwin #include <vm/vm_page.h>
58321b17ecSEdward Tomasz Napierala #include <netinet/in.h>
59321b17ecSEdward Tomasz Napierala #include <netinet/tcp.h>
60321b17ecSEdward Tomasz Napierala 
61321b17ecSEdward Tomasz Napierala #include <dev/iscsi/icl.h>
62321b17ecSEdward Tomasz Napierala #include <dev/iscsi/iscsi_proto.h>
63321b17ecSEdward Tomasz Napierala #include <icl_conn_if.h>
64321b17ecSEdward Tomasz Napierala 
6587322a90SJohn Baldwin #define ICL_CONN_STATE_BHS		1
6687322a90SJohn Baldwin #define ICL_CONN_STATE_AHS		2
6787322a90SJohn Baldwin #define ICL_CONN_STATE_HEADER_DIGEST	3
6887322a90SJohn Baldwin #define ICL_CONN_STATE_DATA		4
6987322a90SJohn Baldwin #define ICL_CONN_STATE_DATA_DIGEST	5
7087322a90SJohn Baldwin 
7187322a90SJohn Baldwin struct icl_soft_conn {
7287322a90SJohn Baldwin 	struct icl_conn	 ic;
7387322a90SJohn Baldwin 
7487322a90SJohn Baldwin 	/* soft specific stuff goes here. */
7587322a90SJohn Baldwin 	STAILQ_HEAD(, icl_pdu) to_send;
7687322a90SJohn Baldwin 	struct cv	 send_cv;
7787322a90SJohn Baldwin 	struct cv	 receive_cv;
7887322a90SJohn Baldwin 	struct icl_pdu	*receive_pdu;
7987322a90SJohn Baldwin 	size_t		 receive_len;
8087322a90SJohn Baldwin 	int		 receive_state;
8187322a90SJohn Baldwin 	bool		 receive_running;
8287322a90SJohn Baldwin 	bool		 check_send_space;
8387322a90SJohn Baldwin 	bool		 send_running;
8487322a90SJohn Baldwin };
8587322a90SJohn Baldwin 
869a4510acSAlexander Motin struct icl_soft_pdu {
879a4510acSAlexander Motin 	struct icl_pdu	 ip;
889a4510acSAlexander Motin 
899a4510acSAlexander Motin 	/* soft specific stuff goes here. */
909a4510acSAlexander Motin 	u_int		 ref_cnt;
919a4510acSAlexander Motin 	icl_pdu_cb	 cb;
929a4510acSAlexander Motin 	int		 error;
939a4510acSAlexander Motin };
949a4510acSAlexander Motin 
95b75168edSAlexander Motin SYSCTL_NODE(_kern_icl, OID_AUTO, soft, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
96b75168edSAlexander Motin     "Software iSCSI");
97321b17ecSEdward Tomasz Napierala static int coalesce = 1;
98b75168edSAlexander Motin SYSCTL_INT(_kern_icl_soft, OID_AUTO, coalesce, CTLFLAG_RWTUN,
99321b17ecSEdward Tomasz Napierala     &coalesce, 0, "Try to coalesce PDUs before sending");
100b75168edSAlexander Motin static int partial_receive_len = 256 * 1024;
101b75168edSAlexander Motin SYSCTL_INT(_kern_icl_soft, OID_AUTO, partial_receive_len, CTLFLAG_RWTUN,
102321b17ecSEdward Tomasz Napierala     &partial_receive_len, 0, "Minimum read size for partially received "
103321b17ecSEdward Tomasz Napierala     "data segment");
104b75168edSAlexander Motin static int max_data_segment_length = 256 * 1024;
105b75168edSAlexander Motin SYSCTL_INT(_kern_icl_soft, OID_AUTO, max_data_segment_length, CTLFLAG_RWTUN,
106b75168edSAlexander Motin     &max_data_segment_length, 0, "Maximum data segment length");
107b75168edSAlexander Motin static int first_burst_length = 1024 * 1024;
108b75168edSAlexander Motin SYSCTL_INT(_kern_icl_soft, OID_AUTO, first_burst_length, CTLFLAG_RWTUN,
109b75168edSAlexander Motin     &first_burst_length, 0, "First burst length");
110b75168edSAlexander Motin static int max_burst_length = 1024 * 1024;
111b75168edSAlexander Motin SYSCTL_INT(_kern_icl_soft, OID_AUTO, max_burst_length, CTLFLAG_RWTUN,
112b75168edSAlexander Motin     &max_burst_length, 0, "Maximum burst length");
113b75168edSAlexander Motin static int sendspace = 1536 * 1024;
114b75168edSAlexander Motin SYSCTL_INT(_kern_icl_soft, OID_AUTO, sendspace, CTLFLAG_RWTUN,
115321b17ecSEdward Tomasz Napierala     &sendspace, 0, "Default send socket buffer size");
116b75168edSAlexander Motin static int recvspace = 1536 * 1024;
117b75168edSAlexander Motin SYSCTL_INT(_kern_icl_soft, OID_AUTO, recvspace, CTLFLAG_RWTUN,
118321b17ecSEdward Tomasz Napierala     &recvspace, 0, "Default receive socket buffer size");
119321b17ecSEdward Tomasz Napierala 
120321b17ecSEdward Tomasz Napierala static MALLOC_DEFINE(M_ICL_SOFT, "icl_soft", "iSCSI software backend");
1219a4510acSAlexander Motin static uma_zone_t icl_soft_pdu_zone;
122321b17ecSEdward Tomasz Napierala 
123321b17ecSEdward Tomasz Napierala static volatile u_int	icl_ncons;
124321b17ecSEdward Tomasz Napierala 
125321b17ecSEdward Tomasz Napierala STAILQ_HEAD(icl_pdu_stailq, icl_pdu);
126321b17ecSEdward Tomasz Napierala 
127321b17ecSEdward Tomasz Napierala static icl_conn_new_pdu_t	icl_soft_conn_new_pdu;
128321b17ecSEdward Tomasz Napierala static icl_conn_pdu_free_t	icl_soft_conn_pdu_free;
129321b17ecSEdward Tomasz Napierala static icl_conn_pdu_data_segment_length_t
130321b17ecSEdward Tomasz Napierala 				    icl_soft_conn_pdu_data_segment_length;
131530e725dSJohn Baldwin static icl_conn_pdu_append_bio_t	icl_soft_conn_pdu_append_bio;
132321b17ecSEdward Tomasz Napierala static icl_conn_pdu_append_data_t	icl_soft_conn_pdu_append_data;
133530e725dSJohn Baldwin static icl_conn_pdu_get_bio_t	icl_soft_conn_pdu_get_bio;
134321b17ecSEdward Tomasz Napierala static icl_conn_pdu_get_data_t	icl_soft_conn_pdu_get_data;
135321b17ecSEdward Tomasz Napierala static icl_conn_pdu_queue_t	icl_soft_conn_pdu_queue;
1369a4510acSAlexander Motin static icl_conn_pdu_queue_cb_t	icl_soft_conn_pdu_queue_cb;
137321b17ecSEdward Tomasz Napierala static icl_conn_handoff_t	icl_soft_conn_handoff;
138321b17ecSEdward Tomasz Napierala static icl_conn_free_t		icl_soft_conn_free;
139321b17ecSEdward Tomasz Napierala static icl_conn_close_t		icl_soft_conn_close;
1407a03d007SEdward Tomasz Napierala static icl_conn_task_setup_t	icl_soft_conn_task_setup;
1417a03d007SEdward Tomasz Napierala static icl_conn_task_done_t	icl_soft_conn_task_done;
1427a03d007SEdward Tomasz Napierala static icl_conn_transfer_setup_t	icl_soft_conn_transfer_setup;
1437a03d007SEdward Tomasz Napierala static icl_conn_transfer_done_t	icl_soft_conn_transfer_done;
144f41492b0SEdward Tomasz Napierala #ifdef ICL_KERNEL_PROXY
145f41492b0SEdward Tomasz Napierala static icl_conn_connect_t	icl_soft_conn_connect;
146f41492b0SEdward Tomasz Napierala #endif
147321b17ecSEdward Tomasz Napierala 
148321b17ecSEdward Tomasz Napierala static kobj_method_t icl_soft_methods[] = {
149321b17ecSEdward Tomasz Napierala 	KOBJMETHOD(icl_conn_new_pdu, icl_soft_conn_new_pdu),
150321b17ecSEdward Tomasz Napierala 	KOBJMETHOD(icl_conn_pdu_free, icl_soft_conn_pdu_free),
151321b17ecSEdward Tomasz Napierala 	KOBJMETHOD(icl_conn_pdu_data_segment_length,
152321b17ecSEdward Tomasz Napierala 	    icl_soft_conn_pdu_data_segment_length),
153530e725dSJohn Baldwin 	KOBJMETHOD(icl_conn_pdu_append_bio, icl_soft_conn_pdu_append_bio),
154321b17ecSEdward Tomasz Napierala 	KOBJMETHOD(icl_conn_pdu_append_data, icl_soft_conn_pdu_append_data),
155530e725dSJohn Baldwin 	KOBJMETHOD(icl_conn_pdu_get_bio, icl_soft_conn_pdu_get_bio),
156321b17ecSEdward Tomasz Napierala 	KOBJMETHOD(icl_conn_pdu_get_data, icl_soft_conn_pdu_get_data),
157321b17ecSEdward Tomasz Napierala 	KOBJMETHOD(icl_conn_pdu_queue, icl_soft_conn_pdu_queue),
1589a4510acSAlexander Motin 	KOBJMETHOD(icl_conn_pdu_queue_cb, icl_soft_conn_pdu_queue_cb),
159321b17ecSEdward Tomasz Napierala 	KOBJMETHOD(icl_conn_handoff, icl_soft_conn_handoff),
160321b17ecSEdward Tomasz Napierala 	KOBJMETHOD(icl_conn_free, icl_soft_conn_free),
161321b17ecSEdward Tomasz Napierala 	KOBJMETHOD(icl_conn_close, icl_soft_conn_close),
1627a03d007SEdward Tomasz Napierala 	KOBJMETHOD(icl_conn_task_setup, icl_soft_conn_task_setup),
1637a03d007SEdward Tomasz Napierala 	KOBJMETHOD(icl_conn_task_done, icl_soft_conn_task_done),
1647a03d007SEdward Tomasz Napierala 	KOBJMETHOD(icl_conn_transfer_setup, icl_soft_conn_transfer_setup),
1657a03d007SEdward Tomasz Napierala 	KOBJMETHOD(icl_conn_transfer_done, icl_soft_conn_transfer_done),
166f41492b0SEdward Tomasz Napierala #ifdef ICL_KERNEL_PROXY
167f41492b0SEdward Tomasz Napierala 	KOBJMETHOD(icl_conn_connect, icl_soft_conn_connect),
168f41492b0SEdward Tomasz Napierala #endif
169321b17ecSEdward Tomasz Napierala 	{ 0, 0 }
170321b17ecSEdward Tomasz Napierala };
171321b17ecSEdward Tomasz Napierala 
17287322a90SJohn Baldwin DEFINE_CLASS(icl_soft, icl_soft_methods, sizeof(struct icl_soft_conn));
173321b17ecSEdward Tomasz Napierala 
174321b17ecSEdward Tomasz Napierala static void
icl_conn_fail(struct icl_conn * ic)175321b17ecSEdward Tomasz Napierala icl_conn_fail(struct icl_conn *ic)
176321b17ecSEdward Tomasz Napierala {
177321b17ecSEdward Tomasz Napierala 	if (ic->ic_socket == NULL)
178321b17ecSEdward Tomasz Napierala 		return;
179321b17ecSEdward Tomasz Napierala 
180321b17ecSEdward Tomasz Napierala 	/*
181321b17ecSEdward Tomasz Napierala 	 * XXX
182321b17ecSEdward Tomasz Napierala 	 */
183321b17ecSEdward Tomasz Napierala 	ic->ic_socket->so_error = EDOOFUS;
184321b17ecSEdward Tomasz Napierala 	(ic->ic_error)(ic);
185321b17ecSEdward Tomasz Napierala }
186321b17ecSEdward Tomasz Napierala 
18782f7fa7aSAlexander Motin static void
icl_soft_conn_pdu_free(struct icl_conn * ic,struct icl_pdu * ip)18882f7fa7aSAlexander Motin icl_soft_conn_pdu_free(struct icl_conn *ic, struct icl_pdu *ip)
18982f7fa7aSAlexander Motin {
1909a4510acSAlexander Motin 	struct icl_soft_pdu *isp = (struct icl_soft_pdu *)ip;
19182f7fa7aSAlexander Motin 
1929a4510acSAlexander Motin 	KASSERT(isp->ref_cnt == 0, ("freeing active PDU"));
19382f7fa7aSAlexander Motin 	m_freem(ip->ip_bhs_mbuf);
19482f7fa7aSAlexander Motin 	m_freem(ip->ip_ahs_mbuf);
19582f7fa7aSAlexander Motin 	m_freem(ip->ip_data_mbuf);
1969a4510acSAlexander Motin 	uma_zfree(icl_soft_pdu_zone, isp);
19782f7fa7aSAlexander Motin #ifdef DIAGNOSTIC
19882f7fa7aSAlexander Motin 	refcount_release(&ic->ic_outstanding_pdus);
19982f7fa7aSAlexander Motin #endif
20082f7fa7aSAlexander Motin }
20182f7fa7aSAlexander Motin 
2029a4510acSAlexander Motin static void
icl_soft_pdu_call_cb(struct icl_pdu * ip)2039a4510acSAlexander Motin icl_soft_pdu_call_cb(struct icl_pdu *ip)
2049a4510acSAlexander Motin {
2059a4510acSAlexander Motin 	struct icl_soft_pdu *isp = (struct icl_soft_pdu *)ip;
2069a4510acSAlexander Motin 
2079a4510acSAlexander Motin 	if (isp->cb != NULL)
2089a4510acSAlexander Motin 		isp->cb(ip, isp->error);
2099a4510acSAlexander Motin #ifdef DIAGNOSTIC
2109a4510acSAlexander Motin 	refcount_release(&ip->ip_conn->ic_outstanding_pdus);
2119a4510acSAlexander Motin #endif
2129a4510acSAlexander Motin 	uma_zfree(icl_soft_pdu_zone, isp);
2139a4510acSAlexander Motin }
2149a4510acSAlexander Motin 
2159a4510acSAlexander Motin static void
icl_soft_pdu_done(struct icl_pdu * ip,int error)2169a4510acSAlexander Motin icl_soft_pdu_done(struct icl_pdu *ip, int error)
2179a4510acSAlexander Motin {
2189a4510acSAlexander Motin 	struct icl_soft_pdu *isp = (struct icl_soft_pdu *)ip;
2199a4510acSAlexander Motin 
2209a4510acSAlexander Motin 	if (error != 0)
2219a4510acSAlexander Motin 		isp->error = error;
2229a4510acSAlexander Motin 
2239a4510acSAlexander Motin 	m_freem(ip->ip_bhs_mbuf);
2249a4510acSAlexander Motin 	ip->ip_bhs_mbuf = NULL;
2259a4510acSAlexander Motin 	m_freem(ip->ip_ahs_mbuf);
2269a4510acSAlexander Motin 	ip->ip_ahs_mbuf = NULL;
2279a4510acSAlexander Motin 	m_freem(ip->ip_data_mbuf);
2289a4510acSAlexander Motin 	ip->ip_data_mbuf = NULL;
2299a4510acSAlexander Motin 
2309a4510acSAlexander Motin 	if (atomic_fetchadd_int(&isp->ref_cnt, -1) == 1)
2319a4510acSAlexander Motin 		icl_soft_pdu_call_cb(ip);
2329a4510acSAlexander Motin }
2339a4510acSAlexander Motin 
2349a4510acSAlexander Motin static void
icl_soft_mbuf_done(struct mbuf * mb)2359a4510acSAlexander Motin icl_soft_mbuf_done(struct mbuf *mb)
2369a4510acSAlexander Motin {
2379a4510acSAlexander Motin 	struct icl_soft_pdu *isp = (struct icl_soft_pdu *)mb->m_ext.ext_arg1;
2389a4510acSAlexander Motin 
2399a4510acSAlexander Motin 	icl_soft_pdu_call_cb(&isp->ip);
2409a4510acSAlexander Motin }
2419a4510acSAlexander Motin 
24282f7fa7aSAlexander Motin /*
24382f7fa7aSAlexander Motin  * Allocate icl_pdu with empty BHS to fill up by the caller.
24482f7fa7aSAlexander Motin  */
24582f7fa7aSAlexander Motin struct icl_pdu *
icl_soft_conn_new_pdu(struct icl_conn * ic,int flags)24682f7fa7aSAlexander Motin icl_soft_conn_new_pdu(struct icl_conn *ic, int flags)
247321b17ecSEdward Tomasz Napierala {
2489a4510acSAlexander Motin 	struct icl_soft_pdu *isp;
249321b17ecSEdward Tomasz Napierala 	struct icl_pdu *ip;
250321b17ecSEdward Tomasz Napierala 
251321b17ecSEdward Tomasz Napierala #ifdef DIAGNOSTIC
252321b17ecSEdward Tomasz Napierala 	refcount_acquire(&ic->ic_outstanding_pdus);
253321b17ecSEdward Tomasz Napierala #endif
2549a4510acSAlexander Motin 	isp = uma_zalloc(icl_soft_pdu_zone, flags | M_ZERO);
2559a4510acSAlexander Motin 	if (isp == NULL) {
2569a4510acSAlexander Motin 		ICL_WARN("failed to allocate soft PDU");
257321b17ecSEdward Tomasz Napierala #ifdef DIAGNOSTIC
258321b17ecSEdward Tomasz Napierala 		refcount_release(&ic->ic_outstanding_pdus);
259321b17ecSEdward Tomasz Napierala #endif
260321b17ecSEdward Tomasz Napierala 		return (NULL);
261321b17ecSEdward Tomasz Napierala 	}
2629a4510acSAlexander Motin 	ip = &isp->ip;
263321b17ecSEdward Tomasz Napierala 	ip->ip_conn = ic;
264321b17ecSEdward Tomasz Napierala 
26533d9db92SAlexander Motin 	CTASSERT(sizeof(struct iscsi_bhs) <= MHLEN);
26633d9db92SAlexander Motin 	ip->ip_bhs_mbuf = m_gethdr(flags, MT_DATA);
267321b17ecSEdward Tomasz Napierala 	if (ip->ip_bhs_mbuf == NULL) {
268d0d587c7SAlexander Motin 		ICL_WARN("failed to allocate BHS mbuf");
26982f7fa7aSAlexander Motin 		icl_soft_conn_pdu_free(ic, ip);
270321b17ecSEdward Tomasz Napierala 		return (NULL);
271321b17ecSEdward Tomasz Napierala 	}
272321b17ecSEdward Tomasz Napierala 	ip->ip_bhs = mtod(ip->ip_bhs_mbuf, struct iscsi_bhs *);
273321b17ecSEdward Tomasz Napierala 	memset(ip->ip_bhs, 0, sizeof(struct iscsi_bhs));
274321b17ecSEdward Tomasz Napierala 	ip->ip_bhs_mbuf->m_len = sizeof(struct iscsi_bhs);
275321b17ecSEdward Tomasz Napierala 
276321b17ecSEdward Tomasz Napierala 	return (ip);
277321b17ecSEdward Tomasz Napierala }
278321b17ecSEdward Tomasz Napierala 
279321b17ecSEdward Tomasz Napierala static int
icl_pdu_ahs_length(const struct icl_pdu * request)280321b17ecSEdward Tomasz Napierala icl_pdu_ahs_length(const struct icl_pdu *request)
281321b17ecSEdward Tomasz Napierala {
282321b17ecSEdward Tomasz Napierala 
283321b17ecSEdward Tomasz Napierala 	return (request->ip_bhs->bhs_total_ahs_len * 4);
284321b17ecSEdward Tomasz Napierala }
285321b17ecSEdward Tomasz Napierala 
286321b17ecSEdward Tomasz Napierala static size_t
icl_pdu_data_segment_length(const struct icl_pdu * request)287321b17ecSEdward Tomasz Napierala icl_pdu_data_segment_length(const struct icl_pdu *request)
288321b17ecSEdward Tomasz Napierala {
289321b17ecSEdward Tomasz Napierala 	uint32_t len = 0;
290321b17ecSEdward Tomasz Napierala 
291321b17ecSEdward Tomasz Napierala 	len += request->ip_bhs->bhs_data_segment_len[0];
292321b17ecSEdward Tomasz Napierala 	len <<= 8;
293321b17ecSEdward Tomasz Napierala 	len += request->ip_bhs->bhs_data_segment_len[1];
294321b17ecSEdward Tomasz Napierala 	len <<= 8;
295321b17ecSEdward Tomasz Napierala 	len += request->ip_bhs->bhs_data_segment_len[2];
296321b17ecSEdward Tomasz Napierala 
297321b17ecSEdward Tomasz Napierala 	return (len);
298321b17ecSEdward Tomasz Napierala }
299321b17ecSEdward Tomasz Napierala 
300321b17ecSEdward Tomasz Napierala size_t
icl_soft_conn_pdu_data_segment_length(struct icl_conn * ic,const struct icl_pdu * request)301321b17ecSEdward Tomasz Napierala icl_soft_conn_pdu_data_segment_length(struct icl_conn *ic,
302321b17ecSEdward Tomasz Napierala     const struct icl_pdu *request)
303321b17ecSEdward Tomasz Napierala {
304321b17ecSEdward Tomasz Napierala 
305321b17ecSEdward Tomasz Napierala 	return (icl_pdu_data_segment_length(request));
306321b17ecSEdward Tomasz Napierala }
307321b17ecSEdward Tomasz Napierala 
308321b17ecSEdward Tomasz Napierala static void
icl_pdu_set_data_segment_length(struct icl_pdu * response,uint32_t len)309321b17ecSEdward Tomasz Napierala icl_pdu_set_data_segment_length(struct icl_pdu *response, uint32_t len)
310321b17ecSEdward Tomasz Napierala {
311321b17ecSEdward Tomasz Napierala 
312321b17ecSEdward Tomasz Napierala 	response->ip_bhs->bhs_data_segment_len[2] = len;
313321b17ecSEdward Tomasz Napierala 	response->ip_bhs->bhs_data_segment_len[1] = len >> 8;
314321b17ecSEdward Tomasz Napierala 	response->ip_bhs->bhs_data_segment_len[0] = len >> 16;
315321b17ecSEdward Tomasz Napierala }
316321b17ecSEdward Tomasz Napierala 
317321b17ecSEdward Tomasz Napierala static size_t
icl_pdu_padding(const struct icl_pdu * ip)318321b17ecSEdward Tomasz Napierala icl_pdu_padding(const struct icl_pdu *ip)
319321b17ecSEdward Tomasz Napierala {
320321b17ecSEdward Tomasz Napierala 
321321b17ecSEdward Tomasz Napierala 	if ((ip->ip_data_len % 4) != 0)
322321b17ecSEdward Tomasz Napierala 		return (4 - (ip->ip_data_len % 4));
323321b17ecSEdward Tomasz Napierala 
324321b17ecSEdward Tomasz Napierala 	return (0);
325321b17ecSEdward Tomasz Napierala }
326321b17ecSEdward Tomasz Napierala 
327321b17ecSEdward Tomasz Napierala static size_t
icl_pdu_size(const struct icl_pdu * response)328321b17ecSEdward Tomasz Napierala icl_pdu_size(const struct icl_pdu *response)
329321b17ecSEdward Tomasz Napierala {
330321b17ecSEdward Tomasz Napierala 	size_t len;
331321b17ecSEdward Tomasz Napierala 
332321b17ecSEdward Tomasz Napierala 	KASSERT(response->ip_ahs_len == 0, ("responding with AHS"));
333321b17ecSEdward Tomasz Napierala 
334321b17ecSEdward Tomasz Napierala 	len = sizeof(struct iscsi_bhs) + response->ip_data_len +
335321b17ecSEdward Tomasz Napierala 	    icl_pdu_padding(response);
336321b17ecSEdward Tomasz Napierala 	if (response->ip_conn->ic_header_crc32c)
337321b17ecSEdward Tomasz Napierala 		len += ISCSI_HEADER_DIGEST_SIZE;
338321b17ecSEdward Tomasz Napierala 	if (response->ip_data_len != 0 && response->ip_conn->ic_data_crc32c)
339321b17ecSEdward Tomasz Napierala 		len += ISCSI_DATA_DIGEST_SIZE;
340321b17ecSEdward Tomasz Napierala 
341321b17ecSEdward Tomasz Napierala 	return (len);
342321b17ecSEdward Tomasz Napierala }
343321b17ecSEdward Tomasz Napierala 
3446895f89fSAlexander Motin static void
icl_soft_receive_buf(struct mbuf ** r,size_t * rs,void * buf,size_t s)3456895f89fSAlexander Motin icl_soft_receive_buf(struct mbuf **r, size_t *rs, void *buf, size_t s)
346321b17ecSEdward Tomasz Napierala {
347321b17ecSEdward Tomasz Napierala 
3486895f89fSAlexander Motin 	m_copydata(*r, 0, s, buf);
3496895f89fSAlexander Motin 	m_adj(*r, s);
3506895f89fSAlexander Motin 	while ((*r) != NULL && (*r)->m_len == 0)
3516895f89fSAlexander Motin 		*r = m_free(*r);
3526895f89fSAlexander Motin 	*rs -= s;
353321b17ecSEdward Tomasz Napierala }
354321b17ecSEdward Tomasz Napierala 
3556895f89fSAlexander Motin static void
icl_pdu_receive_ahs(struct icl_pdu * request,struct mbuf ** r,size_t * rs)3566895f89fSAlexander Motin icl_pdu_receive_ahs(struct icl_pdu *request, struct mbuf **r, size_t *rs)
357321b17ecSEdward Tomasz Napierala {
358321b17ecSEdward Tomasz Napierala 
359321b17ecSEdward Tomasz Napierala 	request->ip_ahs_len = icl_pdu_ahs_length(request);
360321b17ecSEdward Tomasz Napierala 	if (request->ip_ahs_len == 0)
3616895f89fSAlexander Motin 		return;
362321b17ecSEdward Tomasz Napierala 
3636895f89fSAlexander Motin 	request->ip_ahs_mbuf = *r;
3646895f89fSAlexander Motin 	*r = m_split(request->ip_ahs_mbuf, request->ip_ahs_len, M_WAITOK);
3656895f89fSAlexander Motin 	*rs -= request->ip_ahs_len;
366321b17ecSEdward Tomasz Napierala }
367321b17ecSEdward Tomasz Napierala 
368530e725dSJohn Baldwin static int
mbuf_crc32c_helper(void * arg,void * data,u_int len)369530e725dSJohn Baldwin mbuf_crc32c_helper(void *arg, void *data, u_int len)
370530e725dSJohn Baldwin {
371530e725dSJohn Baldwin 	uint32_t *digestp = arg;
372530e725dSJohn Baldwin 
373530e725dSJohn Baldwin 	*digestp = calculate_crc32c(*digestp, data, len);
374530e725dSJohn Baldwin 	return (0);
375530e725dSJohn Baldwin }
376530e725dSJohn Baldwin 
377321b17ecSEdward Tomasz Napierala static uint32_t
icl_mbuf_to_crc32c(struct mbuf * m0,size_t len)378530e725dSJohn Baldwin icl_mbuf_to_crc32c(struct mbuf *m0, size_t len)
379321b17ecSEdward Tomasz Napierala {
380321b17ecSEdward Tomasz Napierala 	uint32_t digest = 0xffffffff;
381321b17ecSEdward Tomasz Napierala 
382530e725dSJohn Baldwin 	m_apply(m0, 0, len, mbuf_crc32c_helper, &digest);
383321b17ecSEdward Tomasz Napierala 	digest = digest ^ 0xffffffff;
384321b17ecSEdward Tomasz Napierala 
385321b17ecSEdward Tomasz Napierala 	return (digest);
386321b17ecSEdward Tomasz Napierala }
387321b17ecSEdward Tomasz Napierala 
388321b17ecSEdward Tomasz Napierala static int
icl_pdu_check_header_digest(struct icl_pdu * request,struct mbuf ** r,size_t * rs)3896895f89fSAlexander Motin icl_pdu_check_header_digest(struct icl_pdu *request, struct mbuf **r, size_t *rs)
390321b17ecSEdward Tomasz Napierala {
391321b17ecSEdward Tomasz Napierala 	uint32_t received_digest, valid_digest;
392321b17ecSEdward Tomasz Napierala 
393321b17ecSEdward Tomasz Napierala 	if (request->ip_conn->ic_header_crc32c == false)
394321b17ecSEdward Tomasz Napierala 		return (0);
395321b17ecSEdward Tomasz Napierala 
396d0d587c7SAlexander Motin 	CTASSERT(sizeof(received_digest) == ISCSI_HEADER_DIGEST_SIZE);
3976895f89fSAlexander Motin 	icl_soft_receive_buf(r, rs, &received_digest, ISCSI_HEADER_DIGEST_SIZE);
398321b17ecSEdward Tomasz Napierala 
399875ac6cfSAlexander Motin 	/* Temporary attach AHS to BHS to calculate header digest. */
400875ac6cfSAlexander Motin 	request->ip_bhs_mbuf->m_next = request->ip_ahs_mbuf;
401530e725dSJohn Baldwin 	valid_digest = icl_mbuf_to_crc32c(request->ip_bhs_mbuf, ISCSI_BHS_SIZE);
402875ac6cfSAlexander Motin 	request->ip_bhs_mbuf->m_next = NULL;
403321b17ecSEdward Tomasz Napierala 	if (received_digest != valid_digest) {
404321b17ecSEdward Tomasz Napierala 		ICL_WARN("header digest check failed; got 0x%x, "
405321b17ecSEdward Tomasz Napierala 		    "should be 0x%x", received_digest, valid_digest);
406321b17ecSEdward Tomasz Napierala 		return (-1);
407321b17ecSEdward Tomasz Napierala 	}
408321b17ecSEdward Tomasz Napierala 
409321b17ecSEdward Tomasz Napierala 	return (0);
410321b17ecSEdward Tomasz Napierala }
411321b17ecSEdward Tomasz Napierala 
412321b17ecSEdward Tomasz Napierala /*
413321b17ecSEdward Tomasz Napierala  * Return the number of bytes that should be waiting in the receive socket
414321b17ecSEdward Tomasz Napierala  * before icl_pdu_receive_data_segment() gets called.
415321b17ecSEdward Tomasz Napierala  */
416321b17ecSEdward Tomasz Napierala static size_t
icl_pdu_data_segment_receive_len(const struct icl_pdu * request)417321b17ecSEdward Tomasz Napierala icl_pdu_data_segment_receive_len(const struct icl_pdu *request)
418321b17ecSEdward Tomasz Napierala {
419321b17ecSEdward Tomasz Napierala 	size_t len;
420321b17ecSEdward Tomasz Napierala 
421321b17ecSEdward Tomasz Napierala 	len = icl_pdu_data_segment_length(request);
422321b17ecSEdward Tomasz Napierala 	if (len == 0)
423321b17ecSEdward Tomasz Napierala 		return (0);
424321b17ecSEdward Tomasz Napierala 
425321b17ecSEdward Tomasz Napierala 	/*
426321b17ecSEdward Tomasz Napierala 	 * Account for the parts of data segment already read from
427321b17ecSEdward Tomasz Napierala 	 * the socket buffer.
428321b17ecSEdward Tomasz Napierala 	 */
429321b17ecSEdward Tomasz Napierala 	KASSERT(len > request->ip_data_len, ("len <= request->ip_data_len"));
430321b17ecSEdward Tomasz Napierala 	len -= request->ip_data_len;
431321b17ecSEdward Tomasz Napierala 
432321b17ecSEdward Tomasz Napierala 	/*
433321b17ecSEdward Tomasz Napierala 	 * Don't always wait for the full data segment to be delivered
434321b17ecSEdward Tomasz Napierala 	 * to the socket; this might badly affect performance due to
435321b17ecSEdward Tomasz Napierala 	 * TCP window scaling.
436321b17ecSEdward Tomasz Napierala 	 */
437321b17ecSEdward Tomasz Napierala 	if (len > partial_receive_len) {
438321b17ecSEdward Tomasz Napierala #if 0
439321b17ecSEdward Tomasz Napierala 		ICL_DEBUG("need %zd bytes of data, limiting to %zd",
440321b17ecSEdward Tomasz Napierala 		    len, partial_receive_len));
441321b17ecSEdward Tomasz Napierala #endif
442321b17ecSEdward Tomasz Napierala 		len = partial_receive_len;
443321b17ecSEdward Tomasz Napierala 
444321b17ecSEdward Tomasz Napierala 		return (len);
445321b17ecSEdward Tomasz Napierala 	}
446321b17ecSEdward Tomasz Napierala 
447321b17ecSEdward Tomasz Napierala 	/*
448321b17ecSEdward Tomasz Napierala 	 * Account for padding.  Note that due to the way code is written,
449321b17ecSEdward Tomasz Napierala 	 * the icl_pdu_receive_data_segment() must always receive padding
450321b17ecSEdward Tomasz Napierala 	 * along with the last part of data segment, because it would be
451321b17ecSEdward Tomasz Napierala 	 * impossible to tell whether we've already received the full data
452321b17ecSEdward Tomasz Napierala 	 * segment including padding, or without it.
453321b17ecSEdward Tomasz Napierala 	 */
454321b17ecSEdward Tomasz Napierala 	if ((len % 4) != 0)
455321b17ecSEdward Tomasz Napierala 		len += 4 - (len % 4);
456321b17ecSEdward Tomasz Napierala 
457321b17ecSEdward Tomasz Napierala #if 0
458321b17ecSEdward Tomasz Napierala 	ICL_DEBUG("need %zd bytes of data", len));
459321b17ecSEdward Tomasz Napierala #endif
460321b17ecSEdward Tomasz Napierala 
461321b17ecSEdward Tomasz Napierala 	return (len);
462321b17ecSEdward Tomasz Napierala }
463321b17ecSEdward Tomasz Napierala 
464321b17ecSEdward Tomasz Napierala static int
icl_pdu_receive_data_segment(struct icl_pdu * request,struct mbuf ** r,size_t * rs,bool * more_neededp)4656895f89fSAlexander Motin icl_pdu_receive_data_segment(struct icl_pdu *request, struct mbuf **r,
4666895f89fSAlexander Motin     size_t *rs, bool *more_neededp)
467321b17ecSEdward Tomasz Napierala {
46887322a90SJohn Baldwin 	struct icl_soft_conn *isc;
469321b17ecSEdward Tomasz Napierala 	size_t len, padding = 0;
470321b17ecSEdward Tomasz Napierala 	struct mbuf *m;
471321b17ecSEdward Tomasz Napierala 
47287322a90SJohn Baldwin 	isc = (struct icl_soft_conn *)request->ip_conn;
473321b17ecSEdward Tomasz Napierala 
474321b17ecSEdward Tomasz Napierala 	*more_neededp = false;
47587322a90SJohn Baldwin 	isc->receive_len = 0;
476321b17ecSEdward Tomasz Napierala 
477321b17ecSEdward Tomasz Napierala 	len = icl_pdu_data_segment_length(request);
478321b17ecSEdward Tomasz Napierala 	if (len == 0)
479321b17ecSEdward Tomasz Napierala 		return (0);
480321b17ecSEdward Tomasz Napierala 
481321b17ecSEdward Tomasz Napierala 	if ((len % 4) != 0)
482321b17ecSEdward Tomasz Napierala 		padding = 4 - (len % 4);
483321b17ecSEdward Tomasz Napierala 
484321b17ecSEdward Tomasz Napierala 	/*
485321b17ecSEdward Tomasz Napierala 	 * Account for already received parts of data segment.
486321b17ecSEdward Tomasz Napierala 	 */
487321b17ecSEdward Tomasz Napierala 	KASSERT(len > request->ip_data_len, ("len <= request->ip_data_len"));
488321b17ecSEdward Tomasz Napierala 	len -= request->ip_data_len;
489321b17ecSEdward Tomasz Napierala 
4906895f89fSAlexander Motin 	if (len + padding > *rs) {
491321b17ecSEdward Tomasz Napierala 		/*
492321b17ecSEdward Tomasz Napierala 		 * Not enough data in the socket buffer.  Receive as much
493321b17ecSEdward Tomasz Napierala 		 * as we can.  Don't receive padding, since, obviously, it's
494321b17ecSEdward Tomasz Napierala 		 * not the end of data segment yet.
495321b17ecSEdward Tomasz Napierala 		 */
496321b17ecSEdward Tomasz Napierala #if 0
497321b17ecSEdward Tomasz Napierala 		ICL_DEBUG("limited from %zd to %zd",
4986895f89fSAlexander Motin 		    len + padding, *rs - padding));
499321b17ecSEdward Tomasz Napierala #endif
5006895f89fSAlexander Motin 		len = *rs - padding;
501321b17ecSEdward Tomasz Napierala 		*more_neededp = true;
502321b17ecSEdward Tomasz Napierala 		padding = 0;
503321b17ecSEdward Tomasz Napierala 	}
504321b17ecSEdward Tomasz Napierala 
505321b17ecSEdward Tomasz Napierala 	/*
506321b17ecSEdward Tomasz Napierala 	 * Must not try to receive padding without at least one byte
507321b17ecSEdward Tomasz Napierala 	 * of actual data segment.
508321b17ecSEdward Tomasz Napierala 	 */
509321b17ecSEdward Tomasz Napierala 	if (len > 0) {
5106895f89fSAlexander Motin 		m = *r;
5116895f89fSAlexander Motin 		*r = m_split(m, len + padding, M_WAITOK);
5126895f89fSAlexander Motin 		*rs -= len + padding;
513321b17ecSEdward Tomasz Napierala 
514321b17ecSEdward Tomasz Napierala 		if (request->ip_data_mbuf == NULL)
515321b17ecSEdward Tomasz Napierala 			request->ip_data_mbuf = m;
516321b17ecSEdward Tomasz Napierala 		else
517321b17ecSEdward Tomasz Napierala 			m_cat(request->ip_data_mbuf, m);
518321b17ecSEdward Tomasz Napierala 
519321b17ecSEdward Tomasz Napierala 		request->ip_data_len += len;
520321b17ecSEdward Tomasz Napierala 	} else
521321b17ecSEdward Tomasz Napierala 		ICL_DEBUG("len 0");
522321b17ecSEdward Tomasz Napierala 
523321b17ecSEdward Tomasz Napierala 	if (*more_neededp)
52487322a90SJohn Baldwin 		isc->receive_len = icl_pdu_data_segment_receive_len(request);
525321b17ecSEdward Tomasz Napierala 
526321b17ecSEdward Tomasz Napierala 	return (0);
527321b17ecSEdward Tomasz Napierala }
528321b17ecSEdward Tomasz Napierala 
529321b17ecSEdward Tomasz Napierala static int
icl_pdu_check_data_digest(struct icl_pdu * request,struct mbuf ** r,size_t * rs)5306895f89fSAlexander Motin icl_pdu_check_data_digest(struct icl_pdu *request, struct mbuf **r, size_t *rs)
531321b17ecSEdward Tomasz Napierala {
532321b17ecSEdward Tomasz Napierala 	uint32_t received_digest, valid_digest;
533321b17ecSEdward Tomasz Napierala 
534321b17ecSEdward Tomasz Napierala 	if (request->ip_conn->ic_data_crc32c == false)
535321b17ecSEdward Tomasz Napierala 		return (0);
536321b17ecSEdward Tomasz Napierala 
537321b17ecSEdward Tomasz Napierala 	if (request->ip_data_len == 0)
538321b17ecSEdward Tomasz Napierala 		return (0);
539321b17ecSEdward Tomasz Napierala 
540d0d587c7SAlexander Motin 	CTASSERT(sizeof(received_digest) == ISCSI_DATA_DIGEST_SIZE);
5416895f89fSAlexander Motin 	icl_soft_receive_buf(r, rs, &received_digest, ISCSI_DATA_DIGEST_SIZE);
542321b17ecSEdward Tomasz Napierala 
543321b17ecSEdward Tomasz Napierala 	/*
544321b17ecSEdward Tomasz Napierala 	 * Note that ip_data_mbuf also contains padding; since digest
545321b17ecSEdward Tomasz Napierala 	 * calculation is supposed to include that, we iterate over
546321b17ecSEdward Tomasz Napierala 	 * the entire ip_data_mbuf chain, not just ip_data_len bytes of it.
547321b17ecSEdward Tomasz Napierala 	 */
548530e725dSJohn Baldwin 	valid_digest = icl_mbuf_to_crc32c(request->ip_data_mbuf,
549530e725dSJohn Baldwin 	    roundup2(request->ip_data_len, 4));
550321b17ecSEdward Tomasz Napierala 	if (received_digest != valid_digest) {
551321b17ecSEdward Tomasz Napierala 		ICL_WARN("data digest check failed; got 0x%x, "
552321b17ecSEdward Tomasz Napierala 		    "should be 0x%x", received_digest, valid_digest);
553321b17ecSEdward Tomasz Napierala 		return (-1);
554321b17ecSEdward Tomasz Napierala 	}
555321b17ecSEdward Tomasz Napierala 
556321b17ecSEdward Tomasz Napierala 	return (0);
557321b17ecSEdward Tomasz Napierala }
558321b17ecSEdward Tomasz Napierala 
559321b17ecSEdward Tomasz Napierala /*
560321b17ecSEdward Tomasz Napierala  * Somewhat contrary to the name, this attempts to receive only one
561321b17ecSEdward Tomasz Napierala  * "part" of PDU at a time; call it repeatedly until it returns non-NULL.
562321b17ecSEdward Tomasz Napierala  */
563321b17ecSEdward Tomasz Napierala static struct icl_pdu *
icl_conn_receive_pdu(struct icl_soft_conn * isc,struct mbuf ** r,size_t * rs)56487322a90SJohn Baldwin icl_conn_receive_pdu(struct icl_soft_conn *isc, struct mbuf **r, size_t *rs)
565321b17ecSEdward Tomasz Napierala {
56687322a90SJohn Baldwin 	struct icl_conn *ic = &isc->ic;
567321b17ecSEdward Tomasz Napierala 	struct icl_pdu *request;
568321b17ecSEdward Tomasz Napierala 	size_t len;
5696895f89fSAlexander Motin 	int error = 0;
570321b17ecSEdward Tomasz Napierala 	bool more_needed;
571321b17ecSEdward Tomasz Napierala 
57287322a90SJohn Baldwin 	if (isc->receive_state == ICL_CONN_STATE_BHS) {
57387322a90SJohn Baldwin 		KASSERT(isc->receive_pdu == NULL,
57487322a90SJohn Baldwin 		    ("isc->receive_pdu != NULL"));
575d0d587c7SAlexander Motin 		request = icl_soft_conn_new_pdu(ic, M_NOWAIT);
576321b17ecSEdward Tomasz Napierala 		if (request == NULL) {
577321b17ecSEdward Tomasz Napierala 			ICL_DEBUG("failed to allocate PDU; "
578321b17ecSEdward Tomasz Napierala 			    "dropping connection");
579321b17ecSEdward Tomasz Napierala 			icl_conn_fail(ic);
580321b17ecSEdward Tomasz Napierala 			return (NULL);
581321b17ecSEdward Tomasz Napierala 		}
58287322a90SJohn Baldwin 		isc->receive_pdu = request;
583321b17ecSEdward Tomasz Napierala 	} else {
58487322a90SJohn Baldwin 		KASSERT(isc->receive_pdu != NULL,
58587322a90SJohn Baldwin 		    ("isc->receive_pdu == NULL"));
58687322a90SJohn Baldwin 		request = isc->receive_pdu;
587321b17ecSEdward Tomasz Napierala 	}
588321b17ecSEdward Tomasz Napierala 
58987322a90SJohn Baldwin 	switch (isc->receive_state) {
590321b17ecSEdward Tomasz Napierala 	case ICL_CONN_STATE_BHS:
591321b17ecSEdward Tomasz Napierala 		//ICL_DEBUG("receiving BHS");
5926895f89fSAlexander Motin 		icl_soft_receive_buf(r, rs, request->ip_bhs,
5936895f89fSAlexander Motin 		    sizeof(struct iscsi_bhs));
594321b17ecSEdward Tomasz Napierala 
595321b17ecSEdward Tomasz Napierala 		/*
596321b17ecSEdward Tomasz Napierala 		 * We don't enforce any limit for AHS length;
597321b17ecSEdward Tomasz Napierala 		 * its length is stored in 8 bit field.
598321b17ecSEdward Tomasz Napierala 		 */
599321b17ecSEdward Tomasz Napierala 
600321b17ecSEdward Tomasz Napierala 		len = icl_pdu_data_segment_length(request);
6010cc7d64aSJohn Baldwin 		if (len > ic->ic_max_recv_data_segment_length) {
602321b17ecSEdward Tomasz Napierala 			ICL_WARN("received data segment "
603b75168edSAlexander Motin 			    "length %zd is larger than negotiated; "
604b75168edSAlexander Motin 			    "dropping connection", len);
605321b17ecSEdward Tomasz Napierala 			error = EINVAL;
606321b17ecSEdward Tomasz Napierala 			break;
607321b17ecSEdward Tomasz Napierala 		}
608321b17ecSEdward Tomasz Napierala 
60987322a90SJohn Baldwin 		isc->receive_state = ICL_CONN_STATE_AHS;
61087322a90SJohn Baldwin 		isc->receive_len = icl_pdu_ahs_length(request);
611321b17ecSEdward Tomasz Napierala 		break;
612321b17ecSEdward Tomasz Napierala 
613321b17ecSEdward Tomasz Napierala 	case ICL_CONN_STATE_AHS:
614321b17ecSEdward Tomasz Napierala 		//ICL_DEBUG("receiving AHS");
6156895f89fSAlexander Motin 		icl_pdu_receive_ahs(request, r, rs);
61687322a90SJohn Baldwin 		isc->receive_state = ICL_CONN_STATE_HEADER_DIGEST;
617321b17ecSEdward Tomasz Napierala 		if (ic->ic_header_crc32c == false)
61887322a90SJohn Baldwin 			isc->receive_len = 0;
619321b17ecSEdward Tomasz Napierala 		else
62087322a90SJohn Baldwin 			isc->receive_len = ISCSI_HEADER_DIGEST_SIZE;
621321b17ecSEdward Tomasz Napierala 		break;
622321b17ecSEdward Tomasz Napierala 
623321b17ecSEdward Tomasz Napierala 	case ICL_CONN_STATE_HEADER_DIGEST:
624321b17ecSEdward Tomasz Napierala 		//ICL_DEBUG("receiving header digest");
6256895f89fSAlexander Motin 		error = icl_pdu_check_header_digest(request, r, rs);
626321b17ecSEdward Tomasz Napierala 		if (error != 0) {
627321b17ecSEdward Tomasz Napierala 			ICL_DEBUG("header digest failed; "
628321b17ecSEdward Tomasz Napierala 			    "dropping connection");
629321b17ecSEdward Tomasz Napierala 			break;
630321b17ecSEdward Tomasz Napierala 		}
631321b17ecSEdward Tomasz Napierala 
63287322a90SJohn Baldwin 		isc->receive_state = ICL_CONN_STATE_DATA;
63387322a90SJohn Baldwin 		isc->receive_len = icl_pdu_data_segment_receive_len(request);
634321b17ecSEdward Tomasz Napierala 		break;
635321b17ecSEdward Tomasz Napierala 
636321b17ecSEdward Tomasz Napierala 	case ICL_CONN_STATE_DATA:
637321b17ecSEdward Tomasz Napierala 		//ICL_DEBUG("receiving data segment");
6386895f89fSAlexander Motin 		error = icl_pdu_receive_data_segment(request, r, rs,
639321b17ecSEdward Tomasz Napierala 		    &more_needed);
640321b17ecSEdward Tomasz Napierala 		if (error != 0) {
641321b17ecSEdward Tomasz Napierala 			ICL_DEBUG("failed to receive data segment;"
642321b17ecSEdward Tomasz Napierala 			    "dropping connection");
643321b17ecSEdward Tomasz Napierala 			break;
644321b17ecSEdward Tomasz Napierala 		}
645321b17ecSEdward Tomasz Napierala 
646321b17ecSEdward Tomasz Napierala 		if (more_needed)
647321b17ecSEdward Tomasz Napierala 			break;
648321b17ecSEdward Tomasz Napierala 
64987322a90SJohn Baldwin 		isc->receive_state = ICL_CONN_STATE_DATA_DIGEST;
650321b17ecSEdward Tomasz Napierala 		if (request->ip_data_len == 0 || ic->ic_data_crc32c == false)
65187322a90SJohn Baldwin 			isc->receive_len = 0;
652321b17ecSEdward Tomasz Napierala 		else
65387322a90SJohn Baldwin 			isc->receive_len = ISCSI_DATA_DIGEST_SIZE;
654321b17ecSEdward Tomasz Napierala 		break;
655321b17ecSEdward Tomasz Napierala 
656321b17ecSEdward Tomasz Napierala 	case ICL_CONN_STATE_DATA_DIGEST:
657321b17ecSEdward Tomasz Napierala 		//ICL_DEBUG("receiving data digest");
6586895f89fSAlexander Motin 		error = icl_pdu_check_data_digest(request, r, rs);
659321b17ecSEdward Tomasz Napierala 		if (error != 0) {
660321b17ecSEdward Tomasz Napierala 			ICL_DEBUG("data digest failed; "
661321b17ecSEdward Tomasz Napierala 			    "dropping connection");
662321b17ecSEdward Tomasz Napierala 			break;
663321b17ecSEdward Tomasz Napierala 		}
664321b17ecSEdward Tomasz Napierala 
665321b17ecSEdward Tomasz Napierala 		/*
666321b17ecSEdward Tomasz Napierala 		 * We've received complete PDU; reset the receive state machine
667321b17ecSEdward Tomasz Napierala 		 * and return the PDU.
668321b17ecSEdward Tomasz Napierala 		 */
66987322a90SJohn Baldwin 		isc->receive_state = ICL_CONN_STATE_BHS;
67087322a90SJohn Baldwin 		isc->receive_len = sizeof(struct iscsi_bhs);
67187322a90SJohn Baldwin 		isc->receive_pdu = NULL;
672321b17ecSEdward Tomasz Napierala 		return (request);
673321b17ecSEdward Tomasz Napierala 
674321b17ecSEdward Tomasz Napierala 	default:
67587322a90SJohn Baldwin 		panic("invalid receive_state %d\n", isc->receive_state);
676321b17ecSEdward Tomasz Napierala 	}
677321b17ecSEdward Tomasz Napierala 
678321b17ecSEdward Tomasz Napierala 	if (error != 0) {
679321b17ecSEdward Tomasz Napierala 		/*
68087322a90SJohn Baldwin 		 * Don't free the PDU; it's pointed to by isc->receive_pdu
6815aabcd7cSEdward Tomasz Napierala 		 * and will get freed in icl_soft_conn_close().
682321b17ecSEdward Tomasz Napierala 		 */
683321b17ecSEdward Tomasz Napierala 		icl_conn_fail(ic);
684321b17ecSEdward Tomasz Napierala 	}
685321b17ecSEdward Tomasz Napierala 
686321b17ecSEdward Tomasz Napierala 	return (NULL);
687321b17ecSEdward Tomasz Napierala }
688321b17ecSEdward Tomasz Napierala 
689321b17ecSEdward Tomasz Napierala static void
icl_conn_receive_pdus(struct icl_soft_conn * isc,struct mbuf ** r,size_t * rs)69087322a90SJohn Baldwin icl_conn_receive_pdus(struct icl_soft_conn *isc, struct mbuf **r, size_t *rs)
691321b17ecSEdward Tomasz Napierala {
69287322a90SJohn Baldwin 	struct icl_conn *ic = &isc->ic;
693321b17ecSEdward Tomasz Napierala 	struct icl_pdu *response;
694321b17ecSEdward Tomasz Napierala 
695321b17ecSEdward Tomasz Napierala 	for (;;) {
696321b17ecSEdward Tomasz Napierala 		if (ic->ic_disconnecting)
697321b17ecSEdward Tomasz Napierala 			return;
698321b17ecSEdward Tomasz Napierala 
699321b17ecSEdward Tomasz Napierala 		/*
700321b17ecSEdward Tomasz Napierala 		 * Loop until we have a complete PDU or there is not enough
701321b17ecSEdward Tomasz Napierala 		 * data in the socket buffer.
702321b17ecSEdward Tomasz Napierala 		 */
70387322a90SJohn Baldwin 		if (*rs < isc->receive_len) {
704321b17ecSEdward Tomasz Napierala #if 0
7056895f89fSAlexander Motin 			ICL_DEBUG("not enough data; have %zd, need %zd",
70687322a90SJohn Baldwin 			    *rs, isc->receive_len);
707321b17ecSEdward Tomasz Napierala #endif
708321b17ecSEdward Tomasz Napierala 			return;
709321b17ecSEdward Tomasz Napierala 		}
710321b17ecSEdward Tomasz Napierala 
71187322a90SJohn Baldwin 		response = icl_conn_receive_pdu(isc, r, rs);
712321b17ecSEdward Tomasz Napierala 		if (response == NULL)
713321b17ecSEdward Tomasz Napierala 			continue;
714321b17ecSEdward Tomasz Napierala 
715321b17ecSEdward Tomasz Napierala 		if (response->ip_ahs_len > 0) {
716321b17ecSEdward Tomasz Napierala 			ICL_WARN("received PDU with unsupported "
717321b17ecSEdward Tomasz Napierala 			    "AHS; opcode 0x%x; dropping connection",
718321b17ecSEdward Tomasz Napierala 			    response->ip_bhs->bhs_opcode);
71982f7fa7aSAlexander Motin 			icl_soft_conn_pdu_free(ic, response);
720321b17ecSEdward Tomasz Napierala 			icl_conn_fail(ic);
721321b17ecSEdward Tomasz Napierala 			return;
722321b17ecSEdward Tomasz Napierala 		}
723321b17ecSEdward Tomasz Napierala 
724321b17ecSEdward Tomasz Napierala 		(ic->ic_receive)(response);
725321b17ecSEdward Tomasz Napierala 	}
726321b17ecSEdward Tomasz Napierala }
727321b17ecSEdward Tomasz Napierala 
728321b17ecSEdward Tomasz Napierala static void
icl_receive_thread(void * arg)729321b17ecSEdward Tomasz Napierala icl_receive_thread(void *arg)
730321b17ecSEdward Tomasz Napierala {
73187322a90SJohn Baldwin 	struct icl_soft_conn *isc = arg;
73287322a90SJohn Baldwin 	struct icl_conn *ic = &isc->ic;
7336895f89fSAlexander Motin 	size_t available, read = 0;
734321b17ecSEdward Tomasz Napierala 	struct socket *so;
7356895f89fSAlexander Motin 	struct mbuf *m, *r = NULL;
7366895f89fSAlexander Motin 	struct uio uio;
7376895f89fSAlexander Motin 	int error, flags;
738321b17ecSEdward Tomasz Napierala 
739321b17ecSEdward Tomasz Napierala 	so = ic->ic_socket;
740321b17ecSEdward Tomasz Napierala 
741321b17ecSEdward Tomasz Napierala 	for (;;) {
7426895f89fSAlexander Motin 		SOCKBUF_LOCK(&so->so_rcv);
743321b17ecSEdward Tomasz Napierala 		if (ic->ic_disconnecting) {
7446895f89fSAlexander Motin 			SOCKBUF_UNLOCK(&so->so_rcv);
745321b17ecSEdward Tomasz Napierala 			break;
746321b17ecSEdward Tomasz Napierala 		}
747321b17ecSEdward Tomasz Napierala 
748321b17ecSEdward Tomasz Napierala 		/*
749321b17ecSEdward Tomasz Napierala 		 * Set the low watermark, to be checked by
750321b17ecSEdward Tomasz Napierala 		 * soreadable() in icl_soupcall_receive()
751266078c6SPedro F. Giffuni 		 * to avoid unnecessary wakeups until there
752321b17ecSEdward Tomasz Napierala 		 * is enough data received to read the PDU.
753321b17ecSEdward Tomasz Napierala 		 */
754321b17ecSEdward Tomasz Napierala 		available = sbavail(&so->so_rcv);
75587322a90SJohn Baldwin 		if (read + available < isc->receive_len) {
75687322a90SJohn Baldwin 			so->so_rcv.sb_lowat = isc->receive_len - read;
75787322a90SJohn Baldwin 			cv_wait(&isc->receive_cv, SOCKBUF_MTX(&so->so_rcv));
758321b17ecSEdward Tomasz Napierala 			so->so_rcv.sb_lowat = so->so_rcv.sb_hiwat + 1;
7596895f89fSAlexander Motin 			available = sbavail(&so->so_rcv);
7606895f89fSAlexander Motin 		}
761321b17ecSEdward Tomasz Napierala 		SOCKBUF_UNLOCK(&so->so_rcv);
762321b17ecSEdward Tomasz Napierala 
7636895f89fSAlexander Motin 		if (available == 0) {
7646895f89fSAlexander Motin 			if (so->so_error != 0) {
7656895f89fSAlexander Motin 				ICL_DEBUG("connection error %d; "
7666895f89fSAlexander Motin 				    "dropping connection", so->so_error);
7676895f89fSAlexander Motin 				icl_conn_fail(ic);
7686895f89fSAlexander Motin 				break;
769321b17ecSEdward Tomasz Napierala 			}
7706895f89fSAlexander Motin 			continue;
7716895f89fSAlexander Motin 		}
7726895f89fSAlexander Motin 
7736895f89fSAlexander Motin 		memset(&uio, 0, sizeof(uio));
7746895f89fSAlexander Motin 		uio.uio_resid = available;
7756895f89fSAlexander Motin 		flags = MSG_DONTWAIT;
7766895f89fSAlexander Motin 		error = soreceive(so, NULL, &uio, &m, NULL, &flags);
7776895f89fSAlexander Motin 		if (error != 0) {
7786895f89fSAlexander Motin 			ICL_DEBUG("soreceive error %d", error);
7796895f89fSAlexander Motin 			break;
7806895f89fSAlexander Motin 		}
7816895f89fSAlexander Motin 		if (uio.uio_resid != 0) {
7826895f89fSAlexander Motin 			m_freem(m);
7836895f89fSAlexander Motin 			ICL_DEBUG("short read");
7846895f89fSAlexander Motin 			break;
7856895f89fSAlexander Motin 		}
7866895f89fSAlexander Motin 		if (r)
7876895f89fSAlexander Motin 			m_cat(r, m);
7886895f89fSAlexander Motin 		else
7896895f89fSAlexander Motin 			r = m;
7906895f89fSAlexander Motin 		read += available;
7916895f89fSAlexander Motin 
79287322a90SJohn Baldwin 		icl_conn_receive_pdus(isc, &r, &read);
7936895f89fSAlexander Motin 	}
7946895f89fSAlexander Motin 
7956895f89fSAlexander Motin 	if (r)
7966895f89fSAlexander Motin 		m_freem(r);
797321b17ecSEdward Tomasz Napierala 
798321b17ecSEdward Tomasz Napierala 	ICL_CONN_LOCK(ic);
79987322a90SJohn Baldwin 	isc->receive_running = false;
80087322a90SJohn Baldwin 	cv_signal(&isc->send_cv);
801321b17ecSEdward Tomasz Napierala 	ICL_CONN_UNLOCK(ic);
802321b17ecSEdward Tomasz Napierala 	kthread_exit();
803321b17ecSEdward Tomasz Napierala }
804321b17ecSEdward Tomasz Napierala 
805321b17ecSEdward Tomasz Napierala static int
icl_soupcall_receive(struct socket * so,void * arg,int waitflag)806321b17ecSEdward Tomasz Napierala icl_soupcall_receive(struct socket *so, void *arg, int waitflag)
807321b17ecSEdward Tomasz Napierala {
80887322a90SJohn Baldwin 	struct icl_soft_conn *isc;
809321b17ecSEdward Tomasz Napierala 
810321b17ecSEdward Tomasz Napierala 	if (!soreadable(so))
811321b17ecSEdward Tomasz Napierala 		return (SU_OK);
812321b17ecSEdward Tomasz Napierala 
81387322a90SJohn Baldwin 	isc = arg;
81487322a90SJohn Baldwin 	cv_signal(&isc->receive_cv);
815321b17ecSEdward Tomasz Napierala 	return (SU_OK);
816321b17ecSEdward Tomasz Napierala }
817321b17ecSEdward Tomasz Napierala 
818321b17ecSEdward Tomasz Napierala static int
icl_pdu_finalize(struct icl_pdu * request)819321b17ecSEdward Tomasz Napierala icl_pdu_finalize(struct icl_pdu *request)
820321b17ecSEdward Tomasz Napierala {
821321b17ecSEdward Tomasz Napierala 	size_t padding, pdu_len;
822321b17ecSEdward Tomasz Napierala 	uint32_t digest, zero = 0;
823321b17ecSEdward Tomasz Napierala 	int ok;
824321b17ecSEdward Tomasz Napierala 	struct icl_conn *ic;
825321b17ecSEdward Tomasz Napierala 
826321b17ecSEdward Tomasz Napierala 	ic = request->ip_conn;
827321b17ecSEdward Tomasz Napierala 
828321b17ecSEdward Tomasz Napierala 	icl_pdu_set_data_segment_length(request, request->ip_data_len);
829321b17ecSEdward Tomasz Napierala 
830321b17ecSEdward Tomasz Napierala 	pdu_len = icl_pdu_size(request);
831321b17ecSEdward Tomasz Napierala 
832321b17ecSEdward Tomasz Napierala 	if (ic->ic_header_crc32c) {
833530e725dSJohn Baldwin 		digest = icl_mbuf_to_crc32c(request->ip_bhs_mbuf,
834530e725dSJohn Baldwin 		    ISCSI_BHS_SIZE);
835321b17ecSEdward Tomasz Napierala 		ok = m_append(request->ip_bhs_mbuf, sizeof(digest),
836321b17ecSEdward Tomasz Napierala 		    (void *)&digest);
837321b17ecSEdward Tomasz Napierala 		if (ok != 1) {
838321b17ecSEdward Tomasz Napierala 			ICL_WARN("failed to append header digest");
839321b17ecSEdward Tomasz Napierala 			return (1);
840321b17ecSEdward Tomasz Napierala 		}
841321b17ecSEdward Tomasz Napierala 	}
842321b17ecSEdward Tomasz Napierala 
843321b17ecSEdward Tomasz Napierala 	if (request->ip_data_len != 0) {
844321b17ecSEdward Tomasz Napierala 		padding = icl_pdu_padding(request);
845321b17ecSEdward Tomasz Napierala 		if (padding > 0) {
846321b17ecSEdward Tomasz Napierala 			ok = m_append(request->ip_data_mbuf, padding,
847321b17ecSEdward Tomasz Napierala 			    (void *)&zero);
848321b17ecSEdward Tomasz Napierala 			if (ok != 1) {
849321b17ecSEdward Tomasz Napierala 				ICL_WARN("failed to append padding");
850321b17ecSEdward Tomasz Napierala 				return (1);
851321b17ecSEdward Tomasz Napierala 			}
852321b17ecSEdward Tomasz Napierala 		}
853321b17ecSEdward Tomasz Napierala 
854321b17ecSEdward Tomasz Napierala 		if (ic->ic_data_crc32c) {
855530e725dSJohn Baldwin 			digest = icl_mbuf_to_crc32c(request->ip_data_mbuf,
856530e725dSJohn Baldwin 			    roundup2(request->ip_data_len, 4));
857321b17ecSEdward Tomasz Napierala 
858321b17ecSEdward Tomasz Napierala 			ok = m_append(request->ip_data_mbuf, sizeof(digest),
859321b17ecSEdward Tomasz Napierala 			    (void *)&digest);
860321b17ecSEdward Tomasz Napierala 			if (ok != 1) {
861321b17ecSEdward Tomasz Napierala 				ICL_WARN("failed to append data digest");
862321b17ecSEdward Tomasz Napierala 				return (1);
863321b17ecSEdward Tomasz Napierala 			}
864321b17ecSEdward Tomasz Napierala 		}
865321b17ecSEdward Tomasz Napierala 
866321b17ecSEdward Tomasz Napierala 		m_cat(request->ip_bhs_mbuf, request->ip_data_mbuf);
867321b17ecSEdward Tomasz Napierala 		request->ip_data_mbuf = NULL;
868321b17ecSEdward Tomasz Napierala 	}
869321b17ecSEdward Tomasz Napierala 
870321b17ecSEdward Tomasz Napierala 	request->ip_bhs_mbuf->m_pkthdr.len = pdu_len;
871321b17ecSEdward Tomasz Napierala 
872321b17ecSEdward Tomasz Napierala 	return (0);
873321b17ecSEdward Tomasz Napierala }
874321b17ecSEdward Tomasz Napierala 
875321b17ecSEdward Tomasz Napierala static void
icl_conn_send_pdus(struct icl_soft_conn * isc,struct icl_pdu_stailq * queue)87687322a90SJohn Baldwin icl_conn_send_pdus(struct icl_soft_conn *isc, struct icl_pdu_stailq *queue)
877321b17ecSEdward Tomasz Napierala {
87887322a90SJohn Baldwin 	struct icl_conn *ic = &isc->ic;
879321b17ecSEdward Tomasz Napierala 	struct icl_pdu *request, *request2;
880b85a67f5SAlexander Motin 	struct mbuf *m;
881321b17ecSEdward Tomasz Napierala 	struct socket *so;
882605703b5SAlexander Motin 	long available, size, size2;
883f4f84701SDimitry Andric #ifdef DEBUG_COALESCED
884f4f84701SDimitry Andric 	int coalesced;
885f4f84701SDimitry Andric #endif
886f4f84701SDimitry Andric 	int error;
887321b17ecSEdward Tomasz Napierala 
888321b17ecSEdward Tomasz Napierala 	ICL_CONN_LOCK_ASSERT_NOT(ic);
889321b17ecSEdward Tomasz Napierala 
890321b17ecSEdward Tomasz Napierala 	so = ic->ic_socket;
891321b17ecSEdward Tomasz Napierala 
892321b17ecSEdward Tomasz Napierala 	SOCKBUF_LOCK(&so->so_snd);
893321b17ecSEdward Tomasz Napierala 	/*
894321b17ecSEdward Tomasz Napierala 	 * Check how much space do we have for transmit.  We can't just
895321b17ecSEdward Tomasz Napierala 	 * call sosend() and retry when we get EWOULDBLOCK or EMSGSIZE,
896321b17ecSEdward Tomasz Napierala 	 * as it always frees the mbuf chain passed to it, even in case
897321b17ecSEdward Tomasz Napierala 	 * of error.
898321b17ecSEdward Tomasz Napierala 	 */
899321b17ecSEdward Tomasz Napierala 	available = sbspace(&so->so_snd);
90087322a90SJohn Baldwin 	isc->check_send_space = false;
901321b17ecSEdward Tomasz Napierala 
902321b17ecSEdward Tomasz Napierala 	/*
903321b17ecSEdward Tomasz Napierala 	 * Notify the socket upcall that we don't need wakeups
904321b17ecSEdward Tomasz Napierala 	 * for the time being.
905321b17ecSEdward Tomasz Napierala 	 */
906321b17ecSEdward Tomasz Napierala 	so->so_snd.sb_lowat = so->so_snd.sb_hiwat + 1;
907321b17ecSEdward Tomasz Napierala 	SOCKBUF_UNLOCK(&so->so_snd);
908321b17ecSEdward Tomasz Napierala 
909321b17ecSEdward Tomasz Napierala 	while (!STAILQ_EMPTY(queue)) {
910321b17ecSEdward Tomasz Napierala 		request = STAILQ_FIRST(queue);
911321b17ecSEdward Tomasz Napierala 		size = icl_pdu_size(request);
912321b17ecSEdward Tomasz Napierala 		if (available < size) {
913321b17ecSEdward Tomasz Napierala 			/*
914321b17ecSEdward Tomasz Napierala 			 * Set the low watermark, to be checked by
915321b17ecSEdward Tomasz Napierala 			 * sowriteable() in icl_soupcall_send()
916266078c6SPedro F. Giffuni 			 * to avoid unnecessary wakeups until there
917321b17ecSEdward Tomasz Napierala 			 * is enough space for the PDU to fit.
918321b17ecSEdward Tomasz Napierala 			 */
919321b17ecSEdward Tomasz Napierala 			SOCKBUF_LOCK(&so->so_snd);
920321b17ecSEdward Tomasz Napierala 			available = sbspace(&so->so_snd);
921321b17ecSEdward Tomasz Napierala 			if (available < size) {
922321b17ecSEdward Tomasz Napierala #if 1
923321b17ecSEdward Tomasz Napierala 				ICL_DEBUG("no space to send; "
924605703b5SAlexander Motin 				    "have %ld, need %ld",
925321b17ecSEdward Tomasz Napierala 				    available, size);
926321b17ecSEdward Tomasz Napierala #endif
9271f29b46cSAlexander Motin 				so->so_snd.sb_lowat = max(size,
9281f29b46cSAlexander Motin 				    so->so_snd.sb_hiwat / 8);
929321b17ecSEdward Tomasz Napierala 				SOCKBUF_UNLOCK(&so->so_snd);
930321b17ecSEdward Tomasz Napierala 				return;
931321b17ecSEdward Tomasz Napierala 			}
932321b17ecSEdward Tomasz Napierala 			SOCKBUF_UNLOCK(&so->so_snd);
933321b17ecSEdward Tomasz Napierala 		}
934321b17ecSEdward Tomasz Napierala 		STAILQ_REMOVE_HEAD(queue, ip_next);
935321b17ecSEdward Tomasz Napierala 		error = icl_pdu_finalize(request);
936321b17ecSEdward Tomasz Napierala 		if (error != 0) {
937321b17ecSEdward Tomasz Napierala 			ICL_DEBUG("failed to finalize PDU; "
938321b17ecSEdward Tomasz Napierala 			    "dropping connection");
9399a4510acSAlexander Motin 			icl_soft_pdu_done(request, EIO);
940321b17ecSEdward Tomasz Napierala 			icl_conn_fail(ic);
941321b17ecSEdward Tomasz Napierala 			return;
942321b17ecSEdward Tomasz Napierala 		}
943321b17ecSEdward Tomasz Napierala 		if (coalesce) {
944b85a67f5SAlexander Motin 			m = request->ip_bhs_mbuf;
945f4f84701SDimitry Andric 			for (
946f4f84701SDimitry Andric #ifdef DEBUG_COALESCED
947f4f84701SDimitry Andric 			    coalesced = 1
948f4f84701SDimitry Andric #endif
949f4f84701SDimitry Andric 			    ; ;
950f4f84701SDimitry Andric #ifdef DEBUG_COALESCED
951f4f84701SDimitry Andric 			    coalesced++
952f4f84701SDimitry Andric #endif
953f4f84701SDimitry Andric 			    ) {
954321b17ecSEdward Tomasz Napierala 				request2 = STAILQ_FIRST(queue);
955321b17ecSEdward Tomasz Napierala 				if (request2 == NULL)
956321b17ecSEdward Tomasz Napierala 					break;
957321b17ecSEdward Tomasz Napierala 				size2 = icl_pdu_size(request2);
958321b17ecSEdward Tomasz Napierala 				if (available < size + size2)
959321b17ecSEdward Tomasz Napierala 					break;
960321b17ecSEdward Tomasz Napierala 				STAILQ_REMOVE_HEAD(queue, ip_next);
961321b17ecSEdward Tomasz Napierala 				error = icl_pdu_finalize(request2);
962321b17ecSEdward Tomasz Napierala 				if (error != 0) {
963321b17ecSEdward Tomasz Napierala 					ICL_DEBUG("failed to finalize PDU; "
964321b17ecSEdward Tomasz Napierala 					    "dropping connection");
9659a4510acSAlexander Motin 					icl_soft_pdu_done(request, EIO);
9669a4510acSAlexander Motin 					icl_soft_pdu_done(request2, EIO);
967321b17ecSEdward Tomasz Napierala 					icl_conn_fail(ic);
968321b17ecSEdward Tomasz Napierala 					return;
969321b17ecSEdward Tomasz Napierala 				}
970b85a67f5SAlexander Motin 				while (m->m_next)
971b85a67f5SAlexander Motin 					m = m->m_next;
972b85a67f5SAlexander Motin 				m_cat(m, request2->ip_bhs_mbuf);
973321b17ecSEdward Tomasz Napierala 				request2->ip_bhs_mbuf = NULL;
974321b17ecSEdward Tomasz Napierala 				request->ip_bhs_mbuf->m_pkthdr.len += size2;
975321b17ecSEdward Tomasz Napierala 				size += size2;
9769a4510acSAlexander Motin 				icl_soft_pdu_done(request2, 0);
977321b17ecSEdward Tomasz Napierala 			}
978f4f84701SDimitry Andric #ifdef DEBUG_COALESCED
979321b17ecSEdward Tomasz Napierala 			if (coalesced > 1) {
980605703b5SAlexander Motin 				ICL_DEBUG("coalesced %d PDUs into %ld bytes",
981321b17ecSEdward Tomasz Napierala 				    coalesced, size);
982321b17ecSEdward Tomasz Napierala 			}
983321b17ecSEdward Tomasz Napierala #endif
984321b17ecSEdward Tomasz Napierala 		}
985321b17ecSEdward Tomasz Napierala 		available -= size;
986321b17ecSEdward Tomasz Napierala 		error = sosend(so, NULL, NULL, request->ip_bhs_mbuf,
987321b17ecSEdward Tomasz Napierala 		    NULL, MSG_DONTWAIT, curthread);
988321b17ecSEdward Tomasz Napierala 		request->ip_bhs_mbuf = NULL; /* Sosend consumes the mbuf. */
989321b17ecSEdward Tomasz Napierala 		if (error != 0) {
990321b17ecSEdward Tomasz Napierala 			ICL_DEBUG("failed to send PDU, error %d; "
991321b17ecSEdward Tomasz Napierala 			    "dropping connection", error);
9929a4510acSAlexander Motin 			icl_soft_pdu_done(request, error);
993321b17ecSEdward Tomasz Napierala 			icl_conn_fail(ic);
994321b17ecSEdward Tomasz Napierala 			return;
995321b17ecSEdward Tomasz Napierala 		}
9969a4510acSAlexander Motin 		icl_soft_pdu_done(request, 0);
997321b17ecSEdward Tomasz Napierala 	}
998321b17ecSEdward Tomasz Napierala }
999321b17ecSEdward Tomasz Napierala 
1000321b17ecSEdward Tomasz Napierala static void
icl_send_thread(void * arg)1001321b17ecSEdward Tomasz Napierala icl_send_thread(void *arg)
1002321b17ecSEdward Tomasz Napierala {
100387322a90SJohn Baldwin 	struct icl_soft_conn *isc;
1004321b17ecSEdward Tomasz Napierala 	struct icl_conn *ic;
1005321b17ecSEdward Tomasz Napierala 	struct icl_pdu_stailq queue;
1006321b17ecSEdward Tomasz Napierala 
100787322a90SJohn Baldwin 	isc = arg;
100887322a90SJohn Baldwin 	ic = &isc->ic;
1009321b17ecSEdward Tomasz Napierala 
1010321b17ecSEdward Tomasz Napierala 	STAILQ_INIT(&queue);
1011321b17ecSEdward Tomasz Napierala 
1012321b17ecSEdward Tomasz Napierala 	ICL_CONN_LOCK(ic);
1013321b17ecSEdward Tomasz Napierala 	for (;;) {
1014321b17ecSEdward Tomasz Napierala 		for (;;) {
1015321b17ecSEdward Tomasz Napierala 			/*
1016df3747c6SAlexander Motin 			 * Populate the local queue from the main one.
1017df3747c6SAlexander Motin 			 * This way the icl_conn_send_pdus() can go through
1018df3747c6SAlexander Motin 			 * all the queued PDUs without holding any locks.
1019321b17ecSEdward Tomasz Napierala 			 */
102087322a90SJohn Baldwin 			if (STAILQ_EMPTY(&queue) || isc->check_send_space)
102187322a90SJohn Baldwin 				STAILQ_CONCAT(&queue, &isc->to_send);
1022321b17ecSEdward Tomasz Napierala 
1023321b17ecSEdward Tomasz Napierala 			ICL_CONN_UNLOCK(ic);
102487322a90SJohn Baldwin 			icl_conn_send_pdus(isc, &queue);
1025321b17ecSEdward Tomasz Napierala 			ICL_CONN_LOCK(ic);
1026321b17ecSEdward Tomasz Napierala 
1027321b17ecSEdward Tomasz Napierala 			/*
1028321b17ecSEdward Tomasz Napierala 			 * The icl_soupcall_send() was called since the last
1029321b17ecSEdward Tomasz Napierala 			 * call to sbspace(); go around;
1030321b17ecSEdward Tomasz Napierala 			 */
103187322a90SJohn Baldwin 			if (isc->check_send_space)
1032321b17ecSEdward Tomasz Napierala 				continue;
1033321b17ecSEdward Tomasz Napierala 
1034321b17ecSEdward Tomasz Napierala 			/*
1035321b17ecSEdward Tomasz Napierala 			 * Local queue is empty, but we still have PDUs
1036321b17ecSEdward Tomasz Napierala 			 * in the main one; go around.
1037321b17ecSEdward Tomasz Napierala 			 */
1038321b17ecSEdward Tomasz Napierala 			if (STAILQ_EMPTY(&queue) &&
103987322a90SJohn Baldwin 			    !STAILQ_EMPTY(&isc->to_send))
1040321b17ecSEdward Tomasz Napierala 				continue;
1041321b17ecSEdward Tomasz Napierala 
1042321b17ecSEdward Tomasz Napierala 			/*
1043321b17ecSEdward Tomasz Napierala 			 * There might be some stuff in the local queue,
1044321b17ecSEdward Tomasz Napierala 			 * which didn't get sent due to not having enough send
1045321b17ecSEdward Tomasz Napierala 			 * space.  Wait for socket upcall.
1046321b17ecSEdward Tomasz Napierala 			 */
1047321b17ecSEdward Tomasz Napierala 			break;
1048321b17ecSEdward Tomasz Napierala 		}
1049321b17ecSEdward Tomasz Napierala 
1050321b17ecSEdward Tomasz Napierala 		if (ic->ic_disconnecting) {
1051321b17ecSEdward Tomasz Napierala 			//ICL_DEBUG("terminating");
1052321b17ecSEdward Tomasz Napierala 			break;
1053321b17ecSEdward Tomasz Napierala 		}
1054321b17ecSEdward Tomasz Napierala 
105587322a90SJohn Baldwin 		cv_wait(&isc->send_cv, ic->ic_lock);
1056321b17ecSEdward Tomasz Napierala 	}
1057321b17ecSEdward Tomasz Napierala 
1058321b17ecSEdward Tomasz Napierala 	/*
1059321b17ecSEdward Tomasz Napierala 	 * We're exiting; move PDUs back to the main queue, so they can
1060321b17ecSEdward Tomasz Napierala 	 * get freed properly.  At this point ordering doesn't matter.
1061321b17ecSEdward Tomasz Napierala 	 */
106287322a90SJohn Baldwin 	STAILQ_CONCAT(&isc->to_send, &queue);
1063321b17ecSEdward Tomasz Napierala 
106487322a90SJohn Baldwin 	isc->send_running = false;
106587322a90SJohn Baldwin 	cv_signal(&isc->send_cv);
1066321b17ecSEdward Tomasz Napierala 	ICL_CONN_UNLOCK(ic);
1067321b17ecSEdward Tomasz Napierala 	kthread_exit();
1068321b17ecSEdward Tomasz Napierala }
1069321b17ecSEdward Tomasz Napierala 
1070321b17ecSEdward Tomasz Napierala static int
icl_soupcall_send(struct socket * so,void * arg,int waitflag)1071321b17ecSEdward Tomasz Napierala icl_soupcall_send(struct socket *so, void *arg, int waitflag)
1072321b17ecSEdward Tomasz Napierala {
107387322a90SJohn Baldwin 	struct icl_soft_conn *isc;
1074321b17ecSEdward Tomasz Napierala 	struct icl_conn *ic;
1075321b17ecSEdward Tomasz Napierala 
1076321b17ecSEdward Tomasz Napierala 	if (!sowriteable(so))
1077321b17ecSEdward Tomasz Napierala 		return (SU_OK);
1078321b17ecSEdward Tomasz Napierala 
107987322a90SJohn Baldwin 	isc = arg;
108087322a90SJohn Baldwin 	ic = &isc->ic;
1081321b17ecSEdward Tomasz Napierala 
1082321b17ecSEdward Tomasz Napierala 	ICL_CONN_LOCK(ic);
108387322a90SJohn Baldwin 	isc->check_send_space = true;
1084321b17ecSEdward Tomasz Napierala 	ICL_CONN_UNLOCK(ic);
1085321b17ecSEdward Tomasz Napierala 
108687322a90SJohn Baldwin 	cv_signal(&isc->send_cv);
1087321b17ecSEdward Tomasz Napierala 
1088321b17ecSEdward Tomasz Napierala 	return (SU_OK);
1089321b17ecSEdward Tomasz Napierala }
1090321b17ecSEdward Tomasz Napierala 
1091530e725dSJohn Baldwin static void
icl_soft_free_mext_pg(struct mbuf * m)1092530e725dSJohn Baldwin icl_soft_free_mext_pg(struct mbuf *m)
1093530e725dSJohn Baldwin {
1094530e725dSJohn Baldwin 	struct icl_soft_pdu *isp;
1095530e725dSJohn Baldwin 
1096530e725dSJohn Baldwin 	M_ASSERTEXTPG(m);
1097530e725dSJohn Baldwin 
1098530e725dSJohn Baldwin 	/*
1099530e725dSJohn Baldwin 	 * Nothing to do for the pages; they are owned by the PDU /
1100530e725dSJohn Baldwin 	 * I/O request.
1101530e725dSJohn Baldwin 	 */
1102530e725dSJohn Baldwin 
1103530e725dSJohn Baldwin 	/* Drop reference on the PDU. */
1104530e725dSJohn Baldwin 	isp = m->m_ext.ext_arg1;
1105530e725dSJohn Baldwin 	if (atomic_fetchadd_int(&isp->ref_cnt, -1) == 1)
1106530e725dSJohn Baldwin 		icl_soft_pdu_call_cb(&isp->ip);
1107530e725dSJohn Baldwin }
1108530e725dSJohn Baldwin 
1109530e725dSJohn Baldwin static int
icl_soft_conn_pdu_append_bio(struct icl_conn * ic,struct icl_pdu * request,struct bio * bp,size_t offset,size_t len,int flags)1110530e725dSJohn Baldwin icl_soft_conn_pdu_append_bio(struct icl_conn *ic, struct icl_pdu *request,
1111530e725dSJohn Baldwin     struct bio *bp, size_t offset, size_t len, int flags)
1112530e725dSJohn Baldwin {
1113530e725dSJohn Baldwin 	struct icl_soft_pdu *isp = (struct icl_soft_pdu *)request;
1114530e725dSJohn Baldwin 	struct mbuf *m, *m_tail;
1115530e725dSJohn Baldwin 	vm_offset_t vaddr;
1116530e725dSJohn Baldwin 	size_t mtodo, page_offset, todo;
1117530e725dSJohn Baldwin 	int i;
1118530e725dSJohn Baldwin 
1119530e725dSJohn Baldwin 	KASSERT(len > 0, ("len == 0"));
1120530e725dSJohn Baldwin 
1121530e725dSJohn Baldwin 	m_tail = request->ip_data_mbuf;
1122530e725dSJohn Baldwin 	if (m_tail != NULL)
1123530e725dSJohn Baldwin 		for (; m_tail->m_next != NULL; m_tail = m_tail->m_next)
1124530e725dSJohn Baldwin 			;
1125530e725dSJohn Baldwin 
1126530e725dSJohn Baldwin 	MPASS(bp->bio_flags & BIO_UNMAPPED);
1127530e725dSJohn Baldwin 	if (offset < PAGE_SIZE - bp->bio_ma_offset) {
1128530e725dSJohn Baldwin 		page_offset = bp->bio_ma_offset + offset;
1129530e725dSJohn Baldwin 		i = 0;
1130530e725dSJohn Baldwin 	} else {
1131530e725dSJohn Baldwin 		offset -= PAGE_SIZE - bp->bio_ma_offset;
1132530e725dSJohn Baldwin 		for (i = 1; offset >= PAGE_SIZE; i++)
1133530e725dSJohn Baldwin 			offset -= PAGE_SIZE;
1134530e725dSJohn Baldwin 		page_offset = offset;
1135530e725dSJohn Baldwin 	}
1136530e725dSJohn Baldwin 
1137530e725dSJohn Baldwin 	if (flags & ICL_NOCOPY) {
1138530e725dSJohn Baldwin 		m = NULL;
1139530e725dSJohn Baldwin 		while (len > 0) {
1140530e725dSJohn Baldwin 			if (m == NULL) {
1141530e725dSJohn Baldwin 				m = mb_alloc_ext_pgs(flags & ~ICL_NOCOPY,
1142*314cb279SJohn Baldwin 				    icl_soft_free_mext_pg, 0);
1143530e725dSJohn Baldwin 				if (__predict_false(m == NULL))
1144530e725dSJohn Baldwin 					return (ENOMEM);
1145530e725dSJohn Baldwin 				atomic_add_int(&isp->ref_cnt, 1);
1146530e725dSJohn Baldwin 				m->m_ext.ext_arg1 = isp;
1147530e725dSJohn Baldwin 				m->m_epg_1st_off = page_offset;
1148530e725dSJohn Baldwin 			}
1149530e725dSJohn Baldwin 
1150530e725dSJohn Baldwin 			todo = MIN(len, PAGE_SIZE - page_offset);
1151530e725dSJohn Baldwin 
1152530e725dSJohn Baldwin 			m->m_epg_pa[m->m_epg_npgs] =
1153530e725dSJohn Baldwin 			    VM_PAGE_TO_PHYS(bp->bio_ma[i]);
1154530e725dSJohn Baldwin 			m->m_epg_npgs++;
1155530e725dSJohn Baldwin 			m->m_epg_last_len = todo;
1156530e725dSJohn Baldwin 			m->m_len += todo;
1157530e725dSJohn Baldwin 			m->m_ext.ext_size += PAGE_SIZE;
1158530e725dSJohn Baldwin 			MBUF_EXT_PGS_ASSERT_SANITY(m);
1159530e725dSJohn Baldwin 
1160530e725dSJohn Baldwin 			if (m->m_epg_npgs == MBUF_PEXT_MAX_PGS) {
1161530e725dSJohn Baldwin 				if (m_tail != NULL)
1162530e725dSJohn Baldwin 					m_tail->m_next = m;
1163530e725dSJohn Baldwin 				else
1164530e725dSJohn Baldwin 					request->ip_data_mbuf = m;
1165530e725dSJohn Baldwin 				m_tail = m;
1166530e725dSJohn Baldwin 				request->ip_data_len += m->m_len;
1167530e725dSJohn Baldwin 				m = NULL;
1168530e725dSJohn Baldwin 			}
1169530e725dSJohn Baldwin 
1170530e725dSJohn Baldwin 			page_offset = 0;
1171530e725dSJohn Baldwin 			len -= todo;
1172530e725dSJohn Baldwin 			i++;
1173530e725dSJohn Baldwin 		}
1174530e725dSJohn Baldwin 
1175530e725dSJohn Baldwin 		if (m != NULL) {
1176530e725dSJohn Baldwin 			if (m_tail != NULL)
1177530e725dSJohn Baldwin 				m_tail->m_next = m;
1178530e725dSJohn Baldwin 			else
1179530e725dSJohn Baldwin 				request->ip_data_mbuf = m;
1180530e725dSJohn Baldwin 			request->ip_data_len += m->m_len;
1181530e725dSJohn Baldwin 		}
1182530e725dSJohn Baldwin 		return (0);
1183530e725dSJohn Baldwin 	}
1184530e725dSJohn Baldwin 
1185530e725dSJohn Baldwin 	m = m_getm2(NULL, len, flags, MT_DATA, 0);
1186530e725dSJohn Baldwin 	if (__predict_false(m == NULL))
1187530e725dSJohn Baldwin 		return (ENOMEM);
1188530e725dSJohn Baldwin 
1189530e725dSJohn Baldwin 	if (request->ip_data_mbuf == NULL) {
1190530e725dSJohn Baldwin 		request->ip_data_mbuf = m;
1191530e725dSJohn Baldwin 		request->ip_data_len = len;
1192530e725dSJohn Baldwin 	} else {
1193530e725dSJohn Baldwin 		m_tail->m_next = m;
1194530e725dSJohn Baldwin 		request->ip_data_len += len;
1195530e725dSJohn Baldwin 	}
1196530e725dSJohn Baldwin 
1197530e725dSJohn Baldwin 	while (len > 0) {
1198530e725dSJohn Baldwin 		todo = MIN(len, PAGE_SIZE - page_offset);
1199832acea9SJohn Baldwin 		vaddr = PHYS_TO_DMAP(VM_PAGE_TO_PHYS(bp->bio_ma[i]));
1200530e725dSJohn Baldwin 
1201530e725dSJohn Baldwin 		do {
1202530e725dSJohn Baldwin 			mtodo = min(todo, M_SIZE(m) - m->m_len);
1203530e725dSJohn Baldwin 			memcpy(mtod(m, char *) + m->m_len, (char *)vaddr +
1204530e725dSJohn Baldwin 			    page_offset, mtodo);
1205530e725dSJohn Baldwin 			m->m_len += mtodo;
1206530e725dSJohn Baldwin 			if (m->m_len == M_SIZE(m))
1207530e725dSJohn Baldwin 				m = m->m_next;
1208530e725dSJohn Baldwin 			page_offset += mtodo;
1209530e725dSJohn Baldwin 			todo -= mtodo;
1210530e725dSJohn Baldwin 		} while (todo > 0);
1211530e725dSJohn Baldwin 
1212530e725dSJohn Baldwin 		page_offset = 0;
1213530e725dSJohn Baldwin 		len -= todo;
1214530e725dSJohn Baldwin 		i++;
1215530e725dSJohn Baldwin 	}
1216530e725dSJohn Baldwin 
1217530e725dSJohn Baldwin 	return (0);
1218530e725dSJohn Baldwin }
1219530e725dSJohn Baldwin 
1220321b17ecSEdward Tomasz Napierala static int
icl_soft_conn_pdu_append_data(struct icl_conn * ic,struct icl_pdu * request,const void * addr,size_t len,int flags)122182f7fa7aSAlexander Motin icl_soft_conn_pdu_append_data(struct icl_conn *ic, struct icl_pdu *request,
122282f7fa7aSAlexander Motin     const void *addr, size_t len, int flags)
1223321b17ecSEdward Tomasz Napierala {
12249a4510acSAlexander Motin 	struct icl_soft_pdu *isp = (struct icl_soft_pdu *)request;
1225321b17ecSEdward Tomasz Napierala 	struct mbuf *mb, *newmb;
1226321b17ecSEdward Tomasz Napierala 	size_t copylen, off = 0;
1227321b17ecSEdward Tomasz Napierala 
1228321b17ecSEdward Tomasz Napierala 	KASSERT(len > 0, ("len == 0"));
1229321b17ecSEdward Tomasz Napierala 
12309a4510acSAlexander Motin 	if (flags & ICL_NOCOPY) {
12319a4510acSAlexander Motin 		newmb = m_get(flags & ~ICL_NOCOPY, MT_DATA);
12329a4510acSAlexander Motin 		if (newmb == NULL) {
12339a4510acSAlexander Motin 			ICL_WARN("failed to allocate mbuf");
12349a4510acSAlexander Motin 			return (ENOMEM);
12359a4510acSAlexander Motin 		}
12369a4510acSAlexander Motin 
12379a4510acSAlexander Motin 		newmb->m_flags |= M_RDONLY;
12389a4510acSAlexander Motin 		m_extaddref(newmb, __DECONST(char *, addr), len, &isp->ref_cnt,
12399a4510acSAlexander Motin 		    icl_soft_mbuf_done, isp, NULL);
12409a4510acSAlexander Motin 		newmb->m_len = len;
12419a4510acSAlexander Motin 	} else {
1242898fd11fSAlexander Motin 		newmb = m_getm2(NULL, len, flags, MT_DATA, 0);
1243321b17ecSEdward Tomasz Napierala 		if (newmb == NULL) {
1244321b17ecSEdward Tomasz Napierala 			ICL_WARN("failed to allocate mbuf for %zd bytes", len);
1245321b17ecSEdward Tomasz Napierala 			return (ENOMEM);
1246321b17ecSEdward Tomasz Napierala 		}
1247321b17ecSEdward Tomasz Napierala 
1248321b17ecSEdward Tomasz Napierala 		for (mb = newmb; mb != NULL; mb = mb->m_next) {
1249321b17ecSEdward Tomasz Napierala 			copylen = min(M_TRAILINGSPACE(mb), len - off);
1250321b17ecSEdward Tomasz Napierala 			memcpy(mtod(mb, char *), (const char *)addr + off, copylen);
1251321b17ecSEdward Tomasz Napierala 			mb->m_len = copylen;
1252321b17ecSEdward Tomasz Napierala 			off += copylen;
1253321b17ecSEdward Tomasz Napierala 		}
1254321b17ecSEdward Tomasz Napierala 		KASSERT(off == len, ("%s: off != len", __func__));
12559a4510acSAlexander Motin 	}
1256321b17ecSEdward Tomasz Napierala 
1257321b17ecSEdward Tomasz Napierala 	if (request->ip_data_mbuf == NULL) {
1258321b17ecSEdward Tomasz Napierala 		request->ip_data_mbuf = newmb;
1259321b17ecSEdward Tomasz Napierala 		request->ip_data_len = len;
1260321b17ecSEdward Tomasz Napierala 	} else {
1261321b17ecSEdward Tomasz Napierala 		m_cat(request->ip_data_mbuf, newmb);
1262321b17ecSEdward Tomasz Napierala 		request->ip_data_len += len;
1263321b17ecSEdward Tomasz Napierala 	}
1264321b17ecSEdward Tomasz Napierala 
1265321b17ecSEdward Tomasz Napierala 	return (0);
1266321b17ecSEdward Tomasz Napierala }
1267321b17ecSEdward Tomasz Napierala 
1268321b17ecSEdward Tomasz Napierala void
icl_soft_conn_pdu_get_bio(struct icl_conn * ic,struct icl_pdu * ip,size_t pdu_off,struct bio * bp,size_t bio_off,size_t len)1269530e725dSJohn Baldwin icl_soft_conn_pdu_get_bio(struct icl_conn *ic, struct icl_pdu *ip,
1270530e725dSJohn Baldwin     size_t pdu_off, struct bio *bp, size_t bio_off, size_t len)
1271530e725dSJohn Baldwin {
1272530e725dSJohn Baldwin 	vm_offset_t vaddr;
1273530e725dSJohn Baldwin 	size_t page_offset, todo;
127402a226acSDimitry Andric 	int i __unused;
1275530e725dSJohn Baldwin 
1276530e725dSJohn Baldwin 	MPASS(bp->bio_flags & BIO_UNMAPPED);
1277530e725dSJohn Baldwin 	if (bio_off < PAGE_SIZE - bp->bio_ma_offset) {
1278530e725dSJohn Baldwin 		page_offset = bp->bio_ma_offset + bio_off;
1279530e725dSJohn Baldwin 		i = 0;
1280530e725dSJohn Baldwin 	} else {
1281530e725dSJohn Baldwin 		bio_off -= PAGE_SIZE - bp->bio_ma_offset;
1282530e725dSJohn Baldwin 		for (i = 1; bio_off >= PAGE_SIZE; i++)
1283530e725dSJohn Baldwin 			bio_off -= PAGE_SIZE;
1284530e725dSJohn Baldwin 		page_offset = bio_off;
1285530e725dSJohn Baldwin 	}
1286530e725dSJohn Baldwin 
1287530e725dSJohn Baldwin 	while (len > 0) {
1288530e725dSJohn Baldwin 		todo = MIN(len, PAGE_SIZE - page_offset);
1289530e725dSJohn Baldwin 
1290832acea9SJohn Baldwin 		vaddr = PHYS_TO_DMAP(VM_PAGE_TO_PHYS(bp->bio_ma[i]));
1291530e725dSJohn Baldwin 		m_copydata(ip->ip_data_mbuf, pdu_off, todo, (char *)vaddr +
1292530e725dSJohn Baldwin 		    page_offset);
1293530e725dSJohn Baldwin 
1294530e725dSJohn Baldwin 		page_offset = 0;
1295530e725dSJohn Baldwin 		pdu_off += todo;
1296530e725dSJohn Baldwin 		len -= todo;
1297530e725dSJohn Baldwin 		i++;
1298530e725dSJohn Baldwin 	}
1299530e725dSJohn Baldwin }
1300530e725dSJohn Baldwin 
1301530e725dSJohn Baldwin void
icl_soft_conn_pdu_get_data(struct icl_conn * ic,struct icl_pdu * ip,size_t off,void * addr,size_t len)1302321b17ecSEdward Tomasz Napierala icl_soft_conn_pdu_get_data(struct icl_conn *ic, struct icl_pdu *ip,
1303321b17ecSEdward Tomasz Napierala     size_t off, void *addr, size_t len)
1304321b17ecSEdward Tomasz Napierala {
1305321b17ecSEdward Tomasz Napierala 
130682f7fa7aSAlexander Motin 	m_copydata(ip->ip_data_mbuf, off, len, addr);
1307321b17ecSEdward Tomasz Napierala }
1308321b17ecSEdward Tomasz Napierala 
1309321b17ecSEdward Tomasz Napierala static void
icl_soft_conn_pdu_queue(struct icl_conn * ic,struct icl_pdu * ip)13109a4510acSAlexander Motin icl_soft_conn_pdu_queue(struct icl_conn *ic, struct icl_pdu *ip)
1311321b17ecSEdward Tomasz Napierala {
1312321b17ecSEdward Tomasz Napierala 
13139a4510acSAlexander Motin 	icl_soft_conn_pdu_queue_cb(ic, ip, NULL);
13149a4510acSAlexander Motin }
13159a4510acSAlexander Motin 
13169a4510acSAlexander Motin static void
icl_soft_conn_pdu_queue_cb(struct icl_conn * ic,struct icl_pdu * ip,icl_pdu_cb cb)13179a4510acSAlexander Motin icl_soft_conn_pdu_queue_cb(struct icl_conn *ic, struct icl_pdu *ip,
13189a4510acSAlexander Motin     icl_pdu_cb cb)
13199a4510acSAlexander Motin {
132087322a90SJohn Baldwin 	struct icl_soft_conn *isc = (struct icl_soft_conn *)ic;
13219a4510acSAlexander Motin 	struct icl_soft_pdu *isp = (struct icl_soft_pdu *)ip;
1322321b17ecSEdward Tomasz Napierala 
1323321b17ecSEdward Tomasz Napierala 	ICL_CONN_LOCK_ASSERT(ic);
13249a4510acSAlexander Motin 	isp->ref_cnt++;
13259a4510acSAlexander Motin 	isp->cb = cb;
1326321b17ecSEdward Tomasz Napierala 
1327321b17ecSEdward Tomasz Napierala 	if (ic->ic_disconnecting || ic->ic_socket == NULL) {
1328321b17ecSEdward Tomasz Napierala 		ICL_DEBUG("icl_pdu_queue on closed connection");
13299a4510acSAlexander Motin 		icl_soft_pdu_done(ip, ENOTCONN);
1330321b17ecSEdward Tomasz Napierala 		return;
1331321b17ecSEdward Tomasz Napierala 	}
1332321b17ecSEdward Tomasz Napierala 
133387322a90SJohn Baldwin 	if (!STAILQ_EMPTY(&isc->to_send)) {
133487322a90SJohn Baldwin 		STAILQ_INSERT_TAIL(&isc->to_send, ip, ip_next);
1335321b17ecSEdward Tomasz Napierala 		/*
1336321b17ecSEdward Tomasz Napierala 		 * If the queue is not empty, someone else had already
1337321b17ecSEdward Tomasz Napierala 		 * signaled the send thread; no need to do that again,
1338321b17ecSEdward Tomasz Napierala 		 * just return.
1339321b17ecSEdward Tomasz Napierala 		 */
1340321b17ecSEdward Tomasz Napierala 		return;
1341321b17ecSEdward Tomasz Napierala 	}
1342321b17ecSEdward Tomasz Napierala 
134387322a90SJohn Baldwin 	STAILQ_INSERT_TAIL(&isc->to_send, ip, ip_next);
134487322a90SJohn Baldwin 	cv_signal(&isc->send_cv);
1345321b17ecSEdward Tomasz Napierala }
1346321b17ecSEdward Tomasz Napierala 
1347321b17ecSEdward Tomasz Napierala static struct icl_conn *
icl_soft_new_conn(const char * name,struct mtx * lock)1348321b17ecSEdward Tomasz Napierala icl_soft_new_conn(const char *name, struct mtx *lock)
1349321b17ecSEdward Tomasz Napierala {
135087322a90SJohn Baldwin 	struct icl_soft_conn *isc;
1351321b17ecSEdward Tomasz Napierala 	struct icl_conn *ic;
1352321b17ecSEdward Tomasz Napierala 
1353321b17ecSEdward Tomasz Napierala 	refcount_acquire(&icl_ncons);
1354321b17ecSEdward Tomasz Napierala 
135587322a90SJohn Baldwin 	isc = (struct icl_soft_conn *)kobj_create(&icl_soft_class, M_ICL_SOFT,
135687322a90SJohn Baldwin 	    M_WAITOK | M_ZERO);
1357321b17ecSEdward Tomasz Napierala 
135887322a90SJohn Baldwin 	STAILQ_INIT(&isc->to_send);
135987322a90SJohn Baldwin 	cv_init(&isc->send_cv, "icl_tx");
136087322a90SJohn Baldwin 	cv_init(&isc->receive_cv, "icl_rx");
136187322a90SJohn Baldwin 
136287322a90SJohn Baldwin 	ic = &isc->ic;
1363321b17ecSEdward Tomasz Napierala 	ic->ic_lock = lock;
1364321b17ecSEdward Tomasz Napierala #ifdef DIAGNOSTIC
1365321b17ecSEdward Tomasz Napierala 	refcount_init(&ic->ic_outstanding_pdus, 0);
1366321b17ecSEdward Tomasz Napierala #endif
1367321b17ecSEdward Tomasz Napierala 	ic->ic_name = name;
1368d4b195d3SEdward Tomasz Napierala 	ic->ic_offload = "None";
1369530e725dSJohn Baldwin 	ic->ic_unmapped = PMAP_HAS_DMAP;
1370321b17ecSEdward Tomasz Napierala 
1371321b17ecSEdward Tomasz Napierala 	return (ic);
1372321b17ecSEdward Tomasz Napierala }
1373321b17ecSEdward Tomasz Napierala 
1374321b17ecSEdward Tomasz Napierala void
icl_soft_conn_free(struct icl_conn * ic)1375321b17ecSEdward Tomasz Napierala icl_soft_conn_free(struct icl_conn *ic)
1376321b17ecSEdward Tomasz Napierala {
137787322a90SJohn Baldwin 	struct icl_soft_conn *isc = (struct icl_soft_conn *)ic;
1378321b17ecSEdward Tomasz Napierala 
137922d3bb26SEdward Tomasz Napierala #ifdef DIAGNOSTIC
138022d3bb26SEdward Tomasz Napierala 	KASSERT(ic->ic_outstanding_pdus == 0,
138122d3bb26SEdward Tomasz Napierala 	    ("destroying session with %d outstanding PDUs",
138222d3bb26SEdward Tomasz Napierala 	     ic->ic_outstanding_pdus));
138322d3bb26SEdward Tomasz Napierala #endif
138487322a90SJohn Baldwin 	cv_destroy(&isc->send_cv);
138587322a90SJohn Baldwin 	cv_destroy(&isc->receive_cv);
138687322a90SJohn Baldwin 	kobj_delete((struct kobj *)isc, M_ICL_SOFT);
1387321b17ecSEdward Tomasz Napierala 	refcount_release(&icl_ncons);
1388321b17ecSEdward Tomasz Napierala }
1389321b17ecSEdward Tomasz Napierala 
1390321b17ecSEdward Tomasz Napierala static int
icl_conn_start(struct icl_conn * ic)1391321b17ecSEdward Tomasz Napierala icl_conn_start(struct icl_conn *ic)
1392321b17ecSEdward Tomasz Napierala {
139387322a90SJohn Baldwin 	struct icl_soft_conn *isc = (struct icl_soft_conn *)ic;
1394321b17ecSEdward Tomasz Napierala 	size_t minspace;
1395321b17ecSEdward Tomasz Napierala 	struct sockopt opt;
1396321b17ecSEdward Tomasz Napierala 	int error, one = 1;
1397321b17ecSEdward Tomasz Napierala 
1398321b17ecSEdward Tomasz Napierala 	ICL_CONN_LOCK(ic);
1399321b17ecSEdward Tomasz Napierala 
1400321b17ecSEdward Tomasz Napierala 	/*
1401321b17ecSEdward Tomasz Napierala 	 * XXX: Ugly hack.
1402321b17ecSEdward Tomasz Napierala 	 */
1403321b17ecSEdward Tomasz Napierala 	if (ic->ic_socket == NULL) {
1404321b17ecSEdward Tomasz Napierala 		ICL_CONN_UNLOCK(ic);
1405321b17ecSEdward Tomasz Napierala 		return (EINVAL);
1406321b17ecSEdward Tomasz Napierala 	}
1407321b17ecSEdward Tomasz Napierala 
140887322a90SJohn Baldwin 	isc->receive_state = ICL_CONN_STATE_BHS;
140987322a90SJohn Baldwin 	isc->receive_len = sizeof(struct iscsi_bhs);
1410321b17ecSEdward Tomasz Napierala 	ic->ic_disconnecting = false;
1411321b17ecSEdward Tomasz Napierala 
1412321b17ecSEdward Tomasz Napierala 	ICL_CONN_UNLOCK(ic);
1413321b17ecSEdward Tomasz Napierala 
1414321b17ecSEdward Tomasz Napierala 	/*
1415321b17ecSEdward Tomasz Napierala 	 * For sendspace, this is required because the current code cannot
1416321b17ecSEdward Tomasz Napierala 	 * send a PDU in pieces; thus, the minimum buffer size is equal
1417321b17ecSEdward Tomasz Napierala 	 * to the maximum PDU size.  "+4" is to account for possible padding.
1418321b17ecSEdward Tomasz Napierala 	 */
14190cc7d64aSJohn Baldwin 	minspace = sizeof(struct iscsi_bhs) +
14200cc7d64aSJohn Baldwin 	    ic->ic_max_send_data_segment_length +
1421321b17ecSEdward Tomasz Napierala 	    ISCSI_HEADER_DIGEST_SIZE + ISCSI_DATA_DIGEST_SIZE + 4;
1422321b17ecSEdward Tomasz Napierala 	if (sendspace < minspace) {
1423321b17ecSEdward Tomasz Napierala 		ICL_WARN("kern.icl.sendspace too low; must be at least %zd",
1424321b17ecSEdward Tomasz Napierala 		    minspace);
1425321b17ecSEdward Tomasz Napierala 		sendspace = minspace;
1426321b17ecSEdward Tomasz Napierala 	}
14270cc7d64aSJohn Baldwin 	minspace = sizeof(struct iscsi_bhs) +
14280cc7d64aSJohn Baldwin 	    ic->ic_max_recv_data_segment_length +
14290cc7d64aSJohn Baldwin 	    ISCSI_HEADER_DIGEST_SIZE + ISCSI_DATA_DIGEST_SIZE + 4;
1430321b17ecSEdward Tomasz Napierala 	if (recvspace < minspace) {
1431321b17ecSEdward Tomasz Napierala 		ICL_WARN("kern.icl.recvspace too low; must be at least %zd",
1432321b17ecSEdward Tomasz Napierala 		    minspace);
1433321b17ecSEdward Tomasz Napierala 		recvspace = minspace;
1434321b17ecSEdward Tomasz Napierala 	}
1435321b17ecSEdward Tomasz Napierala 
1436321b17ecSEdward Tomasz Napierala 	error = soreserve(ic->ic_socket, sendspace, recvspace);
1437321b17ecSEdward Tomasz Napierala 	if (error != 0) {
1438321b17ecSEdward Tomasz Napierala 		ICL_WARN("soreserve failed with error %d", error);
14395aabcd7cSEdward Tomasz Napierala 		icl_soft_conn_close(ic);
1440321b17ecSEdward Tomasz Napierala 		return (error);
1441321b17ecSEdward Tomasz Napierala 	}
1442321b17ecSEdward Tomasz Napierala 	ic->ic_socket->so_snd.sb_flags |= SB_AUTOSIZE;
1443321b17ecSEdward Tomasz Napierala 	ic->ic_socket->so_rcv.sb_flags |= SB_AUTOSIZE;
1444321b17ecSEdward Tomasz Napierala 
1445321b17ecSEdward Tomasz Napierala 	/*
1446321b17ecSEdward Tomasz Napierala 	 * Disable Nagle.
1447321b17ecSEdward Tomasz Napierala 	 */
1448321b17ecSEdward Tomasz Napierala 	bzero(&opt, sizeof(opt));
1449321b17ecSEdward Tomasz Napierala 	opt.sopt_dir = SOPT_SET;
1450321b17ecSEdward Tomasz Napierala 	opt.sopt_level = IPPROTO_TCP;
1451321b17ecSEdward Tomasz Napierala 	opt.sopt_name = TCP_NODELAY;
1452321b17ecSEdward Tomasz Napierala 	opt.sopt_val = &one;
1453321b17ecSEdward Tomasz Napierala 	opt.sopt_valsize = sizeof(one);
1454321b17ecSEdward Tomasz Napierala 	error = sosetopt(ic->ic_socket, &opt);
1455321b17ecSEdward Tomasz Napierala 	if (error != 0) {
1456321b17ecSEdward Tomasz Napierala 		ICL_WARN("disabling TCP_NODELAY failed with error %d", error);
14575aabcd7cSEdward Tomasz Napierala 		icl_soft_conn_close(ic);
1458321b17ecSEdward Tomasz Napierala 		return (error);
1459321b17ecSEdward Tomasz Napierala 	}
1460321b17ecSEdward Tomasz Napierala 
1461321b17ecSEdward Tomasz Napierala 	/*
1462321b17ecSEdward Tomasz Napierala 	 * Register socket upcall, to get notified about incoming PDUs
1463321b17ecSEdward Tomasz Napierala 	 * and free space to send outgoing ones.
1464321b17ecSEdward Tomasz Napierala 	 */
1465321b17ecSEdward Tomasz Napierala 	SOCKBUF_LOCK(&ic->ic_socket->so_snd);
146687322a90SJohn Baldwin 	soupcall_set(ic->ic_socket, SO_SND, icl_soupcall_send, isc);
1467321b17ecSEdward Tomasz Napierala 	SOCKBUF_UNLOCK(&ic->ic_socket->so_snd);
1468321b17ecSEdward Tomasz Napierala 	SOCKBUF_LOCK(&ic->ic_socket->so_rcv);
146987322a90SJohn Baldwin 	soupcall_set(ic->ic_socket, SO_RCV, icl_soupcall_receive, isc);
1470321b17ecSEdward Tomasz Napierala 	SOCKBUF_UNLOCK(&ic->ic_socket->so_rcv);
1471321b17ecSEdward Tomasz Napierala 
14725b157f21SAlexander Motin 	/*
14735b157f21SAlexander Motin 	 * Start threads.
14745b157f21SAlexander Motin 	 */
14755b157f21SAlexander Motin 	ICL_CONN_LOCK(ic);
147687322a90SJohn Baldwin 	isc->send_running = isc->receive_running = true;
14775b157f21SAlexander Motin 	ICL_CONN_UNLOCK(ic);
14785b157f21SAlexander Motin 	error = kthread_add(icl_send_thread, ic, NULL, NULL, 0, 0, "%stx",
14795b157f21SAlexander Motin 	    ic->ic_name);
14805b157f21SAlexander Motin 	if (error != 0) {
14815b157f21SAlexander Motin 		ICL_WARN("kthread_add(9) failed with error %d", error);
14825b157f21SAlexander Motin 		ICL_CONN_LOCK(ic);
148387322a90SJohn Baldwin 		isc->send_running = isc->receive_running = false;
148487322a90SJohn Baldwin 		cv_signal(&isc->send_cv);
14855b157f21SAlexander Motin 		ICL_CONN_UNLOCK(ic);
14865b157f21SAlexander Motin 		icl_soft_conn_close(ic);
14875b157f21SAlexander Motin 		return (error);
14885b157f21SAlexander Motin 	}
14895b157f21SAlexander Motin 	error = kthread_add(icl_receive_thread, ic, NULL, NULL, 0, 0, "%srx",
14905b157f21SAlexander Motin 	    ic->ic_name);
14915b157f21SAlexander Motin 	if (error != 0) {
14925b157f21SAlexander Motin 		ICL_WARN("kthread_add(9) failed with error %d", error);
14935b157f21SAlexander Motin 		ICL_CONN_LOCK(ic);
149487322a90SJohn Baldwin 		isc->receive_running = false;
149587322a90SJohn Baldwin 		cv_signal(&isc->send_cv);
14965b157f21SAlexander Motin 		ICL_CONN_UNLOCK(ic);
14975b157f21SAlexander Motin 		icl_soft_conn_close(ic);
14985b157f21SAlexander Motin 		return (error);
14995b157f21SAlexander Motin 	}
15005b157f21SAlexander Motin 
1501321b17ecSEdward Tomasz Napierala 	return (0);
1502321b17ecSEdward Tomasz Napierala }
1503321b17ecSEdward Tomasz Napierala 
1504321b17ecSEdward Tomasz Napierala int
icl_soft_conn_handoff(struct icl_conn * ic,int fd)1505321b17ecSEdward Tomasz Napierala icl_soft_conn_handoff(struct icl_conn *ic, int fd)
1506321b17ecSEdward Tomasz Napierala {
1507321b17ecSEdward Tomasz Napierala 	struct file *fp;
1508321b17ecSEdward Tomasz Napierala 	struct socket *so;
1509321b17ecSEdward Tomasz Napierala 	cap_rights_t rights;
1510321b17ecSEdward Tomasz Napierala 	int error;
1511321b17ecSEdward Tomasz Napierala 
1512321b17ecSEdward Tomasz Napierala 	ICL_CONN_LOCK_ASSERT_NOT(ic);
1513321b17ecSEdward Tomasz Napierala 
1514906a424bSEdward Tomasz Napierala #ifdef ICL_KERNEL_PROXY
1515906a424bSEdward Tomasz Napierala 	/*
1516906a424bSEdward Tomasz Napierala 	 * We're transitioning to Full Feature phase, and we don't
1517906a424bSEdward Tomasz Napierala 	 * really care.
1518906a424bSEdward Tomasz Napierala 	 */
1519906a424bSEdward Tomasz Napierala 	if (fd == 0) {
1520906a424bSEdward Tomasz Napierala 		ICL_CONN_LOCK(ic);
1521906a424bSEdward Tomasz Napierala 		if (ic->ic_socket == NULL) {
1522906a424bSEdward Tomasz Napierala 			ICL_CONN_UNLOCK(ic);
1523906a424bSEdward Tomasz Napierala 			ICL_WARN("proxy handoff without connect");
1524906a424bSEdward Tomasz Napierala 			return (EINVAL);
1525906a424bSEdward Tomasz Napierala 		}
1526906a424bSEdward Tomasz Napierala 		ICL_CONN_UNLOCK(ic);
1527906a424bSEdward Tomasz Napierala 		return (0);
1528906a424bSEdward Tomasz Napierala 	}
1529906a424bSEdward Tomasz Napierala #endif
1530906a424bSEdward Tomasz Napierala 
1531321b17ecSEdward Tomasz Napierala 	/*
1532321b17ecSEdward Tomasz Napierala 	 * Steal the socket from userland.
1533321b17ecSEdward Tomasz Napierala 	 */
1534321b17ecSEdward Tomasz Napierala 	error = fget(curthread, fd,
15356b3a9a0fSMateusz Guzik 	    cap_rights_init_one(&rights, CAP_SOCK_CLIENT), &fp);
1536321b17ecSEdward Tomasz Napierala 	if (error != 0)
1537321b17ecSEdward Tomasz Napierala 		return (error);
1538321b17ecSEdward Tomasz Napierala 	if (fp->f_type != DTYPE_SOCKET) {
1539321b17ecSEdward Tomasz Napierala 		fdrop(fp, curthread);
1540321b17ecSEdward Tomasz Napierala 		return (EINVAL);
1541321b17ecSEdward Tomasz Napierala 	}
1542321b17ecSEdward Tomasz Napierala 	so = fp->f_data;
1543321b17ecSEdward Tomasz Napierala 	if (so->so_type != SOCK_STREAM) {
1544321b17ecSEdward Tomasz Napierala 		fdrop(fp, curthread);
1545321b17ecSEdward Tomasz Napierala 		return (EINVAL);
1546321b17ecSEdward Tomasz Napierala 	}
1547321b17ecSEdward Tomasz Napierala 
1548321b17ecSEdward Tomasz Napierala 	ICL_CONN_LOCK(ic);
1549321b17ecSEdward Tomasz Napierala 
1550321b17ecSEdward Tomasz Napierala 	if (ic->ic_socket != NULL) {
1551321b17ecSEdward Tomasz Napierala 		ICL_CONN_UNLOCK(ic);
1552321b17ecSEdward Tomasz Napierala 		fdrop(fp, curthread);
1553321b17ecSEdward Tomasz Napierala 		return (EBUSY);
1554321b17ecSEdward Tomasz Napierala 	}
1555321b17ecSEdward Tomasz Napierala 
1556321b17ecSEdward Tomasz Napierala 	ic->ic_socket = fp->f_data;
1557321b17ecSEdward Tomasz Napierala 	fp->f_ops = &badfileops;
1558321b17ecSEdward Tomasz Napierala 	fp->f_data = NULL;
1559321b17ecSEdward Tomasz Napierala 	fdrop(fp, curthread);
1560321b17ecSEdward Tomasz Napierala 	ICL_CONN_UNLOCK(ic);
1561321b17ecSEdward Tomasz Napierala 
1562321b17ecSEdward Tomasz Napierala 	error = icl_conn_start(ic);
1563321b17ecSEdward Tomasz Napierala 
1564321b17ecSEdward Tomasz Napierala 	return (error);
1565321b17ecSEdward Tomasz Napierala }
1566321b17ecSEdward Tomasz Napierala 
1567321b17ecSEdward Tomasz Napierala void
icl_soft_conn_close(struct icl_conn * ic)15685aabcd7cSEdward Tomasz Napierala icl_soft_conn_close(struct icl_conn *ic)
1569321b17ecSEdward Tomasz Napierala {
157087322a90SJohn Baldwin 	struct icl_soft_conn *isc = (struct icl_soft_conn *)ic;
1571321b17ecSEdward Tomasz Napierala 	struct icl_pdu *pdu;
15725b157f21SAlexander Motin 	struct socket *so;
1573321b17ecSEdward Tomasz Napierala 
15745b157f21SAlexander Motin 	/*
15755b157f21SAlexander Motin 	 * Wake up the threads, so they can properly terminate.
15766895f89fSAlexander Motin 	 * Receive thread sleeps on so->so_rcv lock, send on ic->ic_lock.
15775b157f21SAlexander Motin 	 */
15786895f89fSAlexander Motin 	ICL_CONN_LOCK(ic);
15796895f89fSAlexander Motin 	if (!ic->ic_disconnecting) {
15806895f89fSAlexander Motin 		so = ic->ic_socket;
158106e9c710SAlexander Motin 		if (so)
15826895f89fSAlexander Motin 			SOCKBUF_LOCK(&so->so_rcv);
15835b157f21SAlexander Motin 		ic->ic_disconnecting = true;
158406e9c710SAlexander Motin 		if (so)
15856895f89fSAlexander Motin 			SOCKBUF_UNLOCK(&so->so_rcv);
15866895f89fSAlexander Motin 	}
158787322a90SJohn Baldwin 	while (isc->receive_running || isc->send_running) {
158887322a90SJohn Baldwin 		cv_signal(&isc->receive_cv);
158987322a90SJohn Baldwin 		cv_signal(&isc->send_cv);
159087322a90SJohn Baldwin 		cv_wait(&isc->send_cv, ic->ic_lock);
15915b157f21SAlexander Motin 	}
15925b157f21SAlexander Motin 
15935b157f21SAlexander Motin 	/* Some other thread could close the connection same time. */
15945b157f21SAlexander Motin 	so = ic->ic_socket;
15955b157f21SAlexander Motin 	if (so == NULL) {
1596321b17ecSEdward Tomasz Napierala 		ICL_CONN_UNLOCK(ic);
1597321b17ecSEdward Tomasz Napierala 		return;
1598321b17ecSEdward Tomasz Napierala 	}
15995b157f21SAlexander Motin 	ic->ic_socket = NULL;
1600321b17ecSEdward Tomasz Napierala 
1601321b17ecSEdward Tomasz Napierala 	/*
1602321b17ecSEdward Tomasz Napierala 	 * Deregister socket upcalls.
1603321b17ecSEdward Tomasz Napierala 	 */
1604321b17ecSEdward Tomasz Napierala 	ICL_CONN_UNLOCK(ic);
16055b157f21SAlexander Motin 	SOCKBUF_LOCK(&so->so_snd);
16065b157f21SAlexander Motin 	if (so->so_snd.sb_upcall != NULL)
16075b157f21SAlexander Motin 		soupcall_clear(so, SO_SND);
16085b157f21SAlexander Motin 	SOCKBUF_UNLOCK(&so->so_snd);
16095b157f21SAlexander Motin 	SOCKBUF_LOCK(&so->so_rcv);
16105b157f21SAlexander Motin 	if (so->so_rcv.sb_upcall != NULL)
16115b157f21SAlexander Motin 		soupcall_clear(so, SO_RCV);
16125b157f21SAlexander Motin 	SOCKBUF_UNLOCK(&so->so_rcv);
16135b157f21SAlexander Motin 	soclose(so);
1614321b17ecSEdward Tomasz Napierala 	ICL_CONN_LOCK(ic);
1615321b17ecSEdward Tomasz Napierala 
161687322a90SJohn Baldwin 	if (isc->receive_pdu != NULL) {
1617321b17ecSEdward Tomasz Napierala 		//ICL_DEBUG("freeing partially received PDU");
161887322a90SJohn Baldwin 		icl_soft_conn_pdu_free(ic, isc->receive_pdu);
161987322a90SJohn Baldwin 		isc->receive_pdu = NULL;
1620321b17ecSEdward Tomasz Napierala 	}
1621321b17ecSEdward Tomasz Napierala 
1622321b17ecSEdward Tomasz Napierala 	/*
1623321b17ecSEdward Tomasz Napierala 	 * Remove any outstanding PDUs from the send queue.
1624321b17ecSEdward Tomasz Napierala 	 */
162587322a90SJohn Baldwin 	while (!STAILQ_EMPTY(&isc->to_send)) {
162687322a90SJohn Baldwin 		pdu = STAILQ_FIRST(&isc->to_send);
162787322a90SJohn Baldwin 		STAILQ_REMOVE_HEAD(&isc->to_send, ip_next);
16289a4510acSAlexander Motin 		icl_soft_pdu_done(pdu, ENOTCONN);
1629321b17ecSEdward Tomasz Napierala 	}
1630321b17ecSEdward Tomasz Napierala 
163187322a90SJohn Baldwin 	KASSERT(STAILQ_EMPTY(&isc->to_send),
1632321b17ecSEdward Tomasz Napierala 	    ("destroying session with non-empty send queue"));
1633321b17ecSEdward Tomasz Napierala 	ICL_CONN_UNLOCK(ic);
1634321b17ecSEdward Tomasz Napierala }
1635321b17ecSEdward Tomasz Napierala 
16367a03d007SEdward Tomasz Napierala int
icl_soft_conn_task_setup(struct icl_conn * ic,struct icl_pdu * ip,struct ccb_scsiio * csio,uint32_t * task_tagp,void ** prvp)1637604c023fSEdward Tomasz Napierala icl_soft_conn_task_setup(struct icl_conn *ic, struct icl_pdu *ip,
1638604c023fSEdward Tomasz Napierala     struct ccb_scsiio *csio, uint32_t *task_tagp, void **prvp)
16397a03d007SEdward Tomasz Napierala {
16407a03d007SEdward Tomasz Napierala 
16417a03d007SEdward Tomasz Napierala 	return (0);
16427a03d007SEdward Tomasz Napierala }
16437a03d007SEdward Tomasz Napierala 
16447a03d007SEdward Tomasz Napierala void
icl_soft_conn_task_done(struct icl_conn * ic,void * prv)16457a03d007SEdward Tomasz Napierala icl_soft_conn_task_done(struct icl_conn *ic, void *prv)
16467a03d007SEdward Tomasz Napierala {
16477a03d007SEdward Tomasz Napierala }
16487a03d007SEdward Tomasz Napierala 
16497a03d007SEdward Tomasz Napierala int
icl_soft_conn_transfer_setup(struct icl_conn * ic,struct icl_pdu * ip,union ctl_io * io,uint32_t * transfer_tag,void ** prvp)16508903d8e3SJohn Baldwin icl_soft_conn_transfer_setup(struct icl_conn *ic, struct icl_pdu *ip,
16518903d8e3SJohn Baldwin     union ctl_io *io, uint32_t *transfer_tag, void **prvp)
16527a03d007SEdward Tomasz Napierala {
16537a03d007SEdward Tomasz Napierala 
16547a03d007SEdward Tomasz Napierala 	return (0);
16557a03d007SEdward Tomasz Napierala }
16567a03d007SEdward Tomasz Napierala 
16577a03d007SEdward Tomasz Napierala void
icl_soft_conn_transfer_done(struct icl_conn * ic,void * prv)16587a03d007SEdward Tomasz Napierala icl_soft_conn_transfer_done(struct icl_conn *ic, void *prv)
16597a03d007SEdward Tomasz Napierala {
16607a03d007SEdward Tomasz Napierala }
16617a03d007SEdward Tomasz Napierala 
1662321b17ecSEdward Tomasz Napierala static int
icl_soft_limits(struct icl_drv_limits * idl,int socket)16637b02c1e8SJohn Baldwin icl_soft_limits(struct icl_drv_limits *idl, int socket)
1664321b17ecSEdward Tomasz Napierala {
1665321b17ecSEdward Tomasz Napierala 
1666b75168edSAlexander Motin 	idl->idl_max_recv_data_segment_length = max_data_segment_length;
1667b75168edSAlexander Motin 	idl->idl_max_send_data_segment_length = max_data_segment_length;
1668b75168edSAlexander Motin 	idl->idl_max_burst_length = max_burst_length;
1669b75168edSAlexander Motin 	idl->idl_first_burst_length = first_burst_length;
1670321b17ecSEdward Tomasz Napierala 
1671321b17ecSEdward Tomasz Napierala 	return (0);
1672321b17ecSEdward Tomasz Napierala }
1673321b17ecSEdward Tomasz Napierala 
1674321b17ecSEdward Tomasz Napierala #ifdef ICL_KERNEL_PROXY
1675321b17ecSEdward Tomasz Napierala int
icl_soft_conn_connect(struct icl_conn * ic,int domain,int socktype,int protocol,struct sockaddr * from_sa,struct sockaddr * to_sa)1676f41492b0SEdward Tomasz Napierala icl_soft_conn_connect(struct icl_conn *ic, int domain, int socktype,
1677f41492b0SEdward Tomasz Napierala     int protocol, struct sockaddr *from_sa, struct sockaddr *to_sa)
1678f41492b0SEdward Tomasz Napierala {
1679f41492b0SEdward Tomasz Napierala 
1680f41492b0SEdward Tomasz Napierala 	return (icl_soft_proxy_connect(ic, domain, socktype, protocol,
1681f41492b0SEdward Tomasz Napierala 	    from_sa, to_sa));
1682f41492b0SEdward Tomasz Napierala }
1683f41492b0SEdward Tomasz Napierala 
1684f41492b0SEdward Tomasz Napierala int
icl_soft_handoff_sock(struct icl_conn * ic,struct socket * so)1685f41492b0SEdward Tomasz Napierala icl_soft_handoff_sock(struct icl_conn *ic, struct socket *so)
1686321b17ecSEdward Tomasz Napierala {
1687321b17ecSEdward Tomasz Napierala 	int error;
1688321b17ecSEdward Tomasz Napierala 
1689321b17ecSEdward Tomasz Napierala 	ICL_CONN_LOCK_ASSERT_NOT(ic);
1690321b17ecSEdward Tomasz Napierala 
1691321b17ecSEdward Tomasz Napierala 	if (so->so_type != SOCK_STREAM)
1692321b17ecSEdward Tomasz Napierala 		return (EINVAL);
1693321b17ecSEdward Tomasz Napierala 
1694321b17ecSEdward Tomasz Napierala 	ICL_CONN_LOCK(ic);
1695321b17ecSEdward Tomasz Napierala 	if (ic->ic_socket != NULL) {
1696321b17ecSEdward Tomasz Napierala 		ICL_CONN_UNLOCK(ic);
1697321b17ecSEdward Tomasz Napierala 		return (EBUSY);
1698321b17ecSEdward Tomasz Napierala 	}
1699321b17ecSEdward Tomasz Napierala 	ic->ic_socket = so;
1700321b17ecSEdward Tomasz Napierala 	ICL_CONN_UNLOCK(ic);
1701321b17ecSEdward Tomasz Napierala 
1702321b17ecSEdward Tomasz Napierala 	error = icl_conn_start(ic);
1703321b17ecSEdward Tomasz Napierala 
1704321b17ecSEdward Tomasz Napierala 	return (error);
1705321b17ecSEdward Tomasz Napierala }
1706321b17ecSEdward Tomasz Napierala #endif /* ICL_KERNEL_PROXY */
1707321b17ecSEdward Tomasz Napierala 
1708321b17ecSEdward Tomasz Napierala static int
icl_soft_load(void)1709321b17ecSEdward Tomasz Napierala icl_soft_load(void)
1710321b17ecSEdward Tomasz Napierala {
1711321b17ecSEdward Tomasz Napierala 	int error;
1712321b17ecSEdward Tomasz Napierala 
17139a4510acSAlexander Motin 	icl_soft_pdu_zone = uma_zcreate("icl_soft_pdu",
17149a4510acSAlexander Motin 	    sizeof(struct icl_soft_pdu), NULL, NULL, NULL, NULL,
1715321b17ecSEdward Tomasz Napierala 	    UMA_ALIGN_PTR, 0);
1716321b17ecSEdward Tomasz Napierala 	refcount_init(&icl_ncons, 0);
1717321b17ecSEdward Tomasz Napierala 
1718321b17ecSEdward Tomasz Napierala 	/*
1719321b17ecSEdward Tomasz Napierala 	 * The reason we call this "none" is that to the user,
1720321b17ecSEdward Tomasz Napierala 	 * it's known as "offload driver"; "offload driver: soft"
1721321b17ecSEdward Tomasz Napierala 	 * doesn't make much sense.
1722321b17ecSEdward Tomasz Napierala 	 */
1723b8911594SEdward Tomasz Napierala 	error = icl_register("none", false, 0,
1724b8911594SEdward Tomasz Napierala 	    icl_soft_limits, icl_soft_new_conn);
1725321b17ecSEdward Tomasz Napierala 	KASSERT(error == 0, ("failed to register"));
1726321b17ecSEdward Tomasz Napierala 
1727b8911594SEdward Tomasz Napierala #if defined(ICL_KERNEL_PROXY) && 0
1728b8911594SEdward Tomasz Napierala 	/*
1729b8911594SEdward Tomasz Napierala 	 * Debugging aid for kernel proxy functionality.
1730b8911594SEdward Tomasz Napierala 	 */
1731b8911594SEdward Tomasz Napierala 	error = icl_register("proxytest", true, 0,
1732b8911594SEdward Tomasz Napierala 	    icl_soft_limits, icl_soft_new_conn);
1733b8911594SEdward Tomasz Napierala 	KASSERT(error == 0, ("failed to register"));
1734b8911594SEdward Tomasz Napierala #endif
1735b8911594SEdward Tomasz Napierala 
1736321b17ecSEdward Tomasz Napierala 	return (error);
1737321b17ecSEdward Tomasz Napierala }
1738321b17ecSEdward Tomasz Napierala 
1739321b17ecSEdward Tomasz Napierala static int
icl_soft_unload(void)1740321b17ecSEdward Tomasz Napierala icl_soft_unload(void)
1741321b17ecSEdward Tomasz Napierala {
1742321b17ecSEdward Tomasz Napierala 
1743321b17ecSEdward Tomasz Napierala 	if (icl_ncons != 0)
1744321b17ecSEdward Tomasz Napierala 		return (EBUSY);
1745321b17ecSEdward Tomasz Napierala 
1746b8911594SEdward Tomasz Napierala 	icl_unregister("none", false);
1747b8911594SEdward Tomasz Napierala #if defined(ICL_KERNEL_PROXY) && 0
1748b8911594SEdward Tomasz Napierala 	icl_unregister("proxytest", true);
1749b8911594SEdward Tomasz Napierala #endif
1750321b17ecSEdward Tomasz Napierala 
17519a4510acSAlexander Motin 	uma_zdestroy(icl_soft_pdu_zone);
1752321b17ecSEdward Tomasz Napierala 
1753321b17ecSEdward Tomasz Napierala 	return (0);
1754321b17ecSEdward Tomasz Napierala }
1755321b17ecSEdward Tomasz Napierala 
1756321b17ecSEdward Tomasz Napierala static int
icl_soft_modevent(module_t mod,int what,void * arg)1757321b17ecSEdward Tomasz Napierala icl_soft_modevent(module_t mod, int what, void *arg)
1758321b17ecSEdward Tomasz Napierala {
1759321b17ecSEdward Tomasz Napierala 
1760321b17ecSEdward Tomasz Napierala 	switch (what) {
1761321b17ecSEdward Tomasz Napierala 	case MOD_LOAD:
1762321b17ecSEdward Tomasz Napierala 		return (icl_soft_load());
1763321b17ecSEdward Tomasz Napierala 	case MOD_UNLOAD:
1764321b17ecSEdward Tomasz Napierala 		return (icl_soft_unload());
1765321b17ecSEdward Tomasz Napierala 	default:
1766321b17ecSEdward Tomasz Napierala 		return (EINVAL);
1767321b17ecSEdward Tomasz Napierala 	}
1768321b17ecSEdward Tomasz Napierala }
1769321b17ecSEdward Tomasz Napierala 
1770321b17ecSEdward Tomasz Napierala moduledata_t icl_soft_data = {
1771321b17ecSEdward Tomasz Napierala 	"icl_soft",
1772321b17ecSEdward Tomasz Napierala 	icl_soft_modevent,
1773321b17ecSEdward Tomasz Napierala 	0
1774321b17ecSEdward Tomasz Napierala };
1775321b17ecSEdward Tomasz Napierala 
1776321b17ecSEdward Tomasz Napierala DECLARE_MODULE(icl_soft, icl_soft_data, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
1777321b17ecSEdward Tomasz Napierala MODULE_DEPEND(icl_soft, icl, 1, 1, 1);
1778872d2d92SEdward Tomasz Napierala MODULE_VERSION(icl_soft, 1);
1779