1e25c779eSMatthew Dillon /*-
2e25c779eSMatthew Dillon * Copyright (c) 2005-2008 Daniel Braniss <danny@cs.huji.ac.il>
3e25c779eSMatthew Dillon * All rights reserved.
4e25c779eSMatthew Dillon *
5e25c779eSMatthew Dillon * Redistribution and use in source and binary forms, with or without
6e25c779eSMatthew Dillon * modification, are permitted provided that the following conditions
7e25c779eSMatthew Dillon * are met:
8e25c779eSMatthew Dillon * 1. Redistributions of source code must retain the above copyright
9e25c779eSMatthew Dillon * notice, this list of conditions and the following disclaimer.
10e25c779eSMatthew Dillon * 2. Redistributions in binary form must reproduce the above copyright
11e25c779eSMatthew Dillon * notice, this list of conditions and the following disclaimer in the
12e25c779eSMatthew Dillon * documentation and/or other materials provided with the distribution.
13e25c779eSMatthew Dillon *
14e25c779eSMatthew Dillon * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15e25c779eSMatthew Dillon * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16e25c779eSMatthew Dillon * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17e25c779eSMatthew Dillon * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18e25c779eSMatthew Dillon * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19e25c779eSMatthew Dillon * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20e25c779eSMatthew Dillon * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21e25c779eSMatthew Dillon * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22e25c779eSMatthew Dillon * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23e25c779eSMatthew Dillon * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24e25c779eSMatthew Dillon * SUCH DAMAGE.
25e25c779eSMatthew Dillon *
26e25c779eSMatthew Dillon * $FreeBSD: src/sys/dev/iscsi/initiator/isc_soc.c,v 1.6 2009/06/25 18:46:30 kib Exp $
27e25c779eSMatthew Dillon */
28e25c779eSMatthew Dillon /*
29e25c779eSMatthew Dillon | iSCSI
30e25c779eSMatthew Dillon | $Id: isc_soc.c,v 1.26 2007/05/19 06:09:01 danny Exp danny $
31e25c779eSMatthew Dillon */
32e25c779eSMatthew Dillon
33e25c779eSMatthew Dillon #include "opt_iscsi_initiator.h"
34e25c779eSMatthew Dillon
35e25c779eSMatthew Dillon #include <sys/param.h>
36e25c779eSMatthew Dillon #include <sys/kernel.h>
37e25c779eSMatthew Dillon #include <sys/conf.h>
38e25c779eSMatthew Dillon #include <sys/systm.h>
39e25c779eSMatthew Dillon #include <sys/malloc.h>
40e25c779eSMatthew Dillon #include <sys/ctype.h>
41e25c779eSMatthew Dillon #include <sys/errno.h>
42e25c779eSMatthew Dillon #include <sys/sysctl.h>
43e25c779eSMatthew Dillon #include <sys/file.h>
44e25c779eSMatthew Dillon #include <sys/uio.h>
45e25c779eSMatthew Dillon #include <sys/socketvar.h>
46e25c779eSMatthew Dillon #include <sys/socket.h>
47e25c779eSMatthew Dillon #include <sys/protosw.h>
48e25c779eSMatthew Dillon #include <sys/proc.h>
49e25c779eSMatthew Dillon #include <sys/queue.h>
50e25c779eSMatthew Dillon #include <sys/kthread.h>
51e25c779eSMatthew Dillon #include <sys/syslog.h>
52e25c779eSMatthew Dillon #include <sys/mbuf.h>
53e25c779eSMatthew Dillon #include <sys/eventhandler.h>
54e25c779eSMatthew Dillon #include <sys/socketops.h>
55e25c779eSMatthew Dillon
56cd8ab232SMatthew Dillon #include <sys/mplock2.h>
57cd8ab232SMatthew Dillon
58e25c779eSMatthew Dillon #include <bus/cam/cam.h>
59e25c779eSMatthew Dillon #include <bus/cam/cam_ccb.h>
60e25c779eSMatthew Dillon
61e25c779eSMatthew Dillon #include <dev/disk/iscsi/initiator/iscsi.h>
62e25c779eSMatthew Dillon #include <dev/disk/iscsi/initiator/iscsivar.h>
63e25c779eSMatthew Dillon
64e25c779eSMatthew Dillon #ifndef NO_USE_MBUF
65e25c779eSMatthew Dillon #define USE_MBUF
66e25c779eSMatthew Dillon #endif
67e25c779eSMatthew Dillon
68e25c779eSMatthew Dillon #ifdef USE_MBUF
69e25c779eSMatthew Dillon
70e25c779eSMatthew Dillon static int ou_refcnt = 0;
71e25c779eSMatthew Dillon
72e25c779eSMatthew Dillon /*
73e25c779eSMatthew Dillon | function for counting refs on external storage for mbuf
74e25c779eSMatthew Dillon */
75e25c779eSMatthew Dillon static void
ext_ref(void * arg)76e25c779eSMatthew Dillon ext_ref(void *arg)
77e25c779eSMatthew Dillon {
78e25c779eSMatthew Dillon pduq_t *a = arg;
79e25c779eSMatthew Dillon
80e25c779eSMatthew Dillon debug(3, "ou_refcnt=%d arg=%p b=%p", ou_refcnt, a, a->buf);
81e25c779eSMatthew Dillon atomic_add_int(&a->refcnt, 1);
82e25c779eSMatthew Dillon }
83e25c779eSMatthew Dillon
84e25c779eSMatthew Dillon /*
85e25c779eSMatthew Dillon | function for freeing external storage for mbuf
86e25c779eSMatthew Dillon */
87e25c779eSMatthew Dillon static void
ext_free(void * arg)88e25c779eSMatthew Dillon ext_free(void *arg)
89e25c779eSMatthew Dillon {
90e25c779eSMatthew Dillon pduq_t *a = arg;
91e25c779eSMatthew Dillon
92e25c779eSMatthew Dillon if (atomic_fetchadd_int(&a->refcnt, -1) == 1)
93e25c779eSMatthew Dillon if (a->buf != NULL) {
94e25c779eSMatthew Dillon debug(3, "ou_refcnt=%d a=%p b=%p", ou_refcnt, a, a->buf);
95e25c779eSMatthew Dillon kfree(a->buf, M_ISCSI);
96e25c779eSMatthew Dillon a->buf = NULL;
97e25c779eSMatthew Dillon }
98e25c779eSMatthew Dillon }
99e25c779eSMatthew Dillon
100e25c779eSMatthew Dillon int
isc_sendPDU(isc_session_t * sp,pduq_t * pq)101e25c779eSMatthew Dillon isc_sendPDU(isc_session_t *sp, pduq_t *pq)
102e25c779eSMatthew Dillon {
103e25c779eSMatthew Dillon struct mbuf *mh, **mp;
104e25c779eSMatthew Dillon pdu_t *pp = &pq->pdu;
105e25c779eSMatthew Dillon int len, error;
106e25c779eSMatthew Dillon
107e25c779eSMatthew Dillon debug_called(8);
108e25c779eSMatthew Dillon /*
109e25c779eSMatthew Dillon | mbuf for the iSCSI header
110e25c779eSMatthew Dillon */
111*b5523eacSSascha Wildner MGETHDR(mh, M_WAITOK, MT_DATA);
112e25c779eSMatthew Dillon mh->m_len = mh->m_pkthdr.len = sizeof(union ipdu_u);
113e25c779eSMatthew Dillon mh->m_pkthdr.rcvif = NULL;
114e25c779eSMatthew Dillon MH_ALIGN(mh, sizeof(union ipdu_u));
115e25c779eSMatthew Dillon bcopy(&pp->ipdu, mh->m_data, sizeof(union ipdu_u));
116e25c779eSMatthew Dillon mh->m_next = NULL;
117e25c779eSMatthew Dillon
118e25c779eSMatthew Dillon if(sp->hdrDigest)
119e25c779eSMatthew Dillon pq->pdu.hdr_dig = sp->hdrDigest(&pp->ipdu, sizeof(union ipdu_u), 0);
120e25c779eSMatthew Dillon if(pp->ahs_len) {
121e25c779eSMatthew Dillon /*
122e25c779eSMatthew Dillon | Add any AHS to the iSCSI hdr mbuf
123e25c779eSMatthew Dillon | XXX Assert: (mh->m_pkthdr.len + pp->ahs_len) < MHLEN
124e25c779eSMatthew Dillon */
125e25c779eSMatthew Dillon bcopy(pp->ahs, (mh->m_data + mh->m_len), pp->ahs_len);
126e25c779eSMatthew Dillon mh->m_len += pp->ahs_len;
127e25c779eSMatthew Dillon mh->m_pkthdr.len += pp->ahs_len;
128e25c779eSMatthew Dillon
129e25c779eSMatthew Dillon if(sp->hdrDigest)
130e25c779eSMatthew Dillon pq->pdu.hdr_dig = sp->hdrDigest(&pp->ahs, pp->ahs_len, pq->pdu.hdr_dig);
131e25c779eSMatthew Dillon }
132e25c779eSMatthew Dillon if(sp->hdrDigest) {
133e25c779eSMatthew Dillon debug(2, "hdr_dig=%x", pq->pdu.hdr_dig);
134e25c779eSMatthew Dillon /*
135e25c779eSMatthew Dillon | Add header digest to the iSCSI hdr mbuf
136e25c779eSMatthew Dillon | XXX Assert: (mh->m_pkthdr.len + 4) < MHLEN
137e25c779eSMatthew Dillon */
138e25c779eSMatthew Dillon bcopy(&pp->hdr_dig, (mh->m_data + mh->m_len), sizeof(int));
139e25c779eSMatthew Dillon mh->m_len += sizeof(int);
140e25c779eSMatthew Dillon mh->m_pkthdr.len += sizeof(int);
141e25c779eSMatthew Dillon }
142e25c779eSMatthew Dillon mp = &mh->m_next;
143e25c779eSMatthew Dillon if(pq->pdu.ds) {
144e25c779eSMatthew Dillon struct mbuf *md;
145e25c779eSMatthew Dillon int off = 0;
146e25c779eSMatthew Dillon
147e25c779eSMatthew Dillon len = pp->ds_len;
148e25c779eSMatthew Dillon while(len & 03) // the specs say it must be int alligned
149e25c779eSMatthew Dillon len++;
150e25c779eSMatthew Dillon while(len > 0) {
151e25c779eSMatthew Dillon int l;
152e25c779eSMatthew Dillon
153*b5523eacSSascha Wildner MGET(md, M_WAITOK, MT_DATA);
154e25c779eSMatthew Dillon pq->refcnt++;
155e25c779eSMatthew Dillon
156e25c779eSMatthew Dillon l = min(MCLBYTES, len);
157e25c779eSMatthew Dillon debug(5, "setting ext_free(arg=%p len/l=%d/%d)", pq->buf, len, l);
158e25c779eSMatthew Dillon md->m_ext.ext_buf = pq->buf;
159e25c779eSMatthew Dillon md->m_ext.ext_free = ext_free;
160e25c779eSMatthew Dillon md->m_ext.ext_ref = ext_ref;
161e25c779eSMatthew Dillon md->m_ext.ext_arg = pq;
162e25c779eSMatthew Dillon md->m_ext.ext_size = l;
163e25c779eSMatthew Dillon md->m_flags |= M_EXT;
164e25c779eSMatthew Dillon md->m_data = pp->ds + off;
165e25c779eSMatthew Dillon md->m_len = l;
166e25c779eSMatthew Dillon md->m_next = NULL;
167e25c779eSMatthew Dillon mh->m_pkthdr.len += l;
168e25c779eSMatthew Dillon *mp = md;
169e25c779eSMatthew Dillon mp = &md->m_next;
170e25c779eSMatthew Dillon len -= l;
171e25c779eSMatthew Dillon off += l;
172e25c779eSMatthew Dillon }
173e25c779eSMatthew Dillon }
174e25c779eSMatthew Dillon if(sp->dataDigest) {
175e25c779eSMatthew Dillon struct mbuf *me;
176e25c779eSMatthew Dillon
177e25c779eSMatthew Dillon pp->ds_dig = sp->dataDigest(pp->ds, pp->ds_len, 0);
178e25c779eSMatthew Dillon
179*b5523eacSSascha Wildner MGET(me, M_WAITOK, MT_DATA);
180e25c779eSMatthew Dillon me->m_len = sizeof(int);
181e25c779eSMatthew Dillon MH_ALIGN(mh, sizeof(int));
182e25c779eSMatthew Dillon bcopy(&pp->ds_dig, me->m_data, sizeof(int));
183e25c779eSMatthew Dillon me->m_next = NULL;
184e25c779eSMatthew Dillon mh->m_pkthdr.len += sizeof(int);
185e25c779eSMatthew Dillon *mp = me;
186e25c779eSMatthew Dillon }
1878650c3d4SMatthew Dillon if((error = sosend(sp->soc, NULL, NULL, mh, 0, 0, curthread)) != 0) {
188e25c779eSMatthew Dillon sdebug(3, "error=%d", error);
189e25c779eSMatthew Dillon return error;
190e25c779eSMatthew Dillon }
191e25c779eSMatthew Dillon sp->stats.nsent++;
192e25c779eSMatthew Dillon getmicrouptime(&sp->stats.t_sent);
193e25c779eSMatthew Dillon return 0;
194e25c779eSMatthew Dillon }
195e25c779eSMatthew Dillon #else /* NO_USE_MBUF */
196e25c779eSMatthew Dillon int
isc_sendPDU(isc_session_t * sp,pduq_t * pq)197e25c779eSMatthew Dillon isc_sendPDU(isc_session_t *sp, pduq_t *pq)
198e25c779eSMatthew Dillon {
199e25c779eSMatthew Dillon struct uio *uio = &pq->uio;
200e25c779eSMatthew Dillon struct iovec *iv;
201e25c779eSMatthew Dillon pdu_t *pp = &pq->pdu;
202e25c779eSMatthew Dillon int len, error;
203e25c779eSMatthew Dillon
204e25c779eSMatthew Dillon debug_called(8);
205e25c779eSMatthew Dillon
206e25c779eSMatthew Dillon bzero(uio, sizeof(struct uio));
207e25c779eSMatthew Dillon uio->uio_rw = UIO_WRITE;
208e25c779eSMatthew Dillon uio->uio_segflg = UIO_SYSSPACE;
2098650c3d4SMatthew Dillon uio->uio_td = curthread;
210e25c779eSMatthew Dillon uio->uio_iov = iv = pq->iov;
211e25c779eSMatthew Dillon
212e25c779eSMatthew Dillon iv->iov_base = &pp->ipdu;
213e25c779eSMatthew Dillon iv->iov_len = sizeof(union ipdu_u);
214e25c779eSMatthew Dillon uio->uio_resid = pq->len;
215e25c779eSMatthew Dillon iv++;
216e25c779eSMatthew Dillon if(sp->hdrDigest)
217e25c779eSMatthew Dillon pq->pdu.hdr_dig = sp->hdrDigest(&pp->ipdu, sizeof(union ipdu_u), 0);
218e25c779eSMatthew Dillon if(pp->ahs_len) {
219e25c779eSMatthew Dillon iv->iov_base = pp->ahs;
220e25c779eSMatthew Dillon iv->iov_len = pp->ahs_len;
221e25c779eSMatthew Dillon iv++;
222e25c779eSMatthew Dillon
223e25c779eSMatthew Dillon if(sp->hdrDigest)
224e25c779eSMatthew Dillon pq->pdu.hdr_dig = sp->hdrDigest(&pp->ahs, pp->ahs_len, pq->pdu.hdr_dig);
225e25c779eSMatthew Dillon }
226e25c779eSMatthew Dillon if(sp->hdrDigest) {
227e25c779eSMatthew Dillon debug(2, "hdr_dig=%x", pq->pdu.hdr_dig);
228e25c779eSMatthew Dillon iv->iov_base = &pp->hdr_dig;
229e25c779eSMatthew Dillon iv->iov_len = sizeof(int);
230e25c779eSMatthew Dillon iv++;
231e25c779eSMatthew Dillon }
232e25c779eSMatthew Dillon if(pq->pdu.ds) {
233e25c779eSMatthew Dillon iv->iov_base = pp->ds;
234e25c779eSMatthew Dillon iv->iov_len = pp->ds_len;
235e25c779eSMatthew Dillon while(iv->iov_len & 03) // the specs say it must be int alligned
236e25c779eSMatthew Dillon iv->iov_len++;
237e25c779eSMatthew Dillon iv++;
238e25c779eSMatthew Dillon }
239e25c779eSMatthew Dillon if(sp->dataDigest) {
240e25c779eSMatthew Dillon pp->ds_dig = sp->dataDigest(pp->ds, pp->ds_len, 0);
241e25c779eSMatthew Dillon iv->iov_base = &pp->ds_dig;
242e25c779eSMatthew Dillon iv->iov_len = sizeof(int);
243e25c779eSMatthew Dillon iv++;
244e25c779eSMatthew Dillon }
245e25c779eSMatthew Dillon uio->uio_iovcnt = iv - pq->iov;
246e25c779eSMatthew Dillon sdebug(5, "opcode=%x iovcnt=%d uio_resid=%d itt=%x",
247e25c779eSMatthew Dillon pp->ipdu.bhs.opcode, uio->uio_iovcnt, uio->uio_resid,
248e25c779eSMatthew Dillon ntohl(pp->ipdu.bhs.itt));
249e25c779eSMatthew Dillon sdebug(5, "sp=%p sp->soc=%p uio=%p sp->td=%p",
250e25c779eSMatthew Dillon sp, sp->soc, uio, sp->td);
251e25c779eSMatthew Dillon do {
252e25c779eSMatthew Dillon len = uio->uio_resid;
2538650c3d4SMatthew Dillon error = sosend(sp->soc, NULL, uio, 0, 0, 0, curthread);
254e25c779eSMatthew Dillon if(uio->uio_resid == 0 || error || len == uio->uio_resid) {
255e25c779eSMatthew Dillon if(uio->uio_resid) {
256e25c779eSMatthew Dillon sdebug(2, "uio->uio_resid=%d uio->uio_iovcnt=%d error=%d len=%d",
257e25c779eSMatthew Dillon uio->uio_resid, uio->uio_iovcnt, error, len);
258e25c779eSMatthew Dillon if(error == 0)
259e25c779eSMatthew Dillon error = EAGAIN; // 35
260e25c779eSMatthew Dillon }
261e25c779eSMatthew Dillon break;
262e25c779eSMatthew Dillon }
263e25c779eSMatthew Dillon /*
264e25c779eSMatthew Dillon | XXX: untested code
265e25c779eSMatthew Dillon */
266e25c779eSMatthew Dillon sdebug(1, "uio->uio_resid=%d uio->uio_iovcnt=%d",
267e25c779eSMatthew Dillon uio->uio_resid, uio->uio_iovcnt);
268e25c779eSMatthew Dillon iv = uio->uio_iov;
269e25c779eSMatthew Dillon len -= uio->uio_resid;
270e25c779eSMatthew Dillon while(uio->uio_iovcnt > 0) {
271e25c779eSMatthew Dillon if(iv->iov_len > len) {
272e25c779eSMatthew Dillon caddr_t bp = (caddr_t)iv->iov_base;
273e25c779eSMatthew Dillon
274e25c779eSMatthew Dillon iv->iov_len -= len;
275e25c779eSMatthew Dillon iv->iov_base = (void *)&bp[len];
276e25c779eSMatthew Dillon break;
277e25c779eSMatthew Dillon }
278e25c779eSMatthew Dillon len -= iv->iov_len;
279e25c779eSMatthew Dillon uio->uio_iovcnt--;
280e25c779eSMatthew Dillon uio->uio_iov++;
281e25c779eSMatthew Dillon iv++;
282e25c779eSMatthew Dillon }
283e25c779eSMatthew Dillon } while(uio->uio_resid);
284e25c779eSMatthew Dillon
285e25c779eSMatthew Dillon if(error == 0) {
286e25c779eSMatthew Dillon sp->stats.nsent++;
287e25c779eSMatthew Dillon getmicrouptime(&sp->stats.t_sent);
288e25c779eSMatthew Dillon
289e25c779eSMatthew Dillon }
290e25c779eSMatthew Dillon
291e25c779eSMatthew Dillon return error;
292e25c779eSMatthew Dillon }
293e25c779eSMatthew Dillon #endif /* USE_MBUF */
294e25c779eSMatthew Dillon
295e25c779eSMatthew Dillon /*
296e25c779eSMatthew Dillon | wait till a PDU header is received
297e25c779eSMatthew Dillon | from the socket.
298e25c779eSMatthew Dillon */
299e25c779eSMatthew Dillon /*
300e25c779eSMatthew Dillon The format of the BHS is:
301e25c779eSMatthew Dillon
302e25c779eSMatthew Dillon Byte/ 0 | 1 | 2 | 3 |
303e25c779eSMatthew Dillon / | | | |
304e25c779eSMatthew Dillon |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
305e25c779eSMatthew Dillon +---------------+---------------+---------------+---------------+
306e25c779eSMatthew Dillon 0|.|I| Opcode |F| Opcode-specific fields |
307e25c779eSMatthew Dillon +---------------+---------------+---------------+---------------+
308e25c779eSMatthew Dillon 4|TotalAHSLength | DataSegmentLength |
309e25c779eSMatthew Dillon +---------------+---------------+---------------+---------------+
310e25c779eSMatthew Dillon 8| LUN or Opcode-specific fields |
311e25c779eSMatthew Dillon + +
312e25c779eSMatthew Dillon 12| |
313e25c779eSMatthew Dillon +---------------+---------------+---------------+---------------+
314e25c779eSMatthew Dillon 16| Initiator Task Tag |
315e25c779eSMatthew Dillon +---------------+---------------+---------------+---------------+
316e25c779eSMatthew Dillon 20/ Opcode-specific fields /
317e25c779eSMatthew Dillon +/ /
318e25c779eSMatthew Dillon +---------------+---------------+---------------+---------------+
319e25c779eSMatthew Dillon 48
320e25c779eSMatthew Dillon */
321e25c779eSMatthew Dillon static __inline int
so_getbhs(isc_session_t * sp)322e25c779eSMatthew Dillon so_getbhs(isc_session_t *sp)
323e25c779eSMatthew Dillon {
324e25c779eSMatthew Dillon bhs_t *bhs = &sp->bhs;
325e25c779eSMatthew Dillon struct uio *uio = &sp->uio;
326e25c779eSMatthew Dillon struct iovec *iov = &sp->iov;
327e25c779eSMatthew Dillon int error, flags;
328e25c779eSMatthew Dillon
329e25c779eSMatthew Dillon debug_called(8);
330e25c779eSMatthew Dillon
331e25c779eSMatthew Dillon iov->iov_base = bhs;
332e25c779eSMatthew Dillon iov->iov_len = sizeof(bhs_t);
333e25c779eSMatthew Dillon
334e25c779eSMatthew Dillon uio->uio_iov = iov;
335e25c779eSMatthew Dillon uio->uio_iovcnt = 1;
336e25c779eSMatthew Dillon uio->uio_rw = UIO_READ;
337e25c779eSMatthew Dillon uio->uio_segflg = UIO_SYSSPACE;
338e25c779eSMatthew Dillon uio->uio_td = curthread; // why ...
339e25c779eSMatthew Dillon uio->uio_resid = sizeof(bhs_t);
340e25c779eSMatthew Dillon
341e25c779eSMatthew Dillon flags = MSG_WAITALL;
342e25c779eSMatthew Dillon error = so_pru_soreceive(sp->soc, NULL, uio, NULL, NULL, &flags);
343e25c779eSMatthew Dillon
344e25c779eSMatthew Dillon if(error)
345bfc09ba0SMatthew Dillon debug(2, "error=%d so_error=%d uio->uio_resid=%zd iov.iov_len=%zd",
346e25c779eSMatthew Dillon error,
347e25c779eSMatthew Dillon sp->soc->so_error, uio->uio_resid, iov->iov_len);
348e25c779eSMatthew Dillon if(!error && (uio->uio_resid > 0)) {
349e25c779eSMatthew Dillon error = EPIPE; // was EAGAIN
350bfc09ba0SMatthew Dillon debug(2, "error=%d so_error=%d uio->uio_resid=%zd iov.iov_len=%zd "
351bfc09ba0SMatthew Dillon "so_state=%x",
352e25c779eSMatthew Dillon error,
353bfc09ba0SMatthew Dillon sp->soc->so_error, uio->uio_resid, iov->iov_len,
354bfc09ba0SMatthew Dillon sp->soc->so_state);
355e25c779eSMatthew Dillon }
356e25c779eSMatthew Dillon
357e25c779eSMatthew Dillon return error;
358e25c779eSMatthew Dillon }
359e25c779eSMatthew Dillon
360e25c779eSMatthew Dillon /*
361e25c779eSMatthew Dillon | so_recv gets called when there is at least
362e25c779eSMatthew Dillon | an iSCSI header in the queue
363e25c779eSMatthew Dillon */
364e25c779eSMatthew Dillon static int
so_recv(isc_session_t * sp,pduq_t * pq)365e25c779eSMatthew Dillon so_recv(isc_session_t *sp, pduq_t *pq)
366e25c779eSMatthew Dillon {
367e25c779eSMatthew Dillon struct socket *so = sp->soc;
368e25c779eSMatthew Dillon sn_t *sn = &sp->sn;
369e25c779eSMatthew Dillon struct uio *uio = &pq->uio;
370e25c779eSMatthew Dillon struct sockbuf sbp;
371e25c779eSMatthew Dillon pdu_t *pp;
372e25c779eSMatthew Dillon int error;
373e25c779eSMatthew Dillon size_t n, len;
374e25c779eSMatthew Dillon bhs_t *bhs;
375e25c779eSMatthew Dillon u_int max, exp;
376e25c779eSMatthew Dillon
377e25c779eSMatthew Dillon debug_called(8);
378e25c779eSMatthew Dillon /*
379e25c779eSMatthew Dillon | now calculate how much data should be in the buffer
380e25c779eSMatthew Dillon | NOTE: digest is not verified/calculated - yet
381e25c779eSMatthew Dillon */
382e25c779eSMatthew Dillon pp = &pq->pdu;
383e25c779eSMatthew Dillon bhs = &pp->ipdu.bhs;
384e25c779eSMatthew Dillon
385e25c779eSMatthew Dillon sbinit(&sbp, 0);
386e25c779eSMatthew Dillon len = 0;
387e25c779eSMatthew Dillon if(bhs->AHSLength) {
388e25c779eSMatthew Dillon pp->ahs_len = bhs->AHSLength * 4;
389e25c779eSMatthew Dillon len += pp->ahs_len;
390e25c779eSMatthew Dillon }
391e25c779eSMatthew Dillon if(sp->hdrDigest)
392e25c779eSMatthew Dillon len += 4;
393e25c779eSMatthew Dillon if(bhs->DSLength) {
394e25c779eSMatthew Dillon n = bhs->DSLength;
395e25c779eSMatthew Dillon #if BYTE_ORDER == LITTLE_ENDIAN
396e25c779eSMatthew Dillon pp->ds_len = ((n & 0x00ff0000) >> 16)
397e25c779eSMatthew Dillon | (n & 0x0000ff00)
398e25c779eSMatthew Dillon | ((n & 0x000000ff) << 16);
399e25c779eSMatthew Dillon #else
400e25c779eSMatthew Dillon pp->ds_len = n;
401e25c779eSMatthew Dillon #endif
402e25c779eSMatthew Dillon len += pp->ds_len;
403e25c779eSMatthew Dillon while(len & 03)
404e25c779eSMatthew Dillon len++;
405e25c779eSMatthew Dillon if(sp->dataDigest)
406e25c779eSMatthew Dillon len += 4;
407e25c779eSMatthew Dillon }
408e25c779eSMatthew Dillon
409e25c779eSMatthew Dillon if((sp->opt.maxRecvDataSegmentLength > 0) && (len > sp->opt.maxRecvDataSegmentLength)) {
410e25c779eSMatthew Dillon #if 0
411e25c779eSMatthew Dillon xdebug("impossible PDU length(%d) opt.maxRecvDataSegmentLength=%d",
412e25c779eSMatthew Dillon len, sp->opt.maxRecvDataSegmentLength);
413e25c779eSMatthew Dillon // deep trouble here, probably all we can do is
414e25c779eSMatthew Dillon // force a disconnect, XXX: check RFC ...
415e25c779eSMatthew Dillon log(LOG_ERR,
416e25c779eSMatthew Dillon "so_recv: impossible PDU length(%ld) from iSCSI %s/%s\n",
417e25c779eSMatthew Dillon len, sp->opt.targetAddress, sp->opt.targetName);
418e25c779eSMatthew Dillon #endif
419e25c779eSMatthew Dillon /*
420e25c779eSMatthew Dillon | XXX: this will really screwup the stream.
421e25c779eSMatthew Dillon | should clear up the buffer till a valid header
422e25c779eSMatthew Dillon | is found, or just close connection ...
423e25c779eSMatthew Dillon | should read the RFC.
424e25c779eSMatthew Dillon */
425e25c779eSMatthew Dillon error = E2BIG;
426e25c779eSMatthew Dillon goto out;
427e25c779eSMatthew Dillon }
428e25c779eSMatthew Dillon if(len) {
429e25c779eSMatthew Dillon int flags;
430e25c779eSMatthew Dillon
431e25c779eSMatthew Dillon sbp.sb_climit = len;
432e25c779eSMatthew Dillon uio->uio_td = curthread; // why ...
433e25c779eSMatthew Dillon if(sp->douio) {
434e25c779eSMatthew Dillon // it's more efficient to use mbufs -- why?
435e25c779eSMatthew Dillon if(bhs->opcode == ISCSI_READ_DATA) {
436e25c779eSMatthew Dillon pduq_t *opq;
437e25c779eSMatthew Dillon
438e25c779eSMatthew Dillon opq = i_search_hld(sp, pq->pdu.ipdu.bhs.itt, 1);
439e25c779eSMatthew Dillon if(opq != NULL) {
440e25c779eSMatthew Dillon union ccb *ccb = opq->ccb;
441e25c779eSMatthew Dillon struct ccb_scsiio *csio = &ccb->csio;
442e25c779eSMatthew Dillon pdu_t *opp = &opq->pdu;
443e25c779eSMatthew Dillon scsi_req_t *cmd = &opp->ipdu.scsi_req;
444e25c779eSMatthew Dillon data_in_t *rcmd = &pq->pdu.ipdu.data_in;
445e25c779eSMatthew Dillon bhs_t *bhp = &opp->ipdu.bhs;
446e25c779eSMatthew Dillon int r;
447e25c779eSMatthew Dillon
448e25c779eSMatthew Dillon if(bhp->opcode == ISCSI_SCSI_CMD
449e25c779eSMatthew Dillon && cmd->R
450e25c779eSMatthew Dillon && (ntohl(cmd->edtlen) >= pq->pdu.ds_len)) {
451e25c779eSMatthew Dillon struct iovec *iov = pq->iov;
452e25c779eSMatthew Dillon iov->iov_base = csio->data_ptr + ntohl(rcmd->bo);
453e25c779eSMatthew Dillon iov->iov_len = pq->pdu.ds_len;
454e25c779eSMatthew Dillon
455e25c779eSMatthew Dillon uio->uio_rw = UIO_READ;
456e25c779eSMatthew Dillon uio->uio_segflg = UIO_SYSSPACE;
457e25c779eSMatthew Dillon uio->uio_iov = iov;
458e25c779eSMatthew Dillon uio->uio_iovcnt = 1;
459e25c779eSMatthew Dillon if(len > pq->pdu.ds_len) {
460e25c779eSMatthew Dillon pq->iov[1].iov_base = &r;
461e25c779eSMatthew Dillon pq->iov[1].iov_len = len - pq->pdu.ds_len;
462e25c779eSMatthew Dillon uio->uio_iovcnt++;
463e25c779eSMatthew Dillon }
464e25c779eSMatthew Dillon
465e25c779eSMatthew Dillon sdebug(4, "uio_resid=0x%zx itt=0x%x bp=%p bo=%x len=%x/%x",
466e25c779eSMatthew Dillon uio->uio_resid,
467e25c779eSMatthew Dillon ntohl(pq->pdu.ipdu.bhs.itt),
468e25c779eSMatthew Dillon csio->data_ptr, ntohl(rcmd->bo), ntohl(cmd->edtlen), pq->pdu.ds_len);
469e25c779eSMatthew Dillon }
470e25c779eSMatthew Dillon }
471e25c779eSMatthew Dillon }
472e25c779eSMatthew Dillon }
473e25c779eSMatthew Dillon /*
474e25c779eSMatthew Dillon * Here we call so_pru_receive with a sockbuf so we can obtain
475e25c779eSMatthew Dillon * the mbuf chain that can be assigned later to the pq->mp,
476e25c779eSMatthew Dillon * which is the mbuf wanted.
477e25c779eSMatthew Dillon * For the moment, resid will be saved in the uio.
478e25c779eSMatthew Dillon */
479e25c779eSMatthew Dillon flags = MSG_WAITALL;
480e25c779eSMatthew Dillon error = so_pru_soreceive(so, NULL, NULL, &sbp, NULL, &flags);
481e25c779eSMatthew Dillon pq->mp = sbp.sb_mb;
482e25c779eSMatthew Dillon uio->uio_resid = sbp.sb_climit - sbp.sb_cc;
483e25c779eSMatthew Dillon //if(error == EAGAIN)
484e25c779eSMatthew Dillon // XXX: this needs work! it hangs iscontrol
485e25c779eSMatthew Dillon if(error || uio->uio_resid)
486e25c779eSMatthew Dillon goto out;
487e25c779eSMatthew Dillon }
488e25c779eSMatthew Dillon pq->len += len;
489e25c779eSMatthew Dillon sdebug(6, "len=%d] opcode=0x%x ahs_len=0x%x ds_len=0x%x",
490e25c779eSMatthew Dillon pq->len, bhs->opcode, pp->ahs_len, pp->ds_len);
491e25c779eSMatthew Dillon
492e25c779eSMatthew Dillon max = ntohl(bhs->MaxCmdSN);
493e25c779eSMatthew Dillon exp = ntohl(bhs->ExpStSN);
494e25c779eSMatthew Dillon
495e25c779eSMatthew Dillon if(max < exp - 1 &&
496e25c779eSMatthew Dillon max > exp - _MAXINCR) {
497e25c779eSMatthew Dillon sdebug(2, "bad cmd window size");
498e25c779eSMatthew Dillon error = EIO; // XXX: for now;
499e25c779eSMatthew Dillon goto out; // error
500e25c779eSMatthew Dillon }
501e25c779eSMatthew Dillon
502e25c779eSMatthew Dillon if(SNA_GT(max, sn->maxCmd))
503e25c779eSMatthew Dillon sn->maxCmd = max;
504e25c779eSMatthew Dillon
505e25c779eSMatthew Dillon if(SNA_GT(exp, sn->expCmd))
506e25c779eSMatthew Dillon sn->expCmd = exp;
507e25c779eSMatthew Dillon
508e25c779eSMatthew Dillon sp->cws = sn->maxCmd - sn->expCmd + 1;
509e25c779eSMatthew Dillon
510e25c779eSMatthew Dillon return 0;
511e25c779eSMatthew Dillon
512e25c779eSMatthew Dillon out:
513e25c779eSMatthew Dillon // XXX: need some work here
514e25c779eSMatthew Dillon xdebug("have a problem, error=%d", error);
515e25c779eSMatthew Dillon pdu_free(sp->isc, pq);
516e25c779eSMatthew Dillon if(!error && uio->uio_resid > 0)
517e25c779eSMatthew Dillon error = EPIPE;
518e25c779eSMatthew Dillon return error;
519e25c779eSMatthew Dillon }
520e25c779eSMatthew Dillon
521e25c779eSMatthew Dillon /*
522e25c779eSMatthew Dillon | wait for something to arrive.
523e25c779eSMatthew Dillon | and if the pdu is without errors, process it.
524e25c779eSMatthew Dillon */
525e25c779eSMatthew Dillon static int
so_input(isc_session_t * sp)526e25c779eSMatthew Dillon so_input(isc_session_t *sp)
527e25c779eSMatthew Dillon {
528e25c779eSMatthew Dillon pduq_t *pq;
529e25c779eSMatthew Dillon int error;
530e25c779eSMatthew Dillon
531e25c779eSMatthew Dillon debug_called(8);
532e25c779eSMatthew Dillon /*
533e25c779eSMatthew Dillon | first read in the iSCSI header
534e25c779eSMatthew Dillon */
535e25c779eSMatthew Dillon error = so_getbhs(sp);
536e25c779eSMatthew Dillon if(error == 0) {
537e25c779eSMatthew Dillon /*
538e25c779eSMatthew Dillon | now read the rest.
539e25c779eSMatthew Dillon */
540e25c779eSMatthew Dillon pq = pdu_alloc(sp->isc, M_NOWAIT);
541e25c779eSMatthew Dillon if(pq == NULL) { // XXX: might cause a deadlock ...
542e25c779eSMatthew Dillon debug(3, "out of pdus, wait");
543e25c779eSMatthew Dillon pq = pdu_alloc(sp->isc, M_NOWAIT); // OK to WAIT
544e25c779eSMatthew Dillon }
545e25c779eSMatthew Dillon pq->pdu.ipdu.bhs = sp->bhs;
546e25c779eSMatthew Dillon pq->len = sizeof(bhs_t); // so far only the header was read
547e25c779eSMatthew Dillon error = so_recv(sp, pq);
548e25c779eSMatthew Dillon if(error != 0) {
549e25c779eSMatthew Dillon error += 0x800; // XXX: just to see the error.
550e25c779eSMatthew Dillon // terminal error
551e25c779eSMatthew Dillon // XXX: close connection and exit
552e25c779eSMatthew Dillon }
553e25c779eSMatthew Dillon else {
554e25c779eSMatthew Dillon sp->stats.nrecv++;
555e25c779eSMatthew Dillon getmicrouptime(&sp->stats.t_recv);
556e25c779eSMatthew Dillon ism_recv(sp, pq);
557e25c779eSMatthew Dillon }
558e25c779eSMatthew Dillon }
559e25c779eSMatthew Dillon return error;
560e25c779eSMatthew Dillon }
561e25c779eSMatthew Dillon
562e25c779eSMatthew Dillon /*
563e25c779eSMatthew Dillon | one per active (connected) session.
564e25c779eSMatthew Dillon | this thread is responsible for reading
565e25c779eSMatthew Dillon | in packets from the target.
566e25c779eSMatthew Dillon */
567e25c779eSMatthew Dillon static void
isc_soc(void * vp)568e25c779eSMatthew Dillon isc_soc(void *vp)
569e25c779eSMatthew Dillon {
570e25c779eSMatthew Dillon isc_session_t *sp = (isc_session_t *)vp;
571e25c779eSMatthew Dillon struct socket *so = sp->soc;
572e25c779eSMatthew Dillon int error;
573e25c779eSMatthew Dillon
574cd8ab232SMatthew Dillon get_mplock();
575e25c779eSMatthew Dillon debug_called(8);
576e25c779eSMatthew Dillon
5778650c3d4SMatthew Dillon sp->td = curthread;
578e25c779eSMatthew Dillon if(sp->cam_path)
579e25c779eSMatthew Dillon ic_release(sp);
580e25c779eSMatthew Dillon
581e25c779eSMatthew Dillon error = 0;
582e25c779eSMatthew Dillon while((sp->flags & (ISC_CON_RUN | ISC_LINK_UP)) == (ISC_CON_RUN | ISC_LINK_UP)) {
583e25c779eSMatthew Dillon // XXX: hunting ...
584e25c779eSMatthew Dillon if(sp->soc == NULL || !(so->so_state & SS_ISCONNECTED)) {
585e25c779eSMatthew Dillon debug(2, "sp->soc=%p", sp->soc);
586e25c779eSMatthew Dillon break;
587e25c779eSMatthew Dillon }
588e25c779eSMatthew Dillon error = so_input(sp);
589e25c779eSMatthew Dillon if(error == 0) {
590e25c779eSMatthew Dillon iscsi_lock_ex(&sp->io_mtx);
591e25c779eSMatthew Dillon if(sp->flags & ISC_OWAITING) {
592e25c779eSMatthew Dillon wakeup(&sp->flags);
593e25c779eSMatthew Dillon }
594e25c779eSMatthew Dillon iscsi_unlock_ex(&sp->io_mtx);
595e25c779eSMatthew Dillon } else if(error == EPIPE) {
596e25c779eSMatthew Dillon break;
597e25c779eSMatthew Dillon }
598e25c779eSMatthew Dillon else if(error == EAGAIN) {
599e25c779eSMatthew Dillon if(so->so_state & SS_ISCONNECTED)
600e25c779eSMatthew Dillon // there seems to be a problem in 6.0 ...
601e25c779eSMatthew Dillon tsleep(sp, 0, "iscsoc", 2*hz);
602e25c779eSMatthew Dillon }
603e25c779eSMatthew Dillon }
604e25c779eSMatthew Dillon sdebug(2, "terminated, flags=%x so_state=%x error=%d proc=%p",
605a08a53feSSascha Wildner sp->flags, so ? so->so_state : 0, error, sp->proc);
606e25c779eSMatthew Dillon if((sp->proc != NULL) && sp->signal) {
607e25c779eSMatthew Dillon PROC_LOCK(sp->proc);
608e25c779eSMatthew Dillon ksignal(sp->proc, sp->signal);
609e25c779eSMatthew Dillon PROC_UNLOCK(sp->proc);
610e25c779eSMatthew Dillon sp->flags |= ISC_SIGNALED;
611e25c779eSMatthew Dillon sdebug(2, "pid=%d signaled(%d)", sp->proc->p_pid, sp->signal);
612e25c779eSMatthew Dillon }
613e25c779eSMatthew Dillon else {
614e25c779eSMatthew Dillon // we have to do something ourselves
615e25c779eSMatthew Dillon // like closing this session ...
616e25c779eSMatthew Dillon }
617e25c779eSMatthew Dillon /*
618e25c779eSMatthew Dillon | we've been terminated
619e25c779eSMatthew Dillon */
620e25c779eSMatthew Dillon // do we need this mutex ...?
621e25c779eSMatthew Dillon //iscsi_lock_ex(&sp->io_mtx);
622e25c779eSMatthew Dillon sp->flags &= ~(ISC_CON_RUNNING | ISC_LINK_UP);
623e25c779eSMatthew Dillon wakeup(&sp->soc);
624e25c779eSMatthew Dillon //iscsi_unlock_ex(&sp->io_mtx);
625e25c779eSMatthew Dillon
626e25c779eSMatthew Dillon sdebug(2, "dropped ISC_CON_RUNNING");
627e25c779eSMatthew Dillon
628cd8ab232SMatthew Dillon rel_mplock();
629e25c779eSMatthew Dillon }
630e25c779eSMatthew Dillon
631e25c779eSMatthew Dillon void
isc_stop_receiver(isc_session_t * sp)632e25c779eSMatthew Dillon isc_stop_receiver(isc_session_t *sp)
633e25c779eSMatthew Dillon {
634e25c779eSMatthew Dillon debug_called(8);
6358e44e571SSascha Wildner debug(3, "sp=%p sp->sid=%d sp->soc=%p", sp, sp ? sp->sid : 0,
6368e44e571SSascha Wildner sp ? sp->soc : NULL);
637e25c779eSMatthew Dillon iscsi_lock_ex(&sp->io_mtx);
638e25c779eSMatthew Dillon sp->flags &= ~ISC_LINK_UP;
639e25c779eSMatthew Dillon if (sp->flags & ISC_CON_RUNNING) {
640e25c779eSMatthew Dillon issleep(&sp->soc, &sp->io_mtx, 0, "iscstpc", 5*hz);
641e25c779eSMatthew Dillon }
642e25c779eSMatthew Dillon iscsi_unlock_ex(&sp->io_mtx);
643e25c779eSMatthew Dillon
644e25c779eSMatthew Dillon if (sp->soc)
645e25c779eSMatthew Dillon soshutdown(sp->soc, SHUT_RD);
646e25c779eSMatthew Dillon
647e25c779eSMatthew Dillon iscsi_lock_ex(&sp->io_mtx);
648e25c779eSMatthew Dillon sdebug(3, "soshutdown");
649e25c779eSMatthew Dillon sp->flags &= ~ISC_CON_RUN;
650e25c779eSMatthew Dillon while(sp->flags & ISC_CON_RUNNING) {
651e25c779eSMatthew Dillon sdebug(3, "waiting flags=%x", sp->flags);
652e25c779eSMatthew Dillon issleep(&sp->soc, &sp->io_mtx, 0, "iscstpc", hz);
653e25c779eSMatthew Dillon }
654e25c779eSMatthew Dillon iscsi_unlock_ex(&sp->io_mtx);
655e25c779eSMatthew Dillon
656e25c779eSMatthew Dillon if (sp->fp != NULL) {
657e25c779eSMatthew Dillon fdrop(sp->fp);
658e25c779eSMatthew Dillon sp->fp = NULL;
659e25c779eSMatthew Dillon }
660e25c779eSMatthew Dillon /* sofree(sp->soc); fp deals with socket termination */
661e25c779eSMatthew Dillon sp->soc = NULL;
662e25c779eSMatthew Dillon
663e25c779eSMatthew Dillon sdebug(3, "done");
664e25c779eSMatthew Dillon }
665e25c779eSMatthew Dillon
666e25c779eSMatthew Dillon void
isc_start_receiver(isc_session_t * sp)667e25c779eSMatthew Dillon isc_start_receiver(isc_session_t *sp)
668e25c779eSMatthew Dillon {
669e25c779eSMatthew Dillon debug_called(8);
670e25c779eSMatthew Dillon
671e25c779eSMatthew Dillon sp->flags |= ISC_CON_RUN | ISC_LINK_UP;
672e25c779eSMatthew Dillon sp->flags |= ISC_CON_RUNNING;
673e25c779eSMatthew Dillon
674e25c779eSMatthew Dillon kthread_create(isc_soc, sp, &sp->soc_thr, "iscsi%d", sp->sid);
675e25c779eSMatthew Dillon }
676