1171568Sscottl/*- 2234233Sjpaetzel * Copyright (c) 2005-2011 Daniel Braniss <danny@cs.huji.ac.il> 3171568Sscottl * All rights reserved. 4171568Sscottl * 5171568Sscottl * Redistribution and use in source and binary forms, with or without 6171568Sscottl * modification, are permitted provided that the following conditions 7171568Sscottl * are met: 8171568Sscottl * 1. Redistributions of source code must retain the above copyright 9171568Sscottl * notice, this list of conditions and the following disclaimer. 10171568Sscottl * 2. Redistributions in binary form must reproduce the above copyright 11171568Sscottl * notice, this list of conditions and the following disclaimer in the 12171568Sscottl * documentation and/or other materials provided with the distribution. 13171568Sscottl * 14171568Sscottl * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15171568Sscottl * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16171568Sscottl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17171568Sscottl * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18171568Sscottl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19171568Sscottl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20171568Sscottl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21171568Sscottl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22171568Sscottl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23171568Sscottl * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24171568Sscottl * SUCH DAMAGE. 25171568Sscottl * 26171568Sscottl */ 27171568Sscottl/* 28211095Sdes | $Id: iscsi.c 752 2009-08-20 11:23:28Z danny $ 29171568Sscottl */ 30171568Sscottl 31171568Sscottl#include <sys/cdefs.h> 32171568Sscottl__FBSDID("$FreeBSD$"); 33171568Sscottl 34171568Sscottl#include "opt_iscsi_initiator.h" 35171568Sscottl 36171568Sscottl#include <sys/param.h> 37224778Srwatson#include <sys/capability.h> 38171568Sscottl#include <sys/kernel.h> 39171568Sscottl#include <sys/module.h> 40171568Sscottl#include <sys/conf.h> 41171568Sscottl#include <sys/bus.h> 42171568Sscottl#include <sys/systm.h> 43171568Sscottl#include <sys/malloc.h> 44171568Sscottl#include <sys/ctype.h> 45171568Sscottl#include <sys/errno.h> 46171568Sscottl#include <sys/sysctl.h> 47171568Sscottl#include <sys/file.h> 48171568Sscottl#include <sys/uio.h> 49171568Sscottl#include <sys/socketvar.h> 50171568Sscottl#include <sys/socket.h> 51171568Sscottl#include <sys/protosw.h> 52171568Sscottl#include <sys/proc.h> 53171568Sscottl#include <sys/ioccom.h> 54171568Sscottl#include <sys/queue.h> 55171568Sscottl#include <sys/kthread.h> 56171568Sscottl#include <sys/mbuf.h> 57171568Sscottl#include <sys/syslog.h> 58171568Sscottl#include <vm/uma.h> 59211095Sdes#include <sys/sx.h> 60171568Sscottl 61254657Strasz#include <dev/iscsi_initiator/iscsi.h> 62254657Strasz#include <dev/iscsi_initiator/iscsivar.h> 63234233Sjpaetzelstatic char *iscsi_driver_version = "2.3.1"; 64171568Sscottl 65211095Sdesstatic struct isc_softc *isc; 66171568Sscottl 67171568SscottlMALLOC_DEFINE(M_ISCSI, "iSCSI", "iSCSI driver"); 68211095SdesMALLOC_DEFINE(M_ISCSIBUF, "iSCbuf", "iSCSI buffers"); 69227293Sedstatic MALLOC_DEFINE(M_TMP, "iSCtmp", "iSCSI tmp"); 70171568Sscottl 71171568Sscottl#ifdef ISCSI_INITIATOR_DEBUG 72171568Sscottlint iscsi_debug = ISCSI_INITIATOR_DEBUG; 73171568SscottlSYSCTL_INT(_debug, OID_AUTO, iscsi_initiator, CTLFLAG_RW, &iscsi_debug, 0, 74171568Sscottl "iSCSI driver debug flag"); 75171568Sscottl 76171568Sscottlstruct mtx iscsi_dbg_mtx; 77171568Sscottl#endif 78171568Sscottl 79211095Sdesstatic int max_sessions = MAX_SESSIONS; 80211095SdesSYSCTL_INT(_net, OID_AUTO, iscsi_initiator_max_sessions, CTLFLAG_RDTUN, &max_sessions, MAX_SESSIONS, 81211095Sdes "Max sessions allowed"); 82211095Sdesstatic int max_pdus = MAX_PDUS; 83211095SdesSYSCTL_INT(_net, OID_AUTO, iscsi_initiator_max_pdus, CTLFLAG_RDTUN, &max_pdus, MAX_PDUS, 84211095Sdes "Max pdu pool"); 85171568Sscottl 86171568Sscottlstatic char isid[6+1] = { 87171568Sscottl 0x80, 88171568Sscottl 'D', 89171568Sscottl 'I', 90171568Sscottl 'B', 91171568Sscottl '0', 92171568Sscottl '0', 93171568Sscottl 0 94171568Sscottl}; 95171568Sscottl 96171568Sscottlstatic int i_create_session(struct cdev *dev, int *ndev); 97171568Sscottl 98171568Sscottlstatic int i_ping(struct cdev *dev); 99171568Sscottlstatic int i_send(struct cdev *dev, caddr_t arg, struct thread *td); 100171568Sscottlstatic int i_recv(struct cdev *dev, caddr_t arg, struct thread *td); 101171568Sscottlstatic int i_setsoc(isc_session_t *sp, int fd, struct thread *td); 102211095Sdesstatic int i_fullfeature(struct cdev *dev, int flag); 103171568Sscottl 104171568Sscottlstatic d_open_t iscsi_open; 105171568Sscottlstatic d_close_t iscsi_close; 106171568Sscottlstatic d_ioctl_t iscsi_ioctl; 107171568Sscottl#ifdef ISCSI_INITIATOR_DEBUG 108171568Sscottlstatic d_read_t iscsi_read; 109171568Sscottl#endif 110171568Sscottl 111171568Sscottlstatic struct cdevsw iscsi_cdevsw = { 112171568Sscottl .d_version = D_VERSION, 113171568Sscottl .d_open = iscsi_open, 114171568Sscottl .d_close = iscsi_close, 115171568Sscottl .d_ioctl = iscsi_ioctl, 116171568Sscottl#ifdef ISCSI_INITIATOR_DEBUG 117171568Sscottl .d_read = iscsi_read, 118171568Sscottl#endif 119171568Sscottl .d_name = "iSCSI", 120171568Sscottl}; 121171568Sscottl 122171568Sscottlstatic int 123171568Sscottliscsi_open(struct cdev *dev, int flags, int otype, struct thread *td) 124171568Sscottl{ 125171568Sscottl debug_called(8); 126171568Sscottl 127183397Sed debug(7, "dev=%d", dev2unit(dev)); 128171568Sscottl 129211095Sdes if(dev2unit(dev) > max_sessions) { 130171568Sscottl // should not happen 131171568Sscottl return ENODEV; 132171568Sscottl } 133171568Sscottl return 0; 134171568Sscottl} 135171568Sscottl 136171568Sscottlstatic int 137171568Sscottliscsi_close(struct cdev *dev, int flag, int otyp, struct thread *td) 138171568Sscottl{ 139171568Sscottl isc_session_t *sp; 140171568Sscottl 141171568Sscottl debug_called(8); 142171568Sscottl 143211095Sdes debug(3, "session=%d flag=%x", dev2unit(dev), flag); 144171568Sscottl 145211095Sdes if(dev2unit(dev) == max_sessions) { 146171568Sscottl return 0; 147171568Sscottl } 148211095Sdes sp = dev->si_drv2; 149171568Sscottl if(sp != NULL) { 150211095Sdes sdebug(3, "sp->flags=%x", sp->flags ); 151171568Sscottl /* 152171568Sscottl | if still in full phase, this probably means 153171568Sscottl | that something went realy bad. 154171568Sscottl | it could be a result from 'shutdown', in which case 155171568Sscottl | we will ignore it (so buffers can be flushed). 156171568Sscottl | the problem is that there is no way of differentiating 157171568Sscottl | between a shutdown procedure and 'iscontrol' dying. 158171568Sscottl */ 159171568Sscottl if(sp->flags & ISC_FFPHASE) 160171568Sscottl // delay in case this is a shutdown. 161171568Sscottl tsleep(sp, PRIBIO, "isc-cls", 60*hz); 162171568Sscottl ism_stop(sp); 163171568Sscottl } 164171568Sscottl debug(2, "done"); 165171568Sscottl return 0; 166171568Sscottl} 167171568Sscottl 168171568Sscottlstatic int 169171568Sscottliscsi_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int mode, struct thread *td) 170171568Sscottl{ 171211095Sdes struct isc_softc *sc; 172171568Sscottl isc_session_t *sp; 173171568Sscottl isc_opt_t *opt; 174171568Sscottl int error; 175171568Sscottl 176171568Sscottl debug_called(8); 177171568Sscottl 178171568Sscottl error = 0; 179211095Sdes if(dev2unit(dev) == max_sessions) { 180171568Sscottl /* 181171568Sscottl | non Session commands 182171568Sscottl */ 183211095Sdes sc = dev->si_drv1; 184171568Sscottl if(sc == NULL) 185171568Sscottl return ENXIO; 186171568Sscottl 187171568Sscottl switch(cmd) { 188171568Sscottl case ISCSISETSES: 189171568Sscottl error = i_create_session(dev, (int *)arg); 190171568Sscottl if(error == 0) 191211095Sdes break; 192171568Sscottl 193171568Sscottl default: 194211095Sdes error = ENXIO; 195171568Sscottl } 196171568Sscottl return error; 197171568Sscottl } 198171568Sscottl /* 199171568Sscottl | session commands 200171568Sscottl */ 201211095Sdes sp = dev->si_drv2; 202171568Sscottl if(sp == NULL) 203171568Sscottl return ENXIO; 204171568Sscottl 205183397Sed sdebug(6, "dev=%d cmd=%d", dev2unit(dev), (int)(cmd & 0xff)); 206171568Sscottl 207171568Sscottl switch(cmd) { 208171568Sscottl case ISCSISETSOC: 209171568Sscottl error = i_setsoc(sp, *(u_int *)arg, td); 210171568Sscottl break; 211171568Sscottl 212171568Sscottl case ISCSISETOPT: 213171568Sscottl opt = (isc_opt_t *)arg; 214171568Sscottl error = i_setopt(sp, opt); 215171568Sscottl break; 216171568Sscottl 217171568Sscottl case ISCSISEND: 218171568Sscottl error = i_send(dev, arg, td); 219171568Sscottl break; 220171568Sscottl 221171568Sscottl case ISCSIRECV: 222171568Sscottl error = i_recv(dev, arg, td); 223171568Sscottl break; 224171568Sscottl 225171568Sscottl case ISCSIPING: 226171568Sscottl error = i_ping(dev); 227171568Sscottl break; 228171568Sscottl 229171568Sscottl case ISCSISTART: 230211095Sdes error = sp->soc == NULL? ENOTCONN: i_fullfeature(dev, 1); 231171568Sscottl if(error == 0) { 232171568Sscottl sp->proc = td->td_proc; 233217323Smdf SYSCTL_ADD_INT(&sp->clist, SYSCTL_CHILDREN(sp->oid), 234234233Sjpaetzel OID_AUTO, "pid", CTLFLAG_RD, 235234233Sjpaetzel &sp->proc->p_pid, sizeof(pid_t), "control process id"); 236171568Sscottl } 237171568Sscottl break; 238171568Sscottl 239171568Sscottl case ISCSIRESTART: 240211095Sdes error = sp->soc == NULL? ENOTCONN: i_fullfeature(dev, 2); 241171568Sscottl break; 242171568Sscottl 243171568Sscottl case ISCSISTOP: 244211095Sdes error = i_fullfeature(dev, 0); 245171568Sscottl break; 246171568Sscottl 247171568Sscottl case ISCSISIGNAL: { 248171568Sscottl int sig = *(int *)arg; 249171568Sscottl 250171568Sscottl if(sig < 0 || sig > _SIG_MAXSIG) 251171568Sscottl error = EINVAL; 252171568Sscottl else 253171568Sscottl sp->signal = sig; 254171568Sscottl break; 255171568Sscottl } 256171568Sscottl 257171568Sscottl case ISCSIGETCAM: { 258171568Sscottl iscsi_cam_t *cp = (iscsi_cam_t *)arg; 259171568Sscottl 260171568Sscottl error = ic_getCamVals(sp, cp); 261171568Sscottl break; 262171568Sscottl } 263171568Sscottl 264171568Sscottl default: 265171568Sscottl error = ENOIOCTL; 266171568Sscottl } 267171568Sscottl 268171568Sscottl return error; 269171568Sscottl} 270171568Sscottl 271171568Sscottlstatic int 272171568Sscottliscsi_read(struct cdev *dev, struct uio *uio, int ioflag) 273171568Sscottl{ 274171568Sscottl#ifdef ISCSI_INITIATOR_DEBUG 275171568Sscottl struct isc_softc *sc; 276171568Sscottl isc_session_t *sp; 277171568Sscottl pduq_t *pq; 278171568Sscottl char buf[1024]; 279171568Sscottl 280211095Sdes sc = dev->si_drv1; 281211095Sdes sp = dev->si_drv2; 282211095Sdes if(dev2unit(dev) == max_sessions) { 283171568Sscottl sprintf(buf, "/----- Session ------/\n"); 284171568Sscottl uiomove(buf, strlen(buf), uio); 285171568Sscottl int i = 0; 286171568Sscottl 287171568Sscottl TAILQ_FOREACH(sp, &sc->isc_sess, sp_link) { 288171568Sscottl if(uio->uio_resid == 0) 289171568Sscottl return 0; 290171568Sscottl sprintf(buf, "%03d] '%s' '%s'\n", i++, sp->opt.targetAddress, sp->opt.targetName); 291171568Sscottl uiomove(buf, strlen(buf), uio); 292171568Sscottl } 293234233Sjpaetzel sprintf(buf, "free npdu_alloc=%d, npdu_max=%d\n", sc->npdu_alloc, sc->npdu_max); 294185289Sscottl uiomove(buf, strlen(buf), uio); 295171568Sscottl } 296171568Sscottl else { 297171568Sscottl int i = 0; 298171568Sscottl struct socket *so = sp->soc; 299171568Sscottl#define pukeit(i, pq) do {\ 300211182Sjhb sprintf(buf, "%03d] %06x %02x %06x %06x %jd\n",\ 301211095Sdes i, ntohl(pq->pdu.ipdu.bhs.CmdSN),\ 302171568Sscottl pq->pdu.ipdu.bhs.opcode, ntohl(pq->pdu.ipdu.bhs.itt),\ 303211095Sdes ntohl(pq->pdu.ipdu.bhs.ExpStSN),\ 304211182Sjhb (intmax_t)pq->ts.sec);\ 305171568Sscottl } while(0) 306171568Sscottl 307171568Sscottl sprintf(buf, "%d/%d /---- hld -----/\n", sp->stats.nhld, sp->stats.max_hld); 308171568Sscottl uiomove(buf, strlen(buf), uio); 309171568Sscottl TAILQ_FOREACH(pq, &sp->hld, pq_link) { 310171568Sscottl if(uio->uio_resid == 0) 311171568Sscottl return 0; 312171568Sscottl pukeit(i, pq); i++; 313171568Sscottl uiomove(buf, strlen(buf), uio); 314171568Sscottl } 315171568Sscottl sprintf(buf, "%d/%d /---- rsp -----/\n", sp->stats.nrsp, sp->stats.max_rsp); 316171568Sscottl uiomove(buf, strlen(buf), uio); 317171568Sscottl i = 0; 318171568Sscottl TAILQ_FOREACH(pq, &sp->rsp, pq_link) { 319171568Sscottl if(uio->uio_resid == 0) 320171568Sscottl return 0; 321171568Sscottl pukeit(i, pq); i++; 322171568Sscottl uiomove(buf, strlen(buf), uio); 323171568Sscottl } 324171568Sscottl sprintf(buf, "%d/%d /---- csnd -----/\n", sp->stats.ncsnd, sp->stats.max_csnd); 325171568Sscottl i = 0; 326171568Sscottl uiomove(buf, strlen(buf), uio); 327171568Sscottl TAILQ_FOREACH(pq, &sp->csnd, pq_link) { 328171568Sscottl if(uio->uio_resid == 0) 329171568Sscottl return 0; 330171568Sscottl pukeit(i, pq); i++; 331171568Sscottl uiomove(buf, strlen(buf), uio); 332171568Sscottl } 333171568Sscottl sprintf(buf, "%d/%d /---- wsnd -----/\n", sp->stats.nwsnd, sp->stats.max_wsnd); 334171568Sscottl i = 0; 335171568Sscottl uiomove(buf, strlen(buf), uio); 336171568Sscottl TAILQ_FOREACH(pq, &sp->wsnd, pq_link) { 337171568Sscottl if(uio->uio_resid == 0) 338171568Sscottl return 0; 339171568Sscottl pukeit(i, pq); i++; 340171568Sscottl uiomove(buf, strlen(buf), uio); 341171568Sscottl } 342171568Sscottl sprintf(buf, "%d/%d /---- isnd -----/\n", sp->stats.nisnd, sp->stats.max_isnd); 343171568Sscottl i = 0; 344171568Sscottl uiomove(buf, strlen(buf), uio); 345171568Sscottl TAILQ_FOREACH(pq, &sp->isnd, pq_link) { 346171568Sscottl if(uio->uio_resid == 0) 347171568Sscottl return 0; 348171568Sscottl pukeit(i, pq); i++; 349171568Sscottl uiomove(buf, strlen(buf), uio); 350171568Sscottl } 351171568Sscottl 352171568Sscottl sprintf(buf, "/---- Stats ---/\n"); 353171568Sscottl uiomove(buf, strlen(buf), uio); 354171568Sscottl 355171568Sscottl sprintf(buf, "recv=%d sent=%d\n", sp->stats.nrecv, sp->stats.nsent); 356171568Sscottl uiomove(buf, strlen(buf), uio); 357171568Sscottl 358171568Sscottl sprintf(buf, "flags=%x pdus: alloc=%d max=%d\n", 359171568Sscottl sp->flags, sc->npdu_alloc, sc->npdu_max); 360171568Sscottl uiomove(buf, strlen(buf), uio); 361171568Sscottl 362171568Sscottl sprintf(buf, "cws=%d last cmd=%x exp=%x max=%x stat=%x itt=%x\n", 363171568Sscottl sp->cws, sp->sn.cmd, sp->sn.expCmd, sp->sn.maxCmd, sp->sn.stat, sp->sn.itt); 364171568Sscottl uiomove(buf, strlen(buf), uio); 365171568Sscottl 366171568Sscottl sprintf(buf, "/---- socket -----/\nso_count=%d so_state=%x\n", so->so_count, so->so_state); 367171568Sscottl uiomove(buf, strlen(buf), uio); 368171568Sscottl 369171568Sscottl } 370171568Sscottl#endif 371171568Sscottl return 0; 372171568Sscottl} 373171568Sscottl 374171568Sscottlstatic int 375171568Sscottli_ping(struct cdev *dev) 376171568Sscottl{ 377171568Sscottl return 0; 378171568Sscottl} 379171568Sscottl/* 380171568Sscottl | low level I/O 381171568Sscottl */ 382171568Sscottlstatic int 383171568Sscottli_setsoc(isc_session_t *sp, int fd, struct thread *td) 384171568Sscottl{ 385255219Spjd cap_rights_t rights; 386171568Sscottl int error = 0; 387171568Sscottl 388171568Sscottl if(sp->soc != NULL) 389171568Sscottl isc_stop_receiver(sp); 390171568Sscottl 391255219Spjd error = fget(td, fd, cap_rights_init(&rights, CAP_SOCK_CLIENT), &sp->fp); 392171568Sscottl if(error) 393171568Sscottl return error; 394171568Sscottl 395255219Spjd error = fgetsock(td, fd, cap_rights_init(&rights, CAP_SOCK_CLIENT), 396255219Spjd &sp->soc, 0); 397255219Spjd if(error == 0) { 398171568Sscottl sp->td = td; 399171568Sscottl isc_start_receiver(sp); 400171568Sscottl } 401171568Sscottl else { 402171568Sscottl fdrop(sp->fp, td); 403171568Sscottl sp->fp = NULL; 404171568Sscottl } 405171568Sscottl 406171568Sscottl return error; 407171568Sscottl} 408171568Sscottl 409171568Sscottlstatic int 410171568Sscottli_send(struct cdev *dev, caddr_t arg, struct thread *td) 411171568Sscottl{ 412211095Sdes isc_session_t *sp = dev->si_drv2; 413171568Sscottl caddr_t bp; 414171568Sscottl pduq_t *pq; 415171568Sscottl pdu_t *pp; 416171568Sscottl int n, error; 417171568Sscottl 418171568Sscottl debug_called(8); 419171568Sscottl 420171568Sscottl if(sp->soc == NULL) 421171568Sscottl return ENOTCONN; 422171568Sscottl 423211095Sdes if((pq = pdu_alloc(sp->isc, M_NOWAIT)) == NULL) 424171568Sscottl return EAGAIN; 425171568Sscottl pp = &pq->pdu; 426171568Sscottl pq->pdu = *(pdu_t *)arg; 427171568Sscottl if((error = i_prepPDU(sp, pq)) != 0) 428185289Sscottl goto out; 429171568Sscottl 430211095Sdes bp = NULL; 431211095Sdes if((pq->len - sizeof(union ipdu_u)) > 0) { 432211095Sdes pq->buf = bp = malloc(pq->len - sizeof(union ipdu_u), M_ISCSIBUF, M_NOWAIT); 433211095Sdes if(pq->buf == NULL) { 434211095Sdes error = EAGAIN; 435211095Sdes goto out; 436211095Sdes } 437211095Sdes } 438211095Sdes else 439211095Sdes pq->buf = NULL; // just in case? 440171568Sscottl 441211095Sdes sdebug(2, "len=%d ahs_len=%d ds_len=%d buf=%zu@%p", 442211095Sdes pq->len, pp->ahs_len, pp->ds_len, pq->len - sizeof(union ipdu_u), bp); 443211095Sdes 444171568Sscottl if(pp->ahs_len) { 445211095Sdes // XXX: never tested, looks suspicious 446171568Sscottl n = pp->ahs_len; 447211095Sdes error = copyin(pp->ahs_addr, bp, n); 448185289Sscottl if(error != 0) { 449185289Sscottl sdebug(3, "copyin ahs: error=%d", error); 450185289Sscottl goto out; 451185289Sscottl } 452211095Sdes pp->ahs_addr = (ahs_t *)bp; 453171568Sscottl bp += n; 454171568Sscottl } 455171568Sscottl if(pp->ds_len) { 456171568Sscottl n = pp->ds_len; 457211095Sdes error = copyin(pp->ds_addr, bp, n); 458185289Sscottl if(error != 0) { 459185289Sscottl sdebug(3, "copyin ds: error=%d", error); 460185289Sscottl goto out; 461185289Sscottl } 462211095Sdes pp->ds_addr = bp; 463171568Sscottl bp += n; 464171568Sscottl while(n & 03) { 465171568Sscottl n++; 466171568Sscottl *bp++ = 0; 467171568Sscottl } 468171568Sscottl } 469171568Sscottl 470171568Sscottl error = isc_qout(sp, pq); 471185289Sscottl if(error == 0) 472185289Sscottl wakeup(&sp->flags); // XXX: to 'push' proc_out ... 473185289Sscottlout: 474185289Sscottl if(error) 475211095Sdes pdu_free(sp->isc, pq); 476171568Sscottl 477171568Sscottl return error; 478171568Sscottl} 479171568Sscottl 480171568Sscottlstatic int 481171568Sscottli_recv(struct cdev *dev, caddr_t arg, struct thread *td) 482171568Sscottl{ 483211095Sdes isc_session_t *sp = dev->si_drv2; 484171568Sscottl pduq_t *pq; 485171568Sscottl pdu_t *pp, *up; 486171568Sscottl caddr_t bp; 487171568Sscottl int error, mustfree, cnt; 488171568Sscottl size_t need, have, n; 489171568Sscottl 490171568Sscottl debug_called(8); 491171568Sscottl 492171568Sscottl if(sp == NULL) 493171568Sscottl return EIO; 494171568Sscottl 495171568Sscottl if(sp->soc == NULL) 496171568Sscottl return ENOTCONN; 497171568Sscottl cnt = 6; // XXX: maybe the user can request a time out? 498171568Sscottl mtx_lock(&sp->rsp_mtx); 499171568Sscottl while((pq = TAILQ_FIRST(&sp->rsp)) == NULL) { 500171568Sscottl msleep(&sp->rsp, &sp->rsp_mtx, PRIBIO, "isc_rsp", hz*10); 501171568Sscottl if(cnt-- == 0) break; // XXX: for now, needs work 502171568Sscottl } 503171568Sscottl if(pq != NULL) { 504171568Sscottl sp->stats.nrsp--; 505171568Sscottl TAILQ_REMOVE(&sp->rsp, pq, pq_link); 506171568Sscottl } 507171568Sscottl mtx_unlock(&sp->rsp_mtx); 508171568Sscottl 509211095Sdes sdebug(6, "cnt=%d", cnt); 510171568Sscottl 511171568Sscottl if(pq == NULL) { 512171568Sscottl error = ENOTCONN; 513171568Sscottl sdebug(3, "error=%d sp->flags=%x ", error, sp->flags); 514171568Sscottl return error; 515171568Sscottl } 516171568Sscottl up = (pdu_t *)arg; 517171568Sscottl pp = &pq->pdu; 518171568Sscottl up->ipdu = pp->ipdu; 519171568Sscottl n = 0; 520171568Sscottl up->ds_len = 0; 521171568Sscottl up->ahs_len = 0; 522171568Sscottl error = 0; 523171568Sscottl 524171568Sscottl if(pq->mp) { 525171568Sscottl u_int len; 526171568Sscottl 527171568Sscottl // Grr... 528171568Sscottl len = 0; 529171568Sscottl if(pp->ahs_len) { 530171568Sscottl len += pp->ahs_len; 531171568Sscottl } 532171568Sscottl if(pp->ds_len) { 533171568Sscottl len += pp->ds_len; 534171568Sscottl } 535171568Sscottl 536171568Sscottl mustfree = 0; 537171568Sscottl if(len > pq->mp->m_len) { 538171568Sscottl mustfree++; 539211095Sdes bp = malloc(len, M_TMP, M_WAITOK); 540171568Sscottl sdebug(4, "need mbufcopy: %d", len); 541171568Sscottl i_mbufcopy(pq->mp, bp, len); 542171568Sscottl } 543171568Sscottl else 544171568Sscottl bp = mtod(pq->mp, caddr_t); 545171568Sscottl 546171568Sscottl if(pp->ahs_len) { 547171568Sscottl need = pp->ahs_len; 548171568Sscottl n = MIN(up->ahs_size, need); 549211095Sdes error = copyout(bp, (caddr_t)up->ahs_addr, n); 550171568Sscottl up->ahs_len = n; 551171568Sscottl bp += need; 552171568Sscottl } 553171568Sscottl if(!error && pp->ds_len) { 554171568Sscottl need = pp->ds_len; 555171568Sscottl if((have = up->ds_size) == 0) { 556171568Sscottl have = up->ahs_size - n; 557211095Sdes up->ds_addr = (caddr_t)up->ahs_addr + n; 558171568Sscottl } 559171568Sscottl n = MIN(have, need); 560211095Sdes error = copyout(bp, (caddr_t)up->ds_addr, n); 561171568Sscottl up->ds_len = n; 562171568Sscottl } 563171568Sscottl 564171568Sscottl if(mustfree) 565211095Sdes free(bp, M_TMP); 566171568Sscottl } 567171568Sscottl 568171568Sscottl sdebug(6, "len=%d ahs_len=%d ds_len=%d", pq->len, pp->ahs_len, pp->ds_len); 569171568Sscottl 570171568Sscottl pdu_free(sp->isc, pq); 571171568Sscottl 572171568Sscottl return error; 573171568Sscottl} 574171568Sscottl 575171568Sscottlstatic int 576211095Sdesi_fullfeature(struct cdev *dev, int flag) 577211095Sdes{ 578211095Sdes isc_session_t *sp = dev->si_drv2; 579211095Sdes int error; 580211095Sdes 581211095Sdes sdebug(2, "flag=%d", flag); 582211095Sdes 583211095Sdes error = 0; 584211095Sdes switch(flag) { 585211095Sdes case 0: // stop 586211095Sdes sp->flags &= ~ISC_FFPHASE; 587211095Sdes break; 588211095Sdes case 1: // start 589211095Sdes sp->flags |= ISC_FFPHASE; 590211095Sdes error = ic_init(sp); 591211095Sdes break; 592211095Sdes case 2: // restart 593211095Sdes sp->flags |= ISC_FFPHASE; 594211095Sdes ism_restart(sp); 595211095Sdes break; 596211095Sdes } 597211095Sdes return error; 598211095Sdes} 599211095Sdes 600211095Sdesstatic int 601171568Sscottli_create_session(struct cdev *dev, int *ndev) 602171568Sscottl{ 603211095Sdes struct isc_softc *sc = dev->si_drv1; 604171568Sscottl isc_session_t *sp; 605171568Sscottl int error, n; 606171568Sscottl 607171568Sscottl debug_called(8); 608211095Sdes 609211095Sdes sp = malloc(sizeof(isc_session_t), M_ISCSI, M_WAITOK | M_ZERO); 610171568Sscottl if(sp == NULL) 611171568Sscottl return ENOMEM; 612211095Sdes 613211095Sdes sx_xlock(&sc->unit_sx); 614211095Sdes if((n = alloc_unr(sc->unit)) < 0) { 615211095Sdes sx_unlock(&sc->unit_sx); 616171568Sscottl free(sp, M_ISCSI); 617211095Sdes xdebug("too many sessions!"); 618171568Sscottl return EPERM; 619171568Sscottl } 620211095Sdes sx_unlock(&sc->unit_sx); 621211095Sdes 622211095Sdes mtx_lock(&sc->isc_mtx); 623171568Sscottl TAILQ_INSERT_TAIL(&sc->isc_sess, sp, sp_link); 624211095Sdes isc->nsess++; 625211095Sdes mtx_unlock(&sc->isc_mtx); 626171568Sscottl 627171568Sscottl sp->dev = make_dev(&iscsi_cdevsw, n, UID_ROOT, GID_WHEEL, 0600, "iscsi%d", n); 628171568Sscottl *ndev = sp->sid = n; 629171568Sscottl sp->isc = sc; 630171568Sscottl sp->dev->si_drv1 = sc; 631171568Sscottl sp->dev->si_drv2 = sp; 632171568Sscottl 633171568Sscottl sp->opt.maxRecvDataSegmentLength = 8192; 634171568Sscottl sp->opt.maxXmitDataSegmentLength = 8192; 635171568Sscottl sp->opt.maxBurstLength = 65536; // 64k 636211095Sdes sp->opt.maxluns = ISCSI_MAX_LUNS; 637171568Sscottl 638171568Sscottl error = ism_start(sp); 639171568Sscottl 640171568Sscottl return error; 641171568Sscottl} 642171568Sscottl 643171568Sscottl#ifdef notused 644171568Sscottlstatic void 645171568Sscottliscsi_counters(isc_session_t *sp) 646171568Sscottl{ 647171568Sscottl int h, r, s; 648171568Sscottl pduq_t *pq; 649171568Sscottl 650171568Sscottl#define _puke(i, pq) do {\ 651171568Sscottl debug(2, "%03d] %06x %02x %x %ld %jd %x\n",\ 652171568Sscottl i, ntohl( pq->pdu.ipdu.bhs.CmdSN), \ 653171568Sscottl pq->pdu.ipdu.bhs.opcode, ntohl(pq->pdu.ipdu.bhs.itt),\ 654171568Sscottl (long)pq->ts.sec, pq->ts.frac, pq->flags);\ 655171568Sscottl } while(0) 656171568Sscottl 657171568Sscottl h = r = s = 0; 658171568Sscottl TAILQ_FOREACH(pq, &sp->hld, pq_link) { 659171568Sscottl _puke(h, pq); 660171568Sscottl h++; 661171568Sscottl } 662171568Sscottl TAILQ_FOREACH(pq, &sp->rsp, pq_link) r++; 663171568Sscottl TAILQ_FOREACH(pq, &sp->csnd, pq_link) s++; 664171568Sscottl TAILQ_FOREACH(pq, &sp->wsnd, pq_link) s++; 665171568Sscottl TAILQ_FOREACH(pq, &sp->isnd, pq_link) s++; 666171568Sscottl debug(2, "hld=%d rsp=%d snd=%d", h, r, s); 667171568Sscottl} 668171568Sscottl#endif 669171568Sscottl 670171568Sscottlstatic void 671171568Sscottliscsi_shutdown(void *v) 672171568Sscottl{ 673211095Sdes struct isc_softc *sc = v; 674171568Sscottl isc_session_t *sp; 675171568Sscottl int n; 676171568Sscottl 677171568Sscottl debug_called(8); 678171568Sscottl if(sc == NULL) { 679171568Sscottl xdebug("sc is NULL!"); 680171568Sscottl return; 681171568Sscottl } 682211095Sdes#ifdef DO_EVENTHANDLER 683171568Sscottl if(sc->eh == NULL) 684185289Sscottl debug(2, "sc->eh is NULL"); 685171568Sscottl else { 686171568Sscottl EVENTHANDLER_DEREGISTER(shutdown_pre_sync, sc->eh); 687171568Sscottl debug(2, "done n=%d", sc->nsess); 688171568Sscottl } 689211095Sdes#endif 690171568Sscottl n = 0; 691171568Sscottl TAILQ_FOREACH(sp, &sc->isc_sess, sp_link) { 692171568Sscottl debug(2, "%2d] sp->flags=0x%08x", n, sp->flags); 693171568Sscottl n++; 694171568Sscottl } 695185289Sscottl debug(2, "done"); 696171568Sscottl} 697171568Sscottl 698171568Sscottlstatic void 699185289Sscottlfree_pdus(struct isc_softc *sc) 700185289Sscottl{ 701185289Sscottl debug_called(8); 702185289Sscottl 703185289Sscottl if(sc->pdu_zone != NULL) { 704185289Sscottl uma_zdestroy(sc->pdu_zone); 705185289Sscottl sc->pdu_zone = NULL; 706185289Sscottl } 707185289Sscottl} 708185289Sscottl 709255855Straszstatic int 710171568Sscottliscsi_start(void) 711171568Sscottl{ 712171568Sscottl debug_called(8); 713171568Sscottl 714211095Sdes TUNABLE_INT_FETCH("net.iscsi_initiator.max_sessions", &max_sessions); 715211095Sdes TUNABLE_INT_FETCH("net.iscsi_initiator.max_pdus", &max_pdus); 716171568Sscottl 717211095Sdes isc = malloc(sizeof(struct isc_softc), M_ISCSI, M_ZERO|M_WAITOK); 718255812Strasz mtx_init(&isc->isc_mtx, "iscsi-isc", NULL, MTX_DEF); 719171568Sscottl 720211095Sdes TAILQ_INIT(&isc->isc_sess); 721211095Sdes /* 722211095Sdes | now init the free pdu list 723211095Sdes */ 724211095Sdes isc->pdu_zone = uma_zcreate("pdu", sizeof(pduq_t), 725211095Sdes NULL, NULL, NULL, NULL, 726211095Sdes 0, 0); 727211095Sdes uma_zone_set_max(isc->pdu_zone, max_pdus); 728211095Sdes isc->unit = new_unrhdr(0, max_sessions-1, NULL); 729211095Sdes sx_init(&isc->unit_sx, "iscsi sx"); 730171568Sscottl 731185289Sscottl#ifdef DO_EVENTHANDLER 732211095Sdes if((isc->eh = EVENTHANDLER_REGISTER(shutdown_pre_sync, iscsi_shutdown, 733171568Sscottl sc, SHUTDOWN_PRI_DEFAULT-1)) == NULL) 734171568Sscottl xdebug("shutdown event registration failed\n"); 735185289Sscottl#endif 736171568Sscottl /* 737171568Sscottl | sysctl stuff 738171568Sscottl */ 739211095Sdes sysctl_ctx_init(&isc->clist); 740211095Sdes isc->oid = SYSCTL_ADD_NODE(&isc->clist, 741171568Sscottl SYSCTL_STATIC_CHILDREN(_net), 742171568Sscottl OID_AUTO, 743211095Sdes "iscsi_initiator", 744171568Sscottl CTLFLAG_RD, 745171568Sscottl 0, 746171568Sscottl "iSCSI Subsystem"); 747171568Sscottl 748211095Sdes SYSCTL_ADD_STRING(&isc->clist, 749211095Sdes SYSCTL_CHILDREN(isc->oid), 750171568Sscottl OID_AUTO, 751171568Sscottl "driver_version", 752171568Sscottl CTLFLAG_RD, 753171568Sscottl iscsi_driver_version, 754171568Sscottl 0, 755171568Sscottl "iscsi driver version"); 756171568Sscottl 757211095Sdes SYSCTL_ADD_STRING(&isc->clist, 758211095Sdes SYSCTL_CHILDREN(isc->oid), 759171568Sscottl OID_AUTO, 760171568Sscottl "isid", 761171568Sscottl CTLFLAG_RW, 762171568Sscottl isid, 763171568Sscottl 6+1, 764171568Sscottl "initiator part of the Session Identifier"); 765171568Sscottl 766211095Sdes SYSCTL_ADD_INT(&isc->clist, 767211095Sdes SYSCTL_CHILDREN(isc->oid), 768171568Sscottl OID_AUTO, 769171568Sscottl "sessions", 770171568Sscottl CTLFLAG_RD, 771211095Sdes &isc->nsess, 772211095Sdes sizeof(isc->nsess), 773171568Sscottl "number of active session"); 774185289Sscottl 775234233Sjpaetzel#ifdef ISCSI_INITIATOR_DEBUG 776234233Sjpaetzel mtx_init(&iscsi_dbg_mtx, "iscsi_dbg", NULL, MTX_DEF); 777234233Sjpaetzel#endif 778234233Sjpaetzel 779255855Strasz isc->dev = make_dev_credf(MAKEDEV_CHECKNAME, &iscsi_cdevsw, max_sessions, 780255855Strasz NULL, UID_ROOT, GID_WHEEL, 0600, "iscsi"); 781255855Strasz if (isc->dev == NULL) { 782255855Strasz xdebug("iscsi_initiator: make_dev_credf failed"); 783255855Strasz return (EEXIST); 784255855Strasz } 785255855Strasz isc->dev->si_drv1 = isc; 786255855Strasz 787185289Sscottl printf("iscsi: version %s\n", iscsi_driver_version); 788255855Strasz return (0); 789171568Sscottl} 790171568Sscottl 791171568Sscottl/* 792171568Sscottl | Notes: 793171568Sscottl | unload SHOULD fail if there is activity 794171568Sscottl | activity: there is/are active session/s 795171568Sscottl */ 796171568Sscottlstatic void 797171568Sscottliscsi_stop(void) 798171568Sscottl{ 799171568Sscottl isc_session_t *sp, *sp_tmp; 800171568Sscottl 801171568Sscottl debug_called(8); 802171568Sscottl 803171568Sscottl /* 804171568Sscottl | go through all the sessions 805171568Sscottl | Note: close should have done this ... 806171568Sscottl */ 807211095Sdes TAILQ_FOREACH_SAFE(sp, &isc->isc_sess, sp_link, sp_tmp) { 808171568Sscottl //XXX: check for activity ... 809171568Sscottl ism_stop(sp); 810211095Sdes if(sp->cam_sim != NULL) 811211095Sdes ic_destroy(sp); 812171568Sscottl } 813211095Sdes mtx_destroy(&isc->isc_mtx); 814211095Sdes sx_destroy(&isc->unit_sx); 815171568Sscottl 816211095Sdes free_pdus(isc); 817171568Sscottl 818211095Sdes if(isc->dev) 819211095Sdes destroy_dev(isc->dev); 820171568Sscottl 821211095Sdes if(sysctl_ctx_free(&isc->clist)) 822171568Sscottl xdebug("sysctl_ctx_free failed"); 823185289Sscottl 824211095Sdes iscsi_shutdown(isc); // XXX: check EVENTHANDLER_ ... 825234233Sjpaetzel 826234233Sjpaetzel#ifdef ISCSI_INITIATOR_DEBUG 827234233Sjpaetzel mtx_destroy(&iscsi_dbg_mtx); 828234233Sjpaetzel#endif 829234233Sjpaetzel 830211095Sdes free(isc, M_ISCSI); 831171568Sscottl} 832171568Sscottl 833171568Sscottlstatic int 834171568Sscottliscsi_modevent(module_t mod, int what, void *arg) 835171568Sscottl{ 836255855Strasz int error = 0; 837255855Strasz 838171568Sscottl debug_called(8); 839171568Sscottl 840171568Sscottl switch(what) { 841171568Sscottl case MOD_LOAD: 842255855Strasz error = iscsi_start(); 843171568Sscottl break; 844171568Sscottl 845171568Sscottl case MOD_QUIESCE: 846211095Sdes if(isc->nsess) { 847211095Sdes xdebug("iscsi module busy(nsess=%d), cannot unload", isc->nsess); 848171568Sscottl log(LOG_ERR, "iscsi module busy, cannot unload"); 849171568Sscottl } 850211095Sdes return isc->nsess; 851211095Sdes 852171568Sscottl case MOD_SHUTDOWN: 853171568Sscottl break; 854171568Sscottl 855171568Sscottl case MOD_UNLOAD: 856171568Sscottl iscsi_stop(); 857171568Sscottl break; 858171568Sscottl 859171568Sscottl default: 860171568Sscottl break; 861171568Sscottl } 862255855Strasz return (error); 863171568Sscottl} 864185289Sscottl 865171568Sscottlmoduledata_t iscsi_mod = { 866255570Strasz "iscsi_initiator", 867241394Skevlo (modeventhand_t) iscsi_modevent, 868241394Skevlo 0 869171568Sscottl}; 870171568Sscottl 871185289Sscottl#ifdef ISCSI_ROOT 872185289Sscottlstatic void 873185289Sscottliscsi_rootconf(void) 874185289Sscottl{ 875185289Sscottl#if 0 876185289Sscottl nfs_setup_diskless(); 877185289Sscottl if (nfs_diskless_valid) 878185289Sscottl rootdevnames[0] = "nfs:"; 879185289Sscottl#endif 880185289Sscottl printf("** iscsi_rootconf **\n"); 881185289Sscottl} 882185289Sscottl 883185289SscottlSYSINIT(cpu_rootconf1, SI_SUB_ROOT_CONF, SI_ORDER_FIRST, iscsi_rootconf, NULL) 884185289Sscottl#endif 885185289Sscottl 886255570StraszDECLARE_MODULE(iscsi_initiator, iscsi_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 887255570StraszMODULE_DEPEND(iscsi_initiator, cam, 1, 1, 1); 888