2 * Copyright (c) 2005-2008 Daniel Braniss <danny@cs.huji.ac.il>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 | $Id: iscsi.c,v 1.35 2007/04/22 08:58:29 danny Exp danny $
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
35 #include "opt_iscsi_initiator.h"
37 #include <sys/param.h>
38 #include <sys/kernel.h>
39 #include <sys/module.h>
42 #include <sys/systm.h>
43 #include <sys/malloc.h>
44 #include <sys/ctype.h>
45 #include <sys/errno.h>
46 #include <sys/sysctl.h>
49 #include <sys/socketvar.h>
50 #include <sys/socket.h>
51 #include <sys/protosw.h>
53 #include <sys/ioccom.h>
54 #include <sys/queue.h>
55 #include <sys/kthread.h>
57 #include <sys/syslog.h>
60 #include <dev/iscsi/initiator/iscsi.h>
61 #include <dev/iscsi/initiator/iscsivar.h>
63 static char *iscsi_driver_version = "2.1.0";
65 static struct isc_softc isc;
67 MALLOC_DEFINE(M_ISCSI, "iSCSI", "iSCSI driver");
69 #ifdef ISCSI_INITIATOR_DEBUG
70 int iscsi_debug = ISCSI_INITIATOR_DEBUG;
71 SYSCTL_INT(_debug, OID_AUTO, iscsi_initiator, CTLFLAG_RW, &iscsi_debug, 0,
72 "iSCSI driver debug flag");
74 struct mtx iscsi_dbg_mtx;
78 static char isid[6+1] = {
88 static int i_create_session(struct cdev *dev, int *ndev);
90 static int i_ping(struct cdev *dev);
91 static int i_send(struct cdev *dev, caddr_t arg, struct thread *td);
92 static int i_recv(struct cdev *dev, caddr_t arg, struct thread *td);
93 static int i_setsoc(isc_session_t *sp, int fd, struct thread *td);
95 static d_open_t iscsi_open;
96 static d_close_t iscsi_close;
97 static d_ioctl_t iscsi_ioctl;
98 #ifdef ISCSI_INITIATOR_DEBUG
99 static d_read_t iscsi_read;
102 static struct cdevsw iscsi_cdevsw = {
103 .d_version = D_VERSION,
104 .d_open = iscsi_open,
105 .d_close = iscsi_close,
106 .d_ioctl = iscsi_ioctl,
107 #ifdef ISCSI_INITIATOR_DEBUG
108 .d_read = iscsi_read,
114 iscsi_open(struct cdev *dev, int flags, int otype, struct thread *td)
118 debug(7, "dev=%d", dev2unit(dev));
120 if(dev2unit(dev) > MAX_SESSIONS) {
124 if(dev2unit(dev) == MAX_SESSIONS) {
126 struct isc_softc *sc = (struct isc_softc *)dev->si_drv1;
128 // this should be in iscsi_start
129 if(sc->cam_sim == NULL)
137 iscsi_close(struct cdev *dev, int flag, int otyp, struct thread *td)
144 debug(3, "flag=%x", flag);
146 sc = (struct isc *)dev->si_drv1;
147 if(dev2unit(dev) == MAX_SESSIONS) {
150 sp = (isc_session_t *)dev->si_drv2;
152 sdebug(2, "session=%d flags=%x", dev2unit(dev), sp->flags );
154 | if still in full phase, this probably means
155 | that something went realy bad.
156 | it could be a result from 'shutdown', in which case
157 | we will ignore it (so buffers can be flushed).
158 | the problem is that there is no way of differentiating
159 | between a shutdown procedure and 'iscontrol' dying.
161 if(sp->flags & ISC_FFPHASE)
162 // delay in case this is a shutdown.
163 tsleep(sp, PRIBIO, "isc-cls", 60*hz);
171 iscsi_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
178 sc = (struct isc *)dev->si_drv1;
182 if(dev2unit(dev) == MAX_SESSIONS) {
184 | non Session commands
191 error = i_create_session(dev, (int *)arg);
197 error = ENXIO; // XXX:
201 sp = (isc_session_t *)dev->si_drv2;
208 sdebug(6, "dev=%d cmd=%d", dev2unit(dev), (int)(cmd & 0xff));
212 error = i_setsoc(sp, *(u_int *)arg, td);
216 opt = (isc_opt_t *)arg;
217 error = i_setopt(sp, opt);
221 error = i_send(dev, arg, td);
225 error = i_recv(dev, arg, td);
233 error = sp->soc == NULL? ENOTCONN: ism_fullfeature(dev, 1);
235 sp->proc = td->td_proc;
236 SYSCTL_ADD_UINT(&sp->clist,
237 SYSCTL_CHILDREN(sp->oid),
241 &sp->proc->p_pid, sizeof(pid_t), "control process id");
246 error = sp->soc == NULL? ENOTCONN: ism_fullfeature(dev, 2);
250 error = ism_fullfeature(dev, 0);
254 int sig = *(int *)arg;
256 if(sig < 0 || sig > _SIG_MAXSIG)
264 iscsi_cam_t *cp = (iscsi_cam_t *)arg;
266 error = ic_getCamVals(sp, cp);
278 iscsi_read(struct cdev *dev, struct uio *uio, int ioflag)
280 #ifdef ISCSI_INITIATOR_DEBUG
281 struct isc_softc *sc;
286 sc = (struct isc_softc *)dev->si_drv1;
287 sp = (isc_session_t *)dev->si_drv2;
288 if(dev2unit(dev) == MAX_SESSIONS) {
289 sprintf(buf, "/----- Session ------/\n");
290 uiomove(buf, strlen(buf), uio);
293 TAILQ_FOREACH(sp, &sc->isc_sess, sp_link) {
294 if(uio->uio_resid == 0)
296 sprintf(buf, "%03d] '%s' '%s'\n", i++, sp->opt.targetAddress, sp->opt.targetName);
297 uiomove(buf, strlen(buf), uio);
299 sprintf(buf, "%d/%d /---- free -----/\n", sc->npdu_alloc, sc->npdu_max);
301 uiomove(buf, strlen(buf), uio);
302 TAILQ_FOREACH(pq, &sc->freepdu, pq_link) {
303 if(uio->uio_resid == 0)
305 sprintf(buf, "%03d] %06x\n", i++, ntohl(pq->pdu.ipdu.bhs.itt));
306 uiomove(buf, strlen(buf), uio);
311 struct socket *so = sp->soc;
312 #define pukeit(i, pq) do {\
313 sprintf(buf, "%03d] %06x %02x %x %ld %jd\n",\
314 i, ntohl( pq->pdu.ipdu.bhs.CmdSN), \
315 pq->pdu.ipdu.bhs.opcode, ntohl(pq->pdu.ipdu.bhs.itt),\
316 (long)pq->ts.sec, pq->ts.frac);\
319 sprintf(buf, "%d/%d /---- hld -----/\n", sp->stats.nhld, sp->stats.max_hld);
320 uiomove(buf, strlen(buf), uio);
321 TAILQ_FOREACH(pq, &sp->hld, pq_link) {
322 if(uio->uio_resid == 0)
325 uiomove(buf, strlen(buf), uio);
327 sprintf(buf, "%d/%d /---- rsp -----/\n", sp->stats.nrsp, sp->stats.max_rsp);
328 uiomove(buf, strlen(buf), uio);
330 TAILQ_FOREACH(pq, &sp->rsp, pq_link) {
331 if(uio->uio_resid == 0)
334 uiomove(buf, strlen(buf), uio);
336 sprintf(buf, "%d/%d /---- csnd -----/\n", sp->stats.ncsnd, sp->stats.max_csnd);
338 uiomove(buf, strlen(buf), uio);
339 TAILQ_FOREACH(pq, &sp->csnd, pq_link) {
340 if(uio->uio_resid == 0)
343 uiomove(buf, strlen(buf), uio);
345 sprintf(buf, "%d/%d /---- wsnd -----/\n", sp->stats.nwsnd, sp->stats.max_wsnd);
347 uiomove(buf, strlen(buf), uio);
348 TAILQ_FOREACH(pq, &sp->wsnd, pq_link) {
349 if(uio->uio_resid == 0)
352 uiomove(buf, strlen(buf), uio);
354 sprintf(buf, "%d/%d /---- isnd -----/\n", sp->stats.nisnd, sp->stats.max_isnd);
356 uiomove(buf, strlen(buf), uio);
357 TAILQ_FOREACH(pq, &sp->isnd, pq_link) {
358 if(uio->uio_resid == 0)
361 uiomove(buf, strlen(buf), uio);
364 sprintf(buf, "/---- Stats ---/\n");
365 uiomove(buf, strlen(buf), uio);
367 sprintf(buf, "recv=%d sent=%d\n", sp->stats.nrecv, sp->stats.nsent);
368 uiomove(buf, strlen(buf), uio);
370 sprintf(buf, "flags=%x pdus: alloc=%d max=%d\n",
371 sp->flags, sc->npdu_alloc, sc->npdu_max);
372 uiomove(buf, strlen(buf), uio);
374 sprintf(buf, "cws=%d last cmd=%x exp=%x max=%x stat=%x itt=%x\n",
375 sp->cws, sp->sn.cmd, sp->sn.expCmd, sp->sn.maxCmd, sp->sn.stat, sp->sn.itt);
376 uiomove(buf, strlen(buf), uio);
378 sprintf(buf, "/---- socket -----/\nso_count=%d so_state=%x\n", so->so_count, so->so_state);
379 uiomove(buf, strlen(buf), uio);
387 i_ping(struct cdev *dev)
395 i_setsoc(isc_session_t *sp, int fd, struct thread *td)
400 isc_stop_receiver(sp);
402 error = fget(td, fd, &sp->fp);
406 if((error = fgetsock(td, fd, &sp->soc, 0)) == 0) {
408 isc_start_receiver(sp);
419 i_send(struct cdev *dev, caddr_t arg, struct thread *td)
421 isc_session_t *sp = (isc_session_t *)dev->si_drv2;
422 struct isc_softc *sc = (struct isc_softc *)dev->si_drv1;
433 if((pq = pdu_alloc(sc, M_NOWAIT)) == NULL)
436 pq->pdu = *(pdu_t *)arg;
437 if((error = i_prepPDU(sp, pq)) != 0)
440 sdebug(3, "len=%d ahs_len=%d ds_len=%d", pq->len, pp->ahs_len, pp->ds_len);
442 pq->buf = bp = malloc(pq->len - sizeof(union ipdu_u), M_ISCSI, M_NOWAIT);
443 if(pq->buf == NULL) {
449 error = copyin(pp->ahs, bp, n);
451 sdebug(3, "copyin ahs: error=%d", error);
454 pp->ahs = (ahs_t *)bp;
459 error = copyin(pp->ds, bp, n);
461 sdebug(3, "copyin ds: error=%d", error);
472 error = isc_qout(sp, pq);
475 wakeup(&sp->flags); // XXX: to 'push' proc_out ...
485 | NOTE: must calculate digest if requiered.
488 i_recv(struct cdev *dev, caddr_t arg, struct thread *td)
490 isc_session_t *sp = (isc_session_t *)dev->si_drv2;
494 int error, mustfree, cnt;
495 size_t need, have, n;
505 cnt = 6; // XXX: maybe the user can request a time out?
506 mtx_lock(&sp->rsp_mtx);
507 while((pq = TAILQ_FIRST(&sp->rsp)) == NULL) {
508 msleep(&sp->rsp, &sp->rsp_mtx, PRIBIO, "isc_rsp", hz*10);
509 if(cnt-- == 0) break; // XXX: for now, needs work
513 TAILQ_REMOVE(&sp->rsp, pq, pq_link);
515 mtx_unlock(&sp->rsp_mtx);
517 sdebug(4, "cnt=%d", cnt);
521 sdebug(3, "error=%d sp->flags=%x ", error, sp->flags);
549 if(len > pq->mp->m_len) {
551 bp = malloc(len, M_ISCSI, M_WAITOK);
552 sdebug(4, "need mbufcopy: %d", len);
553 i_mbufcopy(pq->mp, bp, len);
556 bp = mtod(pq->mp, caddr_t);
562 n = MIN(up->ahs_size, need);
563 error = copyout(bp, (caddr_t)up->ahs, n);
567 if(!error && pp->ds_len) {
571 if((have = up->ds_size) == 0) {
572 have = up->ahs_size - n;
573 up->ds = (caddr_t)up->ahs + n;
576 error = copyout(bp, (caddr_t)up->ds, n);
584 sdebug(6, "len=%d ahs_len=%d ds_len=%d", pq->len, pp->ahs_len, pp->ds_len);
586 pdu_free(sp->isc, pq);
592 i_create_session(struct cdev *dev, int *ndev)
594 struct isc_softc *sc = (struct isc_softc *)dev->si_drv1;
599 sp = (isc_session_t *)malloc(sizeof *sp, M_ISCSI, M_WAITOK | M_ZERO);
604 | search for the lowest unused sid
606 for(n = 0; n < MAX_SESSIONS; n++)
607 if(sc->sessions[n] == NULL)
609 if(n == MAX_SESSIONS) {
610 mtx_unlock(&sc->mtx);
614 TAILQ_INSERT_TAIL(&sc->isc_sess, sp, sp_link);
616 mtx_unlock(&sc->mtx);
618 sc->sessions[n] = sp;
619 sp->dev = make_dev(&iscsi_cdevsw, n, UID_ROOT, GID_WHEEL, 0600, "iscsi%d", n);
622 sp->dev->si_drv1 = sc;
623 sp->dev->si_drv2 = sp;
625 sp->opt.maxRecvDataSegmentLength = 8192;
626 sp->opt.maxXmitDataSegmentLength = 8192;
628 sp->opt.maxBurstLength = 65536; // 64k
630 sdebug(2, "sessionID=%d sp=%p", n, sp);
631 error = ism_start(sp);
638 iscsi_counters(isc_session_t *sp)
643 #define _puke(i, pq) do {\
644 debug(2, "%03d] %06x %02x %x %ld %jd %x\n",\
645 i, ntohl( pq->pdu.ipdu.bhs.CmdSN), \
646 pq->pdu.ipdu.bhs.opcode, ntohl(pq->pdu.ipdu.bhs.itt),\
647 (long)pq->ts.sec, pq->ts.frac, pq->flags);\
651 TAILQ_FOREACH(pq, &sp->hld, pq_link) {
655 TAILQ_FOREACH(pq, &sp->rsp, pq_link) r++;
656 TAILQ_FOREACH(pq, &sp->csnd, pq_link) s++;
657 TAILQ_FOREACH(pq, &sp->wsnd, pq_link) s++;
658 TAILQ_FOREACH(pq, &sp->isnd, pq_link) s++;
659 debug(2, "hld=%d rsp=%d snd=%d", h, r, s);
664 iscsi_shutdown(void *v)
666 struct isc_softc *sc = (struct isc_softc *)v;
672 xdebug("sc is NULL!");
676 debug(2, "sc->eh is NULL");
678 EVENTHANDLER_DEREGISTER(shutdown_pre_sync, sc->eh);
679 debug(2, "done n=%d", sc->nsess);
682 TAILQ_FOREACH(sp, &sc->isc_sess, sp_link) {
683 debug(2, "%2d] sp->flags=0x%08x", n, sp->flags);
690 init_pdus(struct isc_softc *sc)
694 sc->pdu_zone = uma_zcreate("pdu", sizeof(pduq_t),
695 NULL, NULL, NULL, NULL,
697 if(sc->pdu_zone == NULL) {
698 printf("iscsi_initiator: uma_zcreate failed");
701 uma_zone_set_max(sc->pdu_zone, MAX_PDUS);
702 TAILQ_INIT(&sc->freepdu);
708 free_pdus(struct isc_softc *sc)
714 if(sc->pdu_zone != NULL) {
715 TAILQ_FOREACH(pq, &sc->freepdu, pq_link) {
716 TAILQ_REMOVE(&sc->freepdu, pq, pq_link);
717 uma_zfree(sc->pdu_zone, pq);
719 uma_zdestroy(sc->pdu_zone);
727 struct isc_softc *sc = &isc;
731 memset(sc, 0, sizeof(struct isc_softc));
733 sc->dev = make_dev(&iscsi_cdevsw, MAX_SESSIONS, UID_ROOT, GID_WHEEL, 0600, "iscsi");
734 sc->dev->si_drv1 = sc;
736 TAILQ_INIT(&sc->isc_sess);
737 if(init_pdus(sc) != 0)
738 xdebug("pdu zone init failed!"); // XXX: should cause terminal failure ...
740 mtx_init(&sc->mtx, "iscsi", NULL, MTX_DEF);
741 mtx_init(&sc->pdu_mtx, "iscsi pdu pool", NULL, MTX_DEF);
744 // XXX: this will cause a panic if the
745 // module is loaded too early
752 #ifdef DO_EVENTHANDLER
753 if((sc->eh = EVENTHANDLER_REGISTER(shutdown_pre_sync, iscsi_shutdown,
754 sc, SHUTDOWN_PRI_DEFAULT-1)) == NULL)
755 xdebug("shutdown event registration failed\n");
760 sysctl_ctx_init(&sc->clist);
761 sc->oid = SYSCTL_ADD_NODE(&sc->clist,
762 SYSCTL_STATIC_CHILDREN(_net),
769 SYSCTL_ADD_STRING(&sc->clist,
770 SYSCTL_CHILDREN(sc->oid),
774 iscsi_driver_version,
776 "iscsi driver version");
778 SYSCTL_ADD_STRING(&sc->clist,
779 SYSCTL_CHILDREN(sc->oid),
785 "initiator part of the Session Identifier");
787 SYSCTL_ADD_INT(&sc->clist,
788 SYSCTL_CHILDREN(sc->oid),
794 "number of active session");
796 printf("iscsi: version %s\n", iscsi_driver_version);
801 | unload SHOULD fail if there is activity
802 | activity: there is/are active session/s
807 struct isc_softc *sc = &isc;
808 isc_session_t *sp, *sp_tmp;
813 | go through all the sessions
814 | Note: close should have done this ...
816 TAILQ_FOREACH_SAFE(sp, &sc->isc_sess, sp_link, sp_tmp) {
817 //XXX: check for activity ...
820 if(sc->cam_sim != NULL)
823 mtx_destroy(&sc->mtx);
824 mtx_destroy(&sc->pdu_mtx);
828 destroy_dev(sc->dev);
830 if(sysctl_ctx_free(&sc->clist))
831 xdebug("sysctl_ctx_free failed");
833 iscsi_shutdown(sc); // XXX: check EVENTHANDLER_ ...
837 iscsi_modevent(module_t mod, int what, void *arg)
849 xdebug("iscsi module busy(nsess=%d), cannot unload", isc.nsess);
850 log(LOG_ERR, "iscsi module busy, cannot unload");
867 moduledata_t iscsi_mod = {
869 (modeventhand_t) iscsi_modevent,
878 nfs_setup_diskless();
879 if (nfs_diskless_valid)
880 rootdevnames[0] = "nfs:";
882 printf("** iscsi_rootconf **\n");
885 SYSINIT(cpu_rootconf1, SI_SUB_ROOT_CONF, SI_ORDER_FIRST, iscsi_rootconf, NULL)
888 DECLARE_MODULE(iscsi, iscsi_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
889 MODULE_DEPEND(iscsi, cam, 1, 1, 1);