1255570Strasz/*- 2255570Strasz * Copyright (c) 2012 The FreeBSD Foundation 3255570Strasz * All rights reserved. 4255570Strasz * 5255570Strasz * This software was developed by Edward Tomasz Napierala under sponsorship 6255570Strasz * from the FreeBSD Foundation. 7255570Strasz * 8255570Strasz * Redistribution and use in source and binary forms, with or without 9255570Strasz * modification, are permitted provided that the following conditions 10255570Strasz * are met: 11255570Strasz * 1. Redistributions of source code must retain the above copyright 12255570Strasz * notice, this list of conditions and the following disclaimer. 13255570Strasz * 2. Redistributions in binary form must reproduce the above copyright 14255570Strasz * notice, this list of conditions and the following disclaimer in the 15255570Strasz * documentation and/or other materials provided with the distribution. 16255570Strasz * 17255570Strasz * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18255570Strasz * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19255570Strasz * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20255570Strasz * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21255570Strasz * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22255570Strasz * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23255570Strasz * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24255570Strasz * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25255570Strasz * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26255570Strasz * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27255570Strasz * SUCH DAMAGE. 28255570Strasz * 29255570Strasz */ 30255570Strasz 31270888Strasz#include <sys/cdefs.h> 32270888Strasz__FBSDID("$FreeBSD: stable/10/sys/dev/iscsi/iscsi.c 325226 2017-10-31 09:58:51Z avg $"); 33270888Strasz 34255570Strasz#include <sys/param.h> 35255570Strasz#include <sys/condvar.h> 36255570Strasz#include <sys/conf.h> 37255570Strasz#include <sys/eventhandler.h> 38255570Strasz#include <sys/file.h> 39255570Strasz#include <sys/kernel.h> 40255570Strasz#include <sys/kthread.h> 41255570Strasz#include <sys/lock.h> 42255570Strasz#include <sys/malloc.h> 43255570Strasz#include <sys/mutex.h> 44255570Strasz#include <sys/module.h> 45255570Strasz#include <sys/sysctl.h> 46255570Strasz#include <sys/systm.h> 47255570Strasz#include <sys/sx.h> 48255570Strasz#include <vm/uma.h> 49255570Strasz 50255570Strasz#include <cam/cam.h> 51255570Strasz#include <cam/cam_ccb.h> 52255570Strasz#include <cam/cam_xpt.h> 53255570Strasz#include <cam/cam_debug.h> 54255570Strasz#include <cam/cam_sim.h> 55255570Strasz#include <cam/cam_xpt_sim.h> 56255570Strasz#include <cam/cam_xpt_periph.h> 57255570Strasz#include <cam/cam_periph.h> 58255570Strasz#include <cam/scsi/scsi_all.h> 59255570Strasz#include <cam/scsi/scsi_message.h> 60255570Strasz 61270891Strasz#include <dev/iscsi/icl.h> 62270891Strasz#include <dev/iscsi/iscsi_ioctl.h> 63270891Strasz#include <dev/iscsi/iscsi_proto.h> 64270891Strasz#include <dev/iscsi/iscsi.h> 65255570Strasz 66255570Strasz#ifdef ICL_KERNEL_PROXY 67255570Strasz#include <sys/socketvar.h> 68255570Strasz#endif 69255570Strasz 70265508Strasz#ifdef ICL_KERNEL_PROXY 71265508StraszFEATURE(iscsi_kernel_proxy, "iSCSI initiator built with ICL_KERNEL_PROXY"); 72265508Strasz#endif 73265508Strasz 74255570Strasz/* 75255570Strasz * XXX: This is global so the iscsi_unload() can access it. 76255570Strasz * Think about how to do this properly. 77255570Strasz */ 78255570Straszstatic struct iscsi_softc *sc; 79255570Strasz 80255570StraszSYSCTL_NODE(_kern, OID_AUTO, iscsi, CTLFLAG_RD, 0, "iSCSI initiator"); 81255570Straszstatic int debug = 1; 82255570StraszTUNABLE_INT("kern.iscsi.debug", &debug); 83265501StraszSYSCTL_INT(_kern_iscsi, OID_AUTO, debug, CTLFLAG_RWTUN, 84265531Strasz &debug, 0, "Enable debug messages"); 85255570Straszstatic int ping_timeout = 5; 86255570StraszTUNABLE_INT("kern.iscsi.ping_timeout", &ping_timeout); 87265501StraszSYSCTL_INT(_kern_iscsi, OID_AUTO, ping_timeout, CTLFLAG_RWTUN, &ping_timeout, 88265531Strasz 0, "Timeout for ping (NOP-Out) requests, in seconds"); 89255570Straszstatic int iscsid_timeout = 60; 90255570StraszTUNABLE_INT("kern.iscsi.iscsid_timeout", &iscsid_timeout); 91265501StraszSYSCTL_INT(_kern_iscsi, OID_AUTO, iscsid_timeout, CTLFLAG_RWTUN, &iscsid_timeout, 92265531Strasz 0, "Time to wait for iscsid(8) to handle reconnection, in seconds"); 93255570Straszstatic int login_timeout = 60; 94255570StraszTUNABLE_INT("kern.iscsi.login_timeout", &login_timeout); 95265501StraszSYSCTL_INT(_kern_iscsi, OID_AUTO, login_timeout, CTLFLAG_RWTUN, &login_timeout, 96265531Strasz 0, "Time to wait for iscsid(8) to finish Login Phase, in seconds"); 97255570Straszstatic int maxtags = 255; 98255570StraszTUNABLE_INT("kern.iscsi.maxtags", &maxtags); 99265501StraszSYSCTL_INT(_kern_iscsi, OID_AUTO, maxtags, CTLFLAG_RWTUN, &maxtags, 100265531Strasz 0, "Max number of IO requests queued"); 101265523Straszstatic int fail_on_disconnection = 0; 102265523StraszTUNABLE_INT("kern.iscsi.fail_on_disconnection", &fail_on_disconnection); 103265523StraszSYSCTL_INT(_kern_iscsi, OID_AUTO, fail_on_disconnection, CTLFLAG_RWTUN, 104265523Strasz &fail_on_disconnection, 0, "Destroy CAM SIM on connection failure"); 105294708Ssmhstatic int fail_on_shutdown = 1; 106294708SsmhSYSCTL_INT(_kern_iscsi, OID_AUTO, fail_on_shutdown, CTLFLAG_RWTUN, 107294708Ssmh &fail_on_shutdown, 0, "Fail disconnected sessions on shutdown"); 108255570Strasz 109255570Straszstatic MALLOC_DEFINE(M_ISCSI, "iSCSI", "iSCSI initiator"); 110255570Straszstatic uma_zone_t iscsi_outstanding_zone; 111255570Strasz 112255570Strasz#define CONN_SESSION(X) ((struct iscsi_session *)X->ic_prv0) 113255570Strasz#define PDU_SESSION(X) (CONN_SESSION(X->ip_conn)) 114255570Strasz 115265505Strasz#define ISCSI_DEBUG(X, ...) \ 116265505Strasz do { \ 117265505Strasz if (debug > 1) \ 118265505Strasz printf("%s: " X "\n", __func__, ## __VA_ARGS__);\ 119255570Strasz } while (0) 120255570Strasz 121265505Strasz#define ISCSI_WARN(X, ...) \ 122265505Strasz do { \ 123265505Strasz if (debug > 0) { \ 124265505Strasz printf("WARNING: %s: " X "\n", \ 125265505Strasz __func__, ## __VA_ARGS__); \ 126265505Strasz } \ 127255570Strasz } while (0) 128255570Strasz 129265505Strasz#define ISCSI_SESSION_DEBUG(S, X, ...) \ 130265505Strasz do { \ 131265505Strasz if (debug > 1) { \ 132265505Strasz printf("%s: %s (%s): " X "\n", \ 133265505Strasz __func__, S->is_conf.isc_target_addr, \ 134265505Strasz S->is_conf.isc_target, ## __VA_ARGS__); \ 135265505Strasz } \ 136255570Strasz } while (0) 137255570Strasz 138265505Strasz#define ISCSI_SESSION_WARN(S, X, ...) \ 139265505Strasz do { \ 140265505Strasz if (debug > 0) { \ 141265505Strasz printf("WARNING: %s (%s): " X "\n", \ 142265505Strasz S->is_conf.isc_target_addr, \ 143265505Strasz S->is_conf.isc_target, ## __VA_ARGS__); \ 144265505Strasz } \ 145255570Strasz } while (0) 146255570Strasz 147255570Strasz#define ISCSI_SESSION_LOCK(X) mtx_lock(&X->is_lock) 148255570Strasz#define ISCSI_SESSION_UNLOCK(X) mtx_unlock(&X->is_lock) 149255570Strasz#define ISCSI_SESSION_LOCK_ASSERT(X) mtx_assert(&X->is_lock, MA_OWNED) 150275559Smav#define ISCSI_SESSION_LOCK_ASSERT_NOT(X) mtx_assert(&X->is_lock, MA_NOTOWNED) 151255570Strasz 152255570Straszstatic int iscsi_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, 153255570Strasz int mode, struct thread *td); 154255570Strasz 155255570Straszstatic struct cdevsw iscsi_cdevsw = { 156255570Strasz .d_version = D_VERSION, 157255570Strasz .d_ioctl = iscsi_ioctl, 158255570Strasz .d_name = "iscsi", 159255570Strasz}; 160255570Strasz 161255570Straszstatic void iscsi_pdu_queue_locked(struct icl_pdu *request); 162255570Straszstatic void iscsi_pdu_queue(struct icl_pdu *request); 163255570Straszstatic void iscsi_pdu_update_statsn(const struct icl_pdu *response); 164255570Straszstatic void iscsi_pdu_handle_nop_in(struct icl_pdu *response); 165255570Straszstatic void iscsi_pdu_handle_scsi_response(struct icl_pdu *response); 166268705Smavstatic void iscsi_pdu_handle_task_response(struct icl_pdu *response); 167255570Straszstatic void iscsi_pdu_handle_data_in(struct icl_pdu *response); 168255570Straszstatic void iscsi_pdu_handle_logout_response(struct icl_pdu *response); 169255570Straszstatic void iscsi_pdu_handle_r2t(struct icl_pdu *response); 170255570Straszstatic void iscsi_pdu_handle_async_message(struct icl_pdu *response); 171255570Straszstatic void iscsi_pdu_handle_reject(struct icl_pdu *response); 172255570Straszstatic void iscsi_session_reconnect(struct iscsi_session *is); 173255570Straszstatic void iscsi_session_terminate(struct iscsi_session *is); 174255570Straszstatic void iscsi_action(struct cam_sim *sim, union ccb *ccb); 175255570Straszstatic void iscsi_poll(struct cam_sim *sim); 176255570Straszstatic struct iscsi_outstanding *iscsi_outstanding_find(struct iscsi_session *is, 177255570Strasz uint32_t initiator_task_tag); 178268705Smavstatic struct iscsi_outstanding *iscsi_outstanding_add(struct iscsi_session *is, 179255570Strasz uint32_t initiator_task_tag, union ccb *ccb); 180255570Straszstatic void iscsi_outstanding_remove(struct iscsi_session *is, 181255570Strasz struct iscsi_outstanding *io); 182255570Strasz 183255570Straszstatic bool 184255570Strasziscsi_pdu_prepare(struct icl_pdu *request) 185255570Strasz{ 186255570Strasz struct iscsi_session *is; 187255570Strasz struct iscsi_bhs_scsi_command *bhssc; 188255570Strasz 189255570Strasz is = PDU_SESSION(request); 190255570Strasz 191255570Strasz ISCSI_SESSION_LOCK_ASSERT(is); 192255570Strasz 193255570Strasz /* 194255570Strasz * We're only using fields common for all the request 195255570Strasz * (initiator -> target) PDUs. 196255570Strasz */ 197255570Strasz bhssc = (struct iscsi_bhs_scsi_command *)request->ip_bhs; 198255570Strasz 199255570Strasz /* 200255570Strasz * Data-Out PDU does not contain CmdSN. 201255570Strasz */ 202255570Strasz if (bhssc->bhssc_opcode != ISCSI_BHS_OPCODE_SCSI_DATA_OUT) { 203276613Smav if (ISCSI_SNGT(is->is_cmdsn, is->is_maxcmdsn) && 204255570Strasz (bhssc->bhssc_opcode & ISCSI_BHS_OPCODE_IMMEDIATE) == 0) { 205255570Strasz /* 206255570Strasz * Current MaxCmdSN prevents us from sending any more 207255570Strasz * SCSI Command PDUs to the target; postpone the PDU. 208255570Strasz * It will get resent by either iscsi_pdu_queue(), 209255570Strasz * or by maintenance thread. 210255570Strasz */ 211255570Strasz#if 0 212276613Smav ISCSI_SESSION_DEBUG(is, "postponing send, CmdSN %u, " 213276613Smav "ExpCmdSN %u, MaxCmdSN %u, opcode 0x%x", 214276613Smav is->is_cmdsn, is->is_expcmdsn, is->is_maxcmdsn, 215276613Smav bhssc->bhssc_opcode); 216255570Strasz#endif 217255570Strasz return (true); 218255570Strasz } 219255570Strasz bhssc->bhssc_cmdsn = htonl(is->is_cmdsn); 220255570Strasz if ((bhssc->bhssc_opcode & ISCSI_BHS_OPCODE_IMMEDIATE) == 0) 221255570Strasz is->is_cmdsn++; 222255570Strasz } 223255570Strasz bhssc->bhssc_expstatsn = htonl(is->is_statsn + 1); 224255570Strasz 225255570Strasz return (false); 226255570Strasz} 227255570Strasz 228255570Straszstatic void 229255570Strasziscsi_session_send_postponed(struct iscsi_session *is) 230255570Strasz{ 231255570Strasz struct icl_pdu *request; 232255570Strasz bool postpone; 233255570Strasz 234255570Strasz ISCSI_SESSION_LOCK_ASSERT(is); 235255570Strasz 236314583Smav if (STAILQ_EMPTY(&is->is_postponed)) 237314583Smav return; 238314583Smav while ((request = STAILQ_FIRST(&is->is_postponed)) != NULL) { 239255570Strasz postpone = iscsi_pdu_prepare(request); 240255570Strasz if (postpone) 241314583Smav return; 242265500Strasz STAILQ_REMOVE_HEAD(&is->is_postponed, ip_next); 243255570Strasz icl_pdu_queue(request); 244255570Strasz } 245314583Smav xpt_release_simq(is->is_sim, 1); 246255570Strasz} 247255570Strasz 248255570Straszstatic void 249255570Strasziscsi_pdu_queue_locked(struct icl_pdu *request) 250255570Strasz{ 251255570Strasz struct iscsi_session *is; 252255570Strasz bool postpone; 253255570Strasz 254255570Strasz is = PDU_SESSION(request); 255255570Strasz ISCSI_SESSION_LOCK_ASSERT(is); 256255570Strasz iscsi_session_send_postponed(is); 257255570Strasz postpone = iscsi_pdu_prepare(request); 258255570Strasz if (postpone) { 259314583Smav if (STAILQ_EMPTY(&is->is_postponed)) 260314583Smav xpt_freeze_simq(is->is_sim, 1); 261265500Strasz STAILQ_INSERT_TAIL(&is->is_postponed, request, ip_next); 262255570Strasz return; 263255570Strasz } 264255570Strasz icl_pdu_queue(request); 265255570Strasz} 266255570Strasz 267255570Straszstatic void 268255570Strasziscsi_pdu_queue(struct icl_pdu *request) 269255570Strasz{ 270255570Strasz struct iscsi_session *is; 271255570Strasz 272255570Strasz is = PDU_SESSION(request); 273255570Strasz ISCSI_SESSION_LOCK(is); 274255570Strasz iscsi_pdu_queue_locked(request); 275255570Strasz ISCSI_SESSION_UNLOCK(is); 276255570Strasz} 277255570Strasz 278255570Straszstatic void 279255570Strasziscsi_session_logout(struct iscsi_session *is) 280255570Strasz{ 281255570Strasz struct icl_pdu *request; 282255570Strasz struct iscsi_bhs_logout_request *bhslr; 283255570Strasz 284276618Smav request = icl_pdu_new(is->is_conn, M_NOWAIT); 285255570Strasz if (request == NULL) 286255570Strasz return; 287255570Strasz 288255570Strasz bhslr = (struct iscsi_bhs_logout_request *)request->ip_bhs; 289255570Strasz bhslr->bhslr_opcode = ISCSI_BHS_OPCODE_LOGOUT_REQUEST; 290255570Strasz bhslr->bhslr_reason = BHSLR_REASON_CLOSE_SESSION; 291255570Strasz iscsi_pdu_queue_locked(request); 292255570Strasz} 293255570Strasz 294255570Straszstatic void 295268705Smaviscsi_session_terminate_task(struct iscsi_session *is, 296325224Savg struct iscsi_outstanding *io, cam_status status) 297255570Strasz{ 298255570Strasz 299268705Smav if (io->io_ccb != NULL) { 300268705Smav io->io_ccb->ccb_h.status &= ~(CAM_SIM_QUEUED | CAM_STATUS_MASK); 301325224Savg io->io_ccb->ccb_h.status |= status; 302255570Strasz if ((io->io_ccb->ccb_h.status & CAM_DEV_QFRZN) == 0) { 303268705Smav io->io_ccb->ccb_h.status |= CAM_DEV_QFRZN; 304255570Strasz xpt_freeze_devq(io->io_ccb->ccb_h.path, 1); 305255570Strasz ISCSI_SESSION_DEBUG(is, "freezing devq"); 306255570Strasz } 307255570Strasz xpt_done(io->io_ccb); 308255570Strasz } 309268705Smav iscsi_outstanding_remove(is, io); 310255570Strasz} 311255570Strasz 312255570Straszstatic void 313325224Savgiscsi_session_terminate_tasks(struct iscsi_session *is, cam_status status) 314268705Smav{ 315268705Smav struct iscsi_outstanding *io, *tmp; 316268705Smav 317268705Smav ISCSI_SESSION_LOCK_ASSERT(is); 318268705Smav 319268705Smav TAILQ_FOREACH_SAFE(io, &is->is_outstanding, io_next, tmp) { 320325224Savg iscsi_session_terminate_task(is, io, status); 321268705Smav } 322268705Smav} 323268705Smav 324268705Smavstatic void 325265523Strasziscsi_session_cleanup(struct iscsi_session *is, bool destroy_sim) 326255570Strasz{ 327255570Strasz struct icl_pdu *pdu; 328255570Strasz 329265523Strasz ISCSI_SESSION_LOCK_ASSERT(is); 330255570Strasz 331255570Strasz /* 332255570Strasz * Don't queue any new PDUs. 333255570Strasz */ 334255570Strasz if (is->is_sim != NULL && is->is_simq_frozen == false) { 335255570Strasz ISCSI_SESSION_DEBUG(is, "freezing"); 336255570Strasz xpt_freeze_simq(is->is_sim, 1); 337255570Strasz is->is_simq_frozen = true; 338255570Strasz } 339255570Strasz 340255570Strasz /* 341255570Strasz * Remove postponed PDUs. 342255570Strasz */ 343314583Smav if (!STAILQ_EMPTY(&is->is_postponed)) 344314583Smav xpt_release_simq(is->is_sim, 1); 345314583Smav while ((pdu = STAILQ_FIRST(&is->is_postponed)) != NULL) { 346265500Strasz STAILQ_REMOVE_HEAD(&is->is_postponed, ip_next); 347255570Strasz icl_pdu_free(pdu); 348255570Strasz } 349255570Strasz 350265523Strasz if (destroy_sim == false) { 351265523Strasz /* 352265523Strasz * Terminate SCSI tasks, asking CAM to requeue them. 353265523Strasz */ 354325224Savg iscsi_session_terminate_tasks(is, CAM_REQUEUE_REQ); 355265523Strasz return; 356265523Strasz } 357255570Strasz 358325224Savg iscsi_session_terminate_tasks(is, CAM_DEV_NOT_THERE); 359265523Strasz 360265523Strasz if (is->is_sim == NULL) 361265523Strasz return; 362265523Strasz 363265523Strasz ISCSI_SESSION_DEBUG(is, "deregistering SIM"); 364265523Strasz xpt_async(AC_LOST_DEVICE, is->is_path, NULL); 365265523Strasz 366265523Strasz if (is->is_simq_frozen) { 367265523Strasz xpt_release_simq(is->is_sim, 1); 368265523Strasz is->is_simq_frozen = false; 369265523Strasz } 370265523Strasz 371265523Strasz xpt_free_path(is->is_path); 372265523Strasz is->is_path = NULL; 373265523Strasz xpt_bus_deregister(cam_sim_path(is->is_sim)); 374265523Strasz cam_sim_free(is->is_sim, TRUE /*free_devq*/); 375265523Strasz is->is_sim = NULL; 376265523Strasz is->is_devq = NULL; 377265523Strasz} 378265523Strasz 379265523Straszstatic void 380265523Strasziscsi_maintenance_thread_reconnect(struct iscsi_session *is) 381265523Strasz{ 382265523Strasz 383265523Strasz icl_conn_close(is->is_conn); 384265523Strasz 385265523Strasz ISCSI_SESSION_LOCK(is); 386265523Strasz 387265523Strasz is->is_connected = false; 388265523Strasz is->is_reconnecting = false; 389265523Strasz is->is_login_phase = false; 390265523Strasz 391265523Strasz#ifdef ICL_KERNEL_PROXY 392265523Strasz if (is->is_login_pdu != NULL) { 393265523Strasz icl_pdu_free(is->is_login_pdu); 394265523Strasz is->is_login_pdu = NULL; 395265523Strasz } 396265523Strasz cv_signal(&is->is_login_cv); 397265523Strasz#endif 398265523Strasz 399265523Strasz if (fail_on_disconnection) { 400265523Strasz ISCSI_SESSION_DEBUG(is, "connection failed, destroying devices"); 401265523Strasz iscsi_session_cleanup(is, true); 402265523Strasz } else { 403265523Strasz iscsi_session_cleanup(is, false); 404265523Strasz } 405265523Strasz 406255570Strasz KASSERT(TAILQ_EMPTY(&is->is_outstanding), 407255570Strasz ("destroying session with active tasks")); 408265500Strasz KASSERT(STAILQ_EMPTY(&is->is_postponed), 409255570Strasz ("destroying session with postponed PDUs")); 410255570Strasz 411255570Strasz /* 412255570Strasz * Request immediate reconnection from iscsid(8). 413255570Strasz */ 414255570Strasz //ISCSI_SESSION_DEBUG(is, "waking up iscsid(8)"); 415255570Strasz is->is_waiting_for_iscsid = true; 416255570Strasz strlcpy(is->is_reason, "Waiting for iscsid(8)", sizeof(is->is_reason)); 417255570Strasz is->is_timeout = 0; 418255570Strasz ISCSI_SESSION_UNLOCK(is); 419255570Strasz cv_signal(&is->is_softc->sc_cv); 420255570Strasz} 421255570Strasz 422255570Straszstatic void 423255570Strasziscsi_maintenance_thread_terminate(struct iscsi_session *is) 424255570Strasz{ 425255570Strasz struct iscsi_softc *sc; 426255570Strasz 427255570Strasz sc = is->is_softc; 428255570Strasz sx_xlock(&sc->sc_lock); 429325222Savg TAILQ_REMOVE(&sc->sc_sessions, is, is_next); 430325222Savg sx_xunlock(&sc->sc_lock); 431255570Strasz 432255570Strasz icl_conn_close(is->is_conn); 433282955Strasz callout_drain(&is->is_callout); 434255570Strasz 435255570Strasz ISCSI_SESSION_LOCK(is); 436255570Strasz 437255570Strasz KASSERT(is->is_terminating, ("is_terminating == false")); 438255570Strasz 439255570Strasz#ifdef ICL_KERNEL_PROXY 440255570Strasz if (is->is_login_pdu != NULL) { 441255570Strasz icl_pdu_free(is->is_login_pdu); 442255570Strasz is->is_login_pdu = NULL; 443255570Strasz } 444255570Strasz cv_signal(&is->is_login_cv); 445255570Strasz#endif 446255570Strasz 447265523Strasz iscsi_session_cleanup(is, true); 448255570Strasz 449255570Strasz KASSERT(TAILQ_EMPTY(&is->is_outstanding), 450255570Strasz ("destroying session with active tasks")); 451265500Strasz KASSERT(STAILQ_EMPTY(&is->is_postponed), 452255570Strasz ("destroying session with postponed PDUs")); 453255570Strasz 454255570Strasz ISCSI_SESSION_UNLOCK(is); 455255570Strasz 456255570Strasz icl_conn_free(is->is_conn); 457255570Strasz mtx_destroy(&is->is_lock); 458255570Strasz cv_destroy(&is->is_maintenance_cv); 459255570Strasz#ifdef ICL_KERNEL_PROXY 460255570Strasz cv_destroy(&is->is_login_cv); 461255570Strasz#endif 462294708Ssmh 463255570Strasz ISCSI_SESSION_DEBUG(is, "terminated"); 464255570Strasz free(is, M_ISCSI); 465255570Strasz 466255570Strasz /* 467255570Strasz * The iscsi_unload() routine might be waiting. 468255570Strasz */ 469255570Strasz cv_signal(&sc->sc_cv); 470255570Strasz} 471255570Strasz 472255570Straszstatic void 473255570Strasziscsi_maintenance_thread(void *arg) 474255570Strasz{ 475314581Smav struct iscsi_session *is = arg; 476255570Strasz 477314581Smav ISCSI_SESSION_LOCK(is); 478255570Strasz for (;;) { 479255570Strasz if (is->is_reconnecting == false && 480255570Strasz is->is_terminating == false && 481314581Smav (STAILQ_EMPTY(&is->is_postponed) || 482314581Smav ISCSI_SNGT(is->is_cmdsn, is->is_maxcmdsn))) 483255570Strasz cv_wait(&is->is_maintenance_cv, &is->is_lock); 484255570Strasz 485294708Ssmh /* Terminate supersedes reconnect. */ 486255570Strasz if (is->is_terminating) { 487255570Strasz ISCSI_SESSION_UNLOCK(is); 488255570Strasz iscsi_maintenance_thread_terminate(is); 489255570Strasz kthread_exit(); 490255570Strasz return; 491255570Strasz } 492255570Strasz 493294708Ssmh if (is->is_reconnecting) { 494294708Ssmh ISCSI_SESSION_UNLOCK(is); 495294708Ssmh iscsi_maintenance_thread_reconnect(is); 496314581Smav ISCSI_SESSION_LOCK(is); 497294708Ssmh continue; 498294708Ssmh } 499294708Ssmh 500255570Strasz iscsi_session_send_postponed(is); 501255570Strasz } 502314581Smav ISCSI_SESSION_UNLOCK(is); 503255570Strasz} 504255570Strasz 505255570Straszstatic void 506255570Strasziscsi_session_reconnect(struct iscsi_session *is) 507255570Strasz{ 508255570Strasz 509255570Strasz /* 510255570Strasz * XXX: We can't use locking here, because 511255570Strasz * it's being called from various contexts. 512255570Strasz * Hope it doesn't break anything. 513255570Strasz */ 514255570Strasz if (is->is_reconnecting) 515255570Strasz return; 516255570Strasz 517255570Strasz is->is_reconnecting = true; 518255570Strasz cv_signal(&is->is_maintenance_cv); 519255570Strasz} 520255570Strasz 521255570Straszstatic void 522255570Strasziscsi_session_terminate(struct iscsi_session *is) 523255570Strasz{ 524282955Strasz 525255570Strasz if (is->is_terminating) 526255570Strasz return; 527255570Strasz 528255570Strasz is->is_terminating = true; 529255570Strasz 530255570Strasz#if 0 531255570Strasz iscsi_session_logout(is); 532255570Strasz#endif 533255570Strasz cv_signal(&is->is_maintenance_cv); 534255570Strasz} 535255570Strasz 536255570Straszstatic void 537255570Strasziscsi_callout(void *context) 538255570Strasz{ 539255570Strasz struct icl_pdu *request; 540255570Strasz struct iscsi_bhs_nop_out *bhsno; 541255570Strasz struct iscsi_session *is; 542255570Strasz bool reconnect_needed = false; 543255570Strasz 544255570Strasz is = context; 545255570Strasz 546282955Strasz ISCSI_SESSION_LOCK(is); 547282955Strasz if (is->is_terminating) { 548282955Strasz ISCSI_SESSION_UNLOCK(is); 549255570Strasz return; 550282955Strasz } 551255570Strasz 552255570Strasz callout_schedule(&is->is_callout, 1 * hz); 553255570Strasz 554255570Strasz is->is_timeout++; 555255570Strasz 556255570Strasz if (is->is_waiting_for_iscsid) { 557273307Smav if (iscsid_timeout > 0 && is->is_timeout > iscsid_timeout) { 558255570Strasz ISCSI_SESSION_WARN(is, "timed out waiting for iscsid(8) " 559255570Strasz "for %d seconds; reconnecting", 560255570Strasz is->is_timeout); 561255570Strasz reconnect_needed = true; 562255570Strasz } 563255570Strasz goto out; 564255570Strasz } 565255570Strasz 566255570Strasz if (is->is_login_phase) { 567273307Smav if (login_timeout > 0 && is->is_timeout > login_timeout) { 568255570Strasz ISCSI_SESSION_WARN(is, "login timed out after %d seconds; " 569255570Strasz "reconnecting", is->is_timeout); 570255570Strasz reconnect_needed = true; 571255570Strasz } 572255570Strasz goto out; 573255570Strasz } 574255570Strasz 575273307Smav if (ping_timeout <= 0) { 576273307Smav /* 577273307Smav * Pings are disabled. Don't send NOP-Out in this case. 578273307Smav * Reset the timeout, to avoid triggering reconnection, 579273307Smav * should the user decide to reenable them. 580273307Smav */ 581273307Smav is->is_timeout = 0; 582273307Smav goto out; 583273307Smav } 584273307Smav 585255570Strasz if (is->is_timeout >= ping_timeout) { 586255570Strasz ISCSI_SESSION_WARN(is, "no ping reply (NOP-In) after %d seconds; " 587255570Strasz "reconnecting", ping_timeout); 588255570Strasz reconnect_needed = true; 589255570Strasz goto out; 590255570Strasz } 591255570Strasz 592255570Strasz ISCSI_SESSION_UNLOCK(is); 593255570Strasz 594255570Strasz /* 595255570Strasz * If the ping was reset less than one second ago - which means 596255570Strasz * that we've received some PDU during the last second - assume 597255570Strasz * the traffic flows correctly and don't bother sending a NOP-Out. 598255570Strasz * 599255570Strasz * (It's 2 - one for one second, and one for incrementing is_timeout 600255570Strasz * earlier in this routine.) 601255570Strasz */ 602255570Strasz if (is->is_timeout < 2) 603255570Strasz return; 604255570Strasz 605276618Smav request = icl_pdu_new(is->is_conn, M_NOWAIT); 606255824Strasz if (request == NULL) { 607255824Strasz ISCSI_SESSION_WARN(is, "failed to allocate PDU"); 608255824Strasz return; 609255824Strasz } 610255570Strasz bhsno = (struct iscsi_bhs_nop_out *)request->ip_bhs; 611255570Strasz bhsno->bhsno_opcode = ISCSI_BHS_OPCODE_NOP_OUT | 612255570Strasz ISCSI_BHS_OPCODE_IMMEDIATE; 613255570Strasz bhsno->bhsno_flags = 0x80; 614255570Strasz bhsno->bhsno_target_transfer_tag = 0xffffffff; 615255570Strasz iscsi_pdu_queue(request); 616255570Strasz return; 617255570Strasz 618255570Straszout: 619294708Ssmh if (is->is_terminating) { 620294708Ssmh ISCSI_SESSION_UNLOCK(is); 621294708Ssmh return; 622294708Ssmh } 623294708Ssmh 624255570Strasz ISCSI_SESSION_UNLOCK(is); 625255570Strasz 626255570Strasz if (reconnect_needed) 627255570Strasz iscsi_session_reconnect(is); 628255570Strasz} 629255570Strasz 630255570Straszstatic void 631255570Strasziscsi_pdu_update_statsn(const struct icl_pdu *response) 632255570Strasz{ 633255570Strasz const struct iscsi_bhs_data_in *bhsdi; 634255570Strasz struct iscsi_session *is; 635276613Smav uint32_t expcmdsn, maxcmdsn, statsn; 636255570Strasz 637255570Strasz is = PDU_SESSION(response); 638255570Strasz 639255570Strasz ISCSI_SESSION_LOCK_ASSERT(is); 640255570Strasz 641255570Strasz /* 642255570Strasz * We're only using fields common for all the response 643255570Strasz * (target -> initiator) PDUs. 644255570Strasz */ 645255570Strasz bhsdi = (const struct iscsi_bhs_data_in *)response->ip_bhs; 646255570Strasz /* 647255570Strasz * Ok, I lied. In case of Data-In, "The fields StatSN, Status, 648255570Strasz * and Residual Count only have meaningful content if the S bit 649255570Strasz * is set to 1", so we also need to check the bit specific for 650255570Strasz * Data-In PDU. 651255570Strasz */ 652255570Strasz if (bhsdi->bhsdi_opcode != ISCSI_BHS_OPCODE_SCSI_DATA_IN || 653255570Strasz (bhsdi->bhsdi_flags & BHSDI_FLAGS_S) != 0) { 654276613Smav statsn = ntohl(bhsdi->bhsdi_statsn); 655276613Smav if (statsn != is->is_statsn && statsn != (is->is_statsn + 1)) { 656276613Smav /* XXX: This is normal situation for MCS */ 657276613Smav ISCSI_SESSION_WARN(is, "PDU 0x%x StatSN %u != " 658276613Smav "session ExpStatSN %u (or + 1); reconnecting", 659276613Smav bhsdi->bhsdi_opcode, statsn, is->is_statsn); 660276613Smav iscsi_session_reconnect(is); 661255570Strasz } 662276613Smav if (ISCSI_SNGT(statsn, is->is_statsn)) 663276613Smav is->is_statsn = statsn; 664255570Strasz } 665255570Strasz 666255570Strasz expcmdsn = ntohl(bhsdi->bhsdi_expcmdsn); 667255570Strasz maxcmdsn = ntohl(bhsdi->bhsdi_maxcmdsn); 668255570Strasz 669276613Smav if (ISCSI_SNLT(maxcmdsn + 1, expcmdsn)) { 670276613Smav ISCSI_SESSION_DEBUG(is, 671276613Smav "PDU MaxCmdSN %u + 1 < PDU ExpCmdSN %u; ignoring", 672255570Strasz maxcmdsn, expcmdsn); 673255570Strasz } else { 674276613Smav if (ISCSI_SNGT(maxcmdsn, is->is_maxcmdsn)) { 675255570Strasz is->is_maxcmdsn = maxcmdsn; 676255570Strasz 677255570Strasz /* 678255570Strasz * Command window increased; kick the maintanance thread 679255570Strasz * to send out postponed commands. 680255570Strasz */ 681265500Strasz if (!STAILQ_EMPTY(&is->is_postponed)) 682255570Strasz cv_signal(&is->is_maintenance_cv); 683276613Smav } else if (ISCSI_SNLT(maxcmdsn, is->is_maxcmdsn)) { 684276613Smav /* XXX: This is normal situation for MCS */ 685276613Smav ISCSI_SESSION_DEBUG(is, 686276613Smav "PDU MaxCmdSN %u < session MaxCmdSN %u; ignoring", 687255570Strasz maxcmdsn, is->is_maxcmdsn); 688255570Strasz } 689255570Strasz 690276613Smav if (ISCSI_SNGT(expcmdsn, is->is_expcmdsn)) { 691255570Strasz is->is_expcmdsn = expcmdsn; 692276613Smav } else if (ISCSI_SNLT(expcmdsn, is->is_expcmdsn)) { 693276613Smav /* XXX: This is normal situation for MCS */ 694276613Smav ISCSI_SESSION_DEBUG(is, 695276613Smav "PDU ExpCmdSN %u < session ExpCmdSN %u; ignoring", 696255570Strasz expcmdsn, is->is_expcmdsn); 697255570Strasz } 698255570Strasz } 699255570Strasz 700255570Strasz /* 701255570Strasz * Every incoming PDU - not just NOP-In - resets the ping timer. 702255570Strasz * The purpose of the timeout is to reset the connection when it stalls; 703255570Strasz * we don't want this to happen when NOP-In or NOP-Out ends up delayed 704255570Strasz * in some queue. 705255570Strasz */ 706255570Strasz is->is_timeout = 0; 707255570Strasz} 708255570Strasz 709255570Straszstatic void 710255570Strasziscsi_receive_callback(struct icl_pdu *response) 711255570Strasz{ 712255570Strasz struct iscsi_session *is; 713255570Strasz 714255570Strasz is = PDU_SESSION(response); 715255570Strasz 716255570Strasz ISCSI_SESSION_LOCK(is); 717255570Strasz 718255570Strasz#ifdef ICL_KERNEL_PROXY 719255570Strasz if (is->is_login_phase) { 720255570Strasz if (is->is_login_pdu == NULL) 721255570Strasz is->is_login_pdu = response; 722255570Strasz else 723255570Strasz icl_pdu_free(response); 724255570Strasz ISCSI_SESSION_UNLOCK(is); 725255570Strasz cv_signal(&is->is_login_cv); 726255570Strasz return; 727255570Strasz } 728255570Strasz#endif 729255570Strasz 730255570Strasz iscsi_pdu_update_statsn(response); 731255570Strasz 732255570Strasz /* 733255570Strasz * The handling routine is responsible for freeing the PDU 734255570Strasz * when it's no longer needed. 735255570Strasz */ 736255570Strasz switch (response->ip_bhs->bhs_opcode) { 737255570Strasz case ISCSI_BHS_OPCODE_NOP_IN: 738255570Strasz iscsi_pdu_handle_nop_in(response); 739275559Smav ISCSI_SESSION_UNLOCK(is); 740255570Strasz break; 741255570Strasz case ISCSI_BHS_OPCODE_SCSI_RESPONSE: 742255570Strasz iscsi_pdu_handle_scsi_response(response); 743275559Smav /* Session lock dropped inside. */ 744275559Smav ISCSI_SESSION_LOCK_ASSERT_NOT(is); 745255570Strasz break; 746268705Smav case ISCSI_BHS_OPCODE_TASK_RESPONSE: 747268705Smav iscsi_pdu_handle_task_response(response); 748275559Smav ISCSI_SESSION_UNLOCK(is); 749268705Smav break; 750255570Strasz case ISCSI_BHS_OPCODE_SCSI_DATA_IN: 751255570Strasz iscsi_pdu_handle_data_in(response); 752275559Smav /* Session lock dropped inside. */ 753275559Smav ISCSI_SESSION_LOCK_ASSERT_NOT(is); 754255570Strasz break; 755255570Strasz case ISCSI_BHS_OPCODE_LOGOUT_RESPONSE: 756255570Strasz iscsi_pdu_handle_logout_response(response); 757275559Smav ISCSI_SESSION_UNLOCK(is); 758255570Strasz break; 759255570Strasz case ISCSI_BHS_OPCODE_R2T: 760255570Strasz iscsi_pdu_handle_r2t(response); 761275559Smav ISCSI_SESSION_UNLOCK(is); 762255570Strasz break; 763255570Strasz case ISCSI_BHS_OPCODE_ASYNC_MESSAGE: 764255570Strasz iscsi_pdu_handle_async_message(response); 765275559Smav ISCSI_SESSION_UNLOCK(is); 766255570Strasz break; 767255570Strasz case ISCSI_BHS_OPCODE_REJECT: 768255570Strasz iscsi_pdu_handle_reject(response); 769275559Smav ISCSI_SESSION_UNLOCK(is); 770255570Strasz break; 771255570Strasz default: 772255570Strasz ISCSI_SESSION_WARN(is, "received PDU with unsupported " 773255570Strasz "opcode 0x%x; reconnecting", 774255570Strasz response->ip_bhs->bhs_opcode); 775255570Strasz iscsi_session_reconnect(is); 776275559Smav ISCSI_SESSION_UNLOCK(is); 777255570Strasz icl_pdu_free(response); 778255570Strasz } 779255570Strasz} 780255570Strasz 781255570Straszstatic void 782255570Strasziscsi_error_callback(struct icl_conn *ic) 783255570Strasz{ 784255570Strasz struct iscsi_session *is; 785255570Strasz 786255570Strasz is = CONN_SESSION(ic); 787255570Strasz 788255570Strasz ISCSI_SESSION_WARN(is, "connection error; reconnecting"); 789255570Strasz iscsi_session_reconnect(is); 790255570Strasz} 791255570Strasz 792255570Straszstatic void 793255570Strasziscsi_pdu_handle_nop_in(struct icl_pdu *response) 794255570Strasz{ 795256187Strasz struct iscsi_session *is; 796255570Strasz struct iscsi_bhs_nop_out *bhsno; 797255570Strasz struct iscsi_bhs_nop_in *bhsni; 798255570Strasz struct icl_pdu *request; 799256187Strasz void *data = NULL; 800256187Strasz size_t datasize; 801256187Strasz int error; 802255570Strasz 803256187Strasz is = PDU_SESSION(response); 804255570Strasz bhsni = (struct iscsi_bhs_nop_in *)response->ip_bhs; 805255570Strasz 806255570Strasz if (bhsni->bhsni_target_transfer_tag == 0xffffffff) { 807255570Strasz /* 808255570Strasz * Nothing to do; iscsi_pdu_update_statsn() already 809255570Strasz * zeroed the timeout. 810255570Strasz */ 811255570Strasz icl_pdu_free(response); 812255570Strasz return; 813255570Strasz } 814255570Strasz 815256187Strasz datasize = icl_pdu_data_segment_length(response); 816256187Strasz if (datasize > 0) { 817256187Strasz data = malloc(datasize, M_ISCSI, M_NOWAIT | M_ZERO); 818256187Strasz if (data == NULL) { 819256187Strasz ISCSI_SESSION_WARN(is, "failed to allocate memory; " 820256187Strasz "reconnecting"); 821256187Strasz icl_pdu_free(response); 822256187Strasz iscsi_session_reconnect(is); 823256187Strasz return; 824256187Strasz } 825256187Strasz icl_pdu_get_data(response, 0, data, datasize); 826256187Strasz } 827256187Strasz 828276618Smav request = icl_pdu_new(response->ip_conn, M_NOWAIT); 829255570Strasz if (request == NULL) { 830256187Strasz ISCSI_SESSION_WARN(is, "failed to allocate memory; " 831256187Strasz "reconnecting"); 832256187Strasz free(data, M_ISCSI); 833255570Strasz icl_pdu_free(response); 834256187Strasz iscsi_session_reconnect(is); 835255570Strasz return; 836255570Strasz } 837255570Strasz bhsno = (struct iscsi_bhs_nop_out *)request->ip_bhs; 838255570Strasz bhsno->bhsno_opcode = ISCSI_BHS_OPCODE_NOP_OUT | 839255570Strasz ISCSI_BHS_OPCODE_IMMEDIATE; 840255570Strasz bhsno->bhsno_flags = 0x80; 841256187Strasz bhsno->bhsno_initiator_task_tag = 0xffffffff; 842255570Strasz bhsno->bhsno_target_transfer_tag = bhsni->bhsni_target_transfer_tag; 843256187Strasz if (datasize > 0) { 844256187Strasz error = icl_pdu_append_data(request, data, datasize, M_NOWAIT); 845256187Strasz if (error != 0) { 846256187Strasz ISCSI_SESSION_WARN(is, "failed to allocate memory; " 847256187Strasz "reconnecting"); 848256187Strasz free(data, M_ISCSI); 849256187Strasz icl_pdu_free(request); 850256187Strasz icl_pdu_free(response); 851256187Strasz iscsi_session_reconnect(is); 852256187Strasz return; 853256187Strasz } 854256187Strasz free(data, M_ISCSI); 855256187Strasz } 856255570Strasz 857255570Strasz icl_pdu_free(response); 858255570Strasz iscsi_pdu_queue_locked(request); 859255570Strasz} 860255570Strasz 861255570Straszstatic void 862255570Strasziscsi_pdu_handle_scsi_response(struct icl_pdu *response) 863255570Strasz{ 864255570Strasz struct iscsi_bhs_scsi_response *bhssr; 865255570Strasz struct iscsi_outstanding *io; 866255570Strasz struct iscsi_session *is; 867275559Smav union ccb *ccb; 868255570Strasz struct ccb_scsiio *csio; 869275559Smav size_t data_segment_len, received; 870255570Strasz uint16_t sense_len; 871255570Strasz 872255570Strasz is = PDU_SESSION(response); 873255570Strasz 874255570Strasz bhssr = (struct iscsi_bhs_scsi_response *)response->ip_bhs; 875255570Strasz io = iscsi_outstanding_find(is, bhssr->bhssr_initiator_task_tag); 876268705Smav if (io == NULL || io->io_ccb == NULL) { 877255570Strasz ISCSI_SESSION_WARN(is, "bad itt 0x%x", bhssr->bhssr_initiator_task_tag); 878255570Strasz icl_pdu_free(response); 879255570Strasz iscsi_session_reconnect(is); 880275559Smav ISCSI_SESSION_UNLOCK(is); 881255570Strasz return; 882255570Strasz } 883255570Strasz 884275559Smav ccb = io->io_ccb; 885275559Smav received = io->io_received; 886275559Smav iscsi_outstanding_remove(is, io); 887275559Smav ISCSI_SESSION_UNLOCK(is); 888275559Smav 889255570Strasz if (bhssr->bhssr_response != BHSSR_RESPONSE_COMMAND_COMPLETED) { 890255570Strasz ISCSI_SESSION_WARN(is, "service response 0x%x", bhssr->bhssr_response); 891275559Smav if ((ccb->ccb_h.status & CAM_DEV_QFRZN) == 0) { 892275559Smav xpt_freeze_devq(ccb->ccb_h.path, 1); 893255570Strasz ISCSI_SESSION_DEBUG(is, "freezing devq"); 894255570Strasz } 895275559Smav ccb->ccb_h.status = CAM_REQ_CMP_ERR | CAM_DEV_QFRZN; 896255570Strasz } else if (bhssr->bhssr_status == 0) { 897275559Smav ccb->ccb_h.status = CAM_REQ_CMP; 898255570Strasz } else { 899275559Smav if ((ccb->ccb_h.status & CAM_DEV_QFRZN) == 0) { 900275559Smav xpt_freeze_devq(ccb->ccb_h.path, 1); 901255570Strasz ISCSI_SESSION_DEBUG(is, "freezing devq"); 902255570Strasz } 903275559Smav ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR | CAM_DEV_QFRZN; 904275559Smav ccb->csio.scsi_status = bhssr->bhssr_status; 905255570Strasz } 906255570Strasz 907275559Smav csio = &ccb->csio; 908255570Strasz data_segment_len = icl_pdu_data_segment_length(response); 909255570Strasz if (data_segment_len > 0) { 910255570Strasz if (data_segment_len < sizeof(sense_len)) { 911255570Strasz ISCSI_SESSION_WARN(is, "truncated data segment (%zd bytes)", 912255570Strasz data_segment_len); 913275559Smav if ((ccb->ccb_h.status & CAM_DEV_QFRZN) == 0) { 914275559Smav xpt_freeze_devq(ccb->ccb_h.path, 1); 915255570Strasz ISCSI_SESSION_DEBUG(is, "freezing devq"); 916255570Strasz } 917275559Smav ccb->ccb_h.status = CAM_REQ_CMP_ERR | CAM_DEV_QFRZN; 918255570Strasz goto out; 919255570Strasz } 920255570Strasz icl_pdu_get_data(response, 0, &sense_len, sizeof(sense_len)); 921255570Strasz sense_len = ntohs(sense_len); 922255570Strasz#if 0 923255570Strasz ISCSI_SESSION_DEBUG(is, "sense_len %d, data len %zd", 924255570Strasz sense_len, data_segment_len); 925255570Strasz#endif 926255570Strasz if (sizeof(sense_len) + sense_len > data_segment_len) { 927255570Strasz ISCSI_SESSION_WARN(is, "truncated data segment " 928255570Strasz "(%zd bytes, should be %zd)", 929255570Strasz data_segment_len, sizeof(sense_len) + sense_len); 930275559Smav if ((ccb->ccb_h.status & CAM_DEV_QFRZN) == 0) { 931275559Smav xpt_freeze_devq(ccb->ccb_h.path, 1); 932255570Strasz ISCSI_SESSION_DEBUG(is, "freezing devq"); 933255570Strasz } 934275559Smav ccb->ccb_h.status = CAM_REQ_CMP_ERR | CAM_DEV_QFRZN; 935255570Strasz goto out; 936255570Strasz } else if (sizeof(sense_len) + sense_len < data_segment_len) 937255570Strasz ISCSI_SESSION_WARN(is, "oversize data segment " 938255570Strasz "(%zd bytes, should be %zd)", 939255570Strasz data_segment_len, sizeof(sense_len) + sense_len); 940255570Strasz if (sense_len > csio->sense_len) { 941255570Strasz ISCSI_SESSION_DEBUG(is, "truncating sense from %d to %d", 942255570Strasz sense_len, csio->sense_len); 943255570Strasz sense_len = csio->sense_len; 944255570Strasz } 945255570Strasz icl_pdu_get_data(response, sizeof(sense_len), &csio->sense_data, sense_len); 946255570Strasz csio->sense_resid = csio->sense_len - sense_len; 947275559Smav ccb->ccb_h.status |= CAM_AUTOSNS_VALID; 948255570Strasz } 949255570Strasz 950255570Straszout: 951255570Strasz if (bhssr->bhssr_flags & BHSSR_FLAGS_RESIDUAL_UNDERFLOW) 952255570Strasz csio->resid = ntohl(bhssr->bhssr_residual_count); 953255570Strasz 954255570Strasz if ((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { 955275559Smav KASSERT(received <= csio->dxfer_len, 956275559Smav ("received > csio->dxfer_len")); 957275559Smav if (received < csio->dxfer_len) { 958275559Smav if (csio->resid != csio->dxfer_len - received) { 959255570Strasz ISCSI_SESSION_WARN(is, "underflow mismatch: " 960255570Strasz "target indicates %d, we calculated %zd", 961275559Smav csio->resid, csio->dxfer_len - received); 962255570Strasz } 963275559Smav csio->resid = csio->dxfer_len - received; 964255570Strasz } 965255570Strasz } 966255570Strasz 967275559Smav xpt_done(ccb); 968255570Strasz icl_pdu_free(response); 969255570Strasz} 970255570Strasz 971255570Straszstatic void 972268705Smaviscsi_pdu_handle_task_response(struct icl_pdu *response) 973268705Smav{ 974268705Smav struct iscsi_bhs_task_management_response *bhstmr; 975268705Smav struct iscsi_outstanding *io, *aio; 976268705Smav struct iscsi_session *is; 977268705Smav 978268705Smav is = PDU_SESSION(response); 979268705Smav 980268705Smav bhstmr = (struct iscsi_bhs_task_management_response *)response->ip_bhs; 981268705Smav io = iscsi_outstanding_find(is, bhstmr->bhstmr_initiator_task_tag); 982268705Smav if (io == NULL || io->io_ccb != NULL) { 983268705Smav ISCSI_SESSION_WARN(is, "bad itt 0x%x", 984268705Smav bhstmr->bhstmr_initiator_task_tag); 985268705Smav icl_pdu_free(response); 986268705Smav iscsi_session_reconnect(is); 987268705Smav return; 988268705Smav } 989268705Smav 990268705Smav if (bhstmr->bhstmr_response != BHSTMR_RESPONSE_FUNCTION_COMPLETE) { 991268705Smav ISCSI_SESSION_WARN(is, "task response 0x%x", 992268705Smav bhstmr->bhstmr_response); 993268705Smav } else { 994268705Smav aio = iscsi_outstanding_find(is, io->io_datasn); 995268705Smav if (aio != NULL && aio->io_ccb != NULL) 996325224Savg iscsi_session_terminate_task(is, aio, CAM_REQ_ABORTED); 997268705Smav } 998268705Smav 999268705Smav iscsi_outstanding_remove(is, io); 1000268705Smav icl_pdu_free(response); 1001268705Smav} 1002268705Smav 1003268705Smavstatic void 1004255570Strasziscsi_pdu_handle_data_in(struct icl_pdu *response) 1005255570Strasz{ 1006255570Strasz struct iscsi_bhs_data_in *bhsdi; 1007255570Strasz struct iscsi_outstanding *io; 1008255570Strasz struct iscsi_session *is; 1009275559Smav union ccb *ccb; 1010255570Strasz struct ccb_scsiio *csio; 1011275559Smav size_t data_segment_len, received, oreceived; 1012255570Strasz 1013255570Strasz is = PDU_SESSION(response); 1014255570Strasz bhsdi = (struct iscsi_bhs_data_in *)response->ip_bhs; 1015255570Strasz io = iscsi_outstanding_find(is, bhsdi->bhsdi_initiator_task_tag); 1016268705Smav if (io == NULL || io->io_ccb == NULL) { 1017255570Strasz ISCSI_SESSION_WARN(is, "bad itt 0x%x", bhsdi->bhsdi_initiator_task_tag); 1018255570Strasz icl_pdu_free(response); 1019255570Strasz iscsi_session_reconnect(is); 1020275559Smav ISCSI_SESSION_UNLOCK(is); 1021255570Strasz return; 1022255570Strasz } 1023255570Strasz 1024255570Strasz data_segment_len = icl_pdu_data_segment_length(response); 1025255570Strasz if (data_segment_len == 0) { 1026255570Strasz /* 1027255570Strasz * "The sending of 0 length data segments should be avoided, 1028255570Strasz * but initiators and targets MUST be able to properly receive 1029255570Strasz * 0 length data segments." 1030255570Strasz */ 1031275559Smav ISCSI_SESSION_UNLOCK(is); 1032255570Strasz icl_pdu_free(response); 1033255570Strasz return; 1034255570Strasz } 1035255570Strasz 1036255570Strasz /* 1037255570Strasz * We need to track this for security reasons - without it, malicious target 1038255570Strasz * could respond to SCSI READ without sending Data-In PDUs, which would result 1039255570Strasz * in read operation on the initiator side returning random kernel data. 1040255570Strasz */ 1041255570Strasz if (ntohl(bhsdi->bhsdi_buffer_offset) != io->io_received) { 1042255570Strasz ISCSI_SESSION_WARN(is, "data out of order; expected offset %zd, got %zd", 1043255570Strasz io->io_received, (size_t)ntohl(bhsdi->bhsdi_buffer_offset)); 1044255570Strasz icl_pdu_free(response); 1045255570Strasz iscsi_session_reconnect(is); 1046275559Smav ISCSI_SESSION_UNLOCK(is); 1047255570Strasz return; 1048255570Strasz } 1049255570Strasz 1050275559Smav ccb = io->io_ccb; 1051275559Smav csio = &ccb->csio; 1052255570Strasz 1053256229Strasz if (io->io_received + data_segment_len > csio->dxfer_len) { 1054255570Strasz ISCSI_SESSION_WARN(is, "oversize data segment (%zd bytes " 1055256229Strasz "at offset %zd, buffer is %d)", 1056256229Strasz data_segment_len, io->io_received, csio->dxfer_len); 1057255570Strasz icl_pdu_free(response); 1058255570Strasz iscsi_session_reconnect(is); 1059275559Smav ISCSI_SESSION_UNLOCK(is); 1060255570Strasz return; 1061255570Strasz } 1062255570Strasz 1063275559Smav oreceived = io->io_received; 1064255570Strasz io->io_received += data_segment_len; 1065275559Smav received = io->io_received; 1066275559Smav if ((bhsdi->bhsdi_flags & BHSDI_FLAGS_S) != 0) 1067275559Smav iscsi_outstanding_remove(is, io); 1068275559Smav ISCSI_SESSION_UNLOCK(is); 1069255570Strasz 1070275559Smav icl_pdu_get_data(response, 0, csio->data_ptr + oreceived, data_segment_len); 1071275559Smav 1072255570Strasz /* 1073255570Strasz * XXX: Check DataSN. 1074255570Strasz * XXX: Check F. 1075255570Strasz */ 1076256238Strasz if ((bhsdi->bhsdi_flags & BHSDI_FLAGS_S) == 0) { 1077256238Strasz /* 1078256238Strasz * Nothing more to do. 1079256238Strasz */ 1080256238Strasz icl_pdu_free(response); 1081256238Strasz return; 1082256238Strasz } 1083256238Strasz 1084256238Strasz //ISCSI_SESSION_DEBUG(is, "got S flag; status 0x%x", bhsdi->bhsdi_status); 1085256238Strasz if (bhsdi->bhsdi_status == 0) { 1086275559Smav ccb->ccb_h.status = CAM_REQ_CMP; 1087256238Strasz } else { 1088275559Smav if ((ccb->ccb_h.status & CAM_DEV_QFRZN) == 0) { 1089275559Smav xpt_freeze_devq(ccb->ccb_h.path, 1); 1090256238Strasz ISCSI_SESSION_DEBUG(is, "freezing devq"); 1091256238Strasz } 1092275559Smav ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR | CAM_DEV_QFRZN; 1093256238Strasz csio->scsi_status = bhsdi->bhsdi_status; 1094256238Strasz } 1095256238Strasz 1096256238Strasz if ((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { 1097275559Smav KASSERT(received <= csio->dxfer_len, 1098275559Smav ("received > csio->dxfer_len")); 1099275559Smav if (received < csio->dxfer_len) { 1100256238Strasz csio->resid = ntohl(bhsdi->bhsdi_residual_count); 1101275559Smav if (csio->resid != csio->dxfer_len - received) { 1102256238Strasz ISCSI_SESSION_WARN(is, "underflow mismatch: " 1103256238Strasz "target indicates %d, we calculated %zd", 1104275559Smav csio->resid, csio->dxfer_len - received); 1105255570Strasz } 1106275559Smav csio->resid = csio->dxfer_len - received; 1107255570Strasz } 1108255570Strasz } 1109255570Strasz 1110275559Smav xpt_done(ccb); 1111255570Strasz icl_pdu_free(response); 1112255570Strasz} 1113255570Strasz 1114255570Straszstatic void 1115255570Strasziscsi_pdu_handle_logout_response(struct icl_pdu *response) 1116255570Strasz{ 1117255570Strasz 1118255570Strasz ISCSI_SESSION_DEBUG(PDU_SESSION(response), "logout response"); 1119255570Strasz icl_pdu_free(response); 1120255570Strasz} 1121255570Strasz 1122255570Straszstatic void 1123255570Strasziscsi_pdu_handle_r2t(struct icl_pdu *response) 1124255570Strasz{ 1125255570Strasz struct icl_pdu *request; 1126255570Strasz struct iscsi_session *is; 1127255570Strasz struct iscsi_bhs_r2t *bhsr2t; 1128255570Strasz struct iscsi_bhs_data_out *bhsdo; 1129255570Strasz struct iscsi_outstanding *io; 1130255570Strasz struct ccb_scsiio *csio; 1131255570Strasz size_t off, len, total_len; 1132255570Strasz int error; 1133255570Strasz 1134255570Strasz is = PDU_SESSION(response); 1135255570Strasz 1136255570Strasz bhsr2t = (struct iscsi_bhs_r2t *)response->ip_bhs; 1137255570Strasz io = iscsi_outstanding_find(is, bhsr2t->bhsr2t_initiator_task_tag); 1138268705Smav if (io == NULL || io->io_ccb == NULL) { 1139255570Strasz ISCSI_SESSION_WARN(is, "bad itt 0x%x; reconnecting", 1140255570Strasz bhsr2t->bhsr2t_initiator_task_tag); 1141255570Strasz icl_pdu_free(response); 1142255570Strasz iscsi_session_reconnect(is); 1143255570Strasz return; 1144255570Strasz } 1145255570Strasz 1146255570Strasz csio = &io->io_ccb->csio; 1147255570Strasz 1148255570Strasz if ((csio->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_OUT) { 1149255570Strasz ISCSI_SESSION_WARN(is, "received R2T for read command; reconnecting"); 1150255570Strasz icl_pdu_free(response); 1151255570Strasz iscsi_session_reconnect(is); 1152255570Strasz return; 1153255570Strasz } 1154255570Strasz 1155255570Strasz /* 1156255570Strasz * XXX: Verify R2TSN. 1157255570Strasz */ 1158255570Strasz 1159255570Strasz io->io_datasn = 0; 1160256229Strasz 1161255570Strasz off = ntohl(bhsr2t->bhsr2t_buffer_offset); 1162256229Strasz if (off > csio->dxfer_len) { 1163256229Strasz ISCSI_SESSION_WARN(is, "target requested invalid offset " 1164256229Strasz "%zd, buffer is is %d; reconnecting", off, csio->dxfer_len); 1165256229Strasz icl_pdu_free(response); 1166256229Strasz iscsi_session_reconnect(is); 1167256229Strasz return; 1168256229Strasz } 1169256229Strasz 1170255570Strasz total_len = ntohl(bhsr2t->bhsr2t_desired_data_transfer_length); 1171256229Strasz if (total_len == 0 || total_len > csio->dxfer_len) { 1172256229Strasz ISCSI_SESSION_WARN(is, "target requested invalid length " 1173256229Strasz "%zd, buffer is %d; reconnecting", total_len, csio->dxfer_len); 1174256229Strasz icl_pdu_free(response); 1175256229Strasz iscsi_session_reconnect(is); 1176256229Strasz return; 1177256229Strasz } 1178255570Strasz 1179255570Strasz //ISCSI_SESSION_DEBUG(is, "r2t; off %zd, len %zd", off, total_len); 1180255570Strasz 1181255570Strasz for (;;) { 1182255570Strasz len = total_len; 1183255570Strasz 1184255570Strasz if (len > is->is_max_data_segment_length) 1185255570Strasz len = is->is_max_data_segment_length; 1186255570Strasz 1187255570Strasz if (off + len > csio->dxfer_len) { 1188256229Strasz ISCSI_SESSION_WARN(is, "target requested invalid " 1189256229Strasz "length/offset %zd, buffer is %d; reconnecting", 1190255570Strasz off + len, csio->dxfer_len); 1191255570Strasz icl_pdu_free(response); 1192255570Strasz iscsi_session_reconnect(is); 1193255570Strasz return; 1194255570Strasz } 1195255570Strasz 1196276618Smav request = icl_pdu_new(response->ip_conn, M_NOWAIT); 1197255570Strasz if (request == NULL) { 1198255570Strasz icl_pdu_free(response); 1199255570Strasz iscsi_session_reconnect(is); 1200255570Strasz return; 1201255570Strasz } 1202255570Strasz 1203255570Strasz bhsdo = (struct iscsi_bhs_data_out *)request->ip_bhs; 1204255570Strasz bhsdo->bhsdo_opcode = ISCSI_BHS_OPCODE_SCSI_DATA_OUT; 1205255570Strasz bhsdo->bhsdo_lun = bhsr2t->bhsr2t_lun; 1206255570Strasz bhsdo->bhsdo_initiator_task_tag = 1207255570Strasz bhsr2t->bhsr2t_initiator_task_tag; 1208255570Strasz bhsdo->bhsdo_target_transfer_tag = 1209255570Strasz bhsr2t->bhsr2t_target_transfer_tag; 1210255570Strasz bhsdo->bhsdo_datasn = htonl(io->io_datasn++); 1211255570Strasz bhsdo->bhsdo_buffer_offset = htonl(off); 1212256229Strasz error = icl_pdu_append_data(request, csio->data_ptr + off, len, 1213256229Strasz M_NOWAIT); 1214255570Strasz if (error != 0) { 1215256229Strasz ISCSI_SESSION_WARN(is, "failed to allocate memory; " 1216256229Strasz "reconnecting"); 1217255570Strasz icl_pdu_free(request); 1218255570Strasz icl_pdu_free(response); 1219255570Strasz iscsi_session_reconnect(is); 1220255570Strasz return; 1221255570Strasz } 1222255570Strasz 1223255570Strasz off += len; 1224255570Strasz total_len -= len; 1225255570Strasz 1226255570Strasz if (total_len == 0) { 1227255570Strasz bhsdo->bhsdo_flags |= BHSDO_FLAGS_F; 1228255570Strasz //ISCSI_SESSION_DEBUG(is, "setting F, off %zd", off); 1229255570Strasz } else { 1230255570Strasz //ISCSI_SESSION_DEBUG(is, "not finished, off %zd", off); 1231255570Strasz } 1232255570Strasz 1233255570Strasz iscsi_pdu_queue_locked(request); 1234255570Strasz 1235255570Strasz if (total_len == 0) 1236255570Strasz break; 1237255570Strasz } 1238255570Strasz 1239255570Strasz icl_pdu_free(response); 1240255570Strasz} 1241255570Strasz 1242255570Straszstatic void 1243255570Strasziscsi_pdu_handle_async_message(struct icl_pdu *response) 1244255570Strasz{ 1245255570Strasz struct iscsi_bhs_asynchronous_message *bhsam; 1246255570Strasz struct iscsi_session *is; 1247255570Strasz 1248255570Strasz is = PDU_SESSION(response); 1249255570Strasz bhsam = (struct iscsi_bhs_asynchronous_message *)response->ip_bhs; 1250255570Strasz switch (bhsam->bhsam_async_event) { 1251255570Strasz case BHSAM_EVENT_TARGET_REQUESTS_LOGOUT: 1252255570Strasz ISCSI_SESSION_WARN(is, "target requests logout; removing session"); 1253255570Strasz iscsi_session_logout(is); 1254255570Strasz iscsi_session_terminate(is); 1255255570Strasz break; 1256255570Strasz case BHSAM_EVENT_TARGET_TERMINATES_CONNECTION: 1257255570Strasz ISCSI_SESSION_WARN(is, "target indicates it will drop drop the connection"); 1258255570Strasz break; 1259255570Strasz case BHSAM_EVENT_TARGET_TERMINATES_SESSION: 1260255570Strasz ISCSI_SESSION_WARN(is, "target indicates it will drop drop the session"); 1261255570Strasz break; 1262255570Strasz default: 1263255570Strasz /* 1264255570Strasz * XXX: Technically, we're obligated to also handle 1265255570Strasz * parameter renegotiation. 1266255570Strasz */ 1267255570Strasz ISCSI_SESSION_WARN(is, "ignoring AsyncEvent %d", bhsam->bhsam_async_event); 1268255570Strasz break; 1269255570Strasz } 1270255570Strasz 1271255570Strasz icl_pdu_free(response); 1272255570Strasz} 1273255570Strasz 1274255570Straszstatic void 1275255570Strasziscsi_pdu_handle_reject(struct icl_pdu *response) 1276255570Strasz{ 1277255570Strasz struct iscsi_bhs_reject *bhsr; 1278255570Strasz struct iscsi_session *is; 1279255570Strasz 1280255570Strasz is = PDU_SESSION(response); 1281255570Strasz bhsr = (struct iscsi_bhs_reject *)response->ip_bhs; 1282255570Strasz ISCSI_SESSION_WARN(is, "received Reject PDU, reason 0x%x; protocol error?", 1283255570Strasz bhsr->bhsr_reason); 1284255570Strasz 1285255570Strasz icl_pdu_free(response); 1286255570Strasz} 1287255570Strasz 1288255570Straszstatic int 1289255570Strasziscsi_ioctl_daemon_wait(struct iscsi_softc *sc, 1290255570Strasz struct iscsi_daemon_request *request) 1291255570Strasz{ 1292255570Strasz struct iscsi_session *is; 1293255570Strasz int error; 1294255570Strasz 1295255570Strasz sx_slock(&sc->sc_lock); 1296255570Strasz for (;;) { 1297255570Strasz TAILQ_FOREACH(is, &sc->sc_sessions, is_next) { 1298265521Strasz ISCSI_SESSION_LOCK(is); 1299255570Strasz if (is->is_waiting_for_iscsid) 1300255570Strasz break; 1301265521Strasz ISCSI_SESSION_UNLOCK(is); 1302255570Strasz } 1303255570Strasz 1304255570Strasz if (is == NULL) { 1305255570Strasz /* 1306255570Strasz * No session requires attention from iscsid(8); wait. 1307255570Strasz */ 1308255570Strasz error = cv_wait_sig(&sc->sc_cv, &sc->sc_lock); 1309255570Strasz if (error != 0) { 1310255570Strasz sx_sunlock(&sc->sc_lock); 1311255570Strasz return (error); 1312255570Strasz } 1313255570Strasz continue; 1314255570Strasz } 1315255570Strasz 1316255570Strasz is->is_waiting_for_iscsid = false; 1317255570Strasz is->is_login_phase = true; 1318255570Strasz is->is_reason[0] = '\0'; 1319255570Strasz ISCSI_SESSION_UNLOCK(is); 1320255570Strasz 1321255570Strasz request->idr_session_id = is->is_id; 1322268703Smav memcpy(&request->idr_isid, &is->is_isid, 1323268703Smav sizeof(request->idr_isid)); 1324268703Smav request->idr_tsih = 0; /* New or reinstated session. */ 1325255570Strasz memcpy(&request->idr_conf, &is->is_conf, 1326255570Strasz sizeof(request->idr_conf)); 1327255570Strasz 1328255570Strasz sx_sunlock(&sc->sc_lock); 1329255570Strasz return (0); 1330255570Strasz } 1331255570Strasz} 1332255570Strasz 1333255570Straszstatic int 1334255570Strasziscsi_ioctl_daemon_handoff(struct iscsi_softc *sc, 1335255570Strasz struct iscsi_daemon_handoff *handoff) 1336255570Strasz{ 1337255570Strasz struct iscsi_session *is; 1338255570Strasz int error; 1339255570Strasz 1340255570Strasz sx_slock(&sc->sc_lock); 1341255570Strasz 1342255570Strasz /* 1343255570Strasz * Find the session to hand off socket to. 1344255570Strasz */ 1345255570Strasz TAILQ_FOREACH(is, &sc->sc_sessions, is_next) { 1346255570Strasz if (is->is_id == handoff->idh_session_id) 1347255570Strasz break; 1348255570Strasz } 1349255570Strasz if (is == NULL) { 1350255570Strasz sx_sunlock(&sc->sc_lock); 1351255570Strasz return (ESRCH); 1352255570Strasz } 1353255570Strasz ISCSI_SESSION_LOCK(is); 1354255570Strasz if (is->is_conf.isc_discovery || is->is_terminating) { 1355255570Strasz ISCSI_SESSION_UNLOCK(is); 1356255570Strasz sx_sunlock(&sc->sc_lock); 1357255570Strasz return (EINVAL); 1358255570Strasz } 1359259306Strasz if (is->is_connected) { 1360259306Strasz /* 1361259306Strasz * This might have happened because another iscsid(8) 1362259306Strasz * instance handed off the connection in the meantime. 1363259306Strasz * Just return. 1364259306Strasz */ 1365259306Strasz ISCSI_SESSION_WARN(is, "handoff on already connected " 1366259306Strasz "session"); 1367259306Strasz ISCSI_SESSION_UNLOCK(is); 1368259306Strasz sx_sunlock(&sc->sc_lock); 1369259306Strasz return (EBUSY); 1370259306Strasz } 1371255570Strasz 1372255570Strasz strlcpy(is->is_target_alias, handoff->idh_target_alias, 1373255570Strasz sizeof(is->is_target_alias)); 1374268703Smav is->is_tsih = handoff->idh_tsih; 1375255570Strasz is->is_statsn = handoff->idh_statsn; 1376255570Strasz is->is_initial_r2t = handoff->idh_initial_r2t; 1377255570Strasz is->is_immediate_data = handoff->idh_immediate_data; 1378255570Strasz is->is_max_data_segment_length = handoff->idh_max_data_segment_length; 1379255570Strasz is->is_max_burst_length = handoff->idh_max_burst_length; 1380255570Strasz is->is_first_burst_length = handoff->idh_first_burst_length; 1381255570Strasz 1382255570Strasz if (handoff->idh_header_digest == ISCSI_DIGEST_CRC32C) 1383255570Strasz is->is_conn->ic_header_crc32c = true; 1384255570Strasz else 1385255570Strasz is->is_conn->ic_header_crc32c = false; 1386255570Strasz if (handoff->idh_data_digest == ISCSI_DIGEST_CRC32C) 1387255570Strasz is->is_conn->ic_data_crc32c = true; 1388255570Strasz else 1389255570Strasz is->is_conn->ic_data_crc32c = false; 1390255570Strasz 1391255570Strasz is->is_cmdsn = 0; 1392265487Strasz is->is_expcmdsn = 0; 1393265487Strasz is->is_maxcmdsn = 0; 1394255570Strasz is->is_waiting_for_iscsid = false; 1395255570Strasz is->is_login_phase = false; 1396255570Strasz is->is_timeout = 0; 1397255570Strasz is->is_connected = true; 1398255570Strasz is->is_reason[0] = '\0'; 1399255570Strasz 1400255570Strasz ISCSI_SESSION_UNLOCK(is); 1401255570Strasz 1402265526Strasz#ifdef ICL_KERNEL_PROXY 1403265526Strasz if (handoff->idh_socket != 0) { 1404265526Strasz#endif 1405265526Strasz /* 1406265526Strasz * Handoff without using ICL proxy. 1407265526Strasz */ 1408265526Strasz error = icl_conn_handoff(is->is_conn, handoff->idh_socket); 1409265526Strasz if (error != 0) { 1410265526Strasz sx_sunlock(&sc->sc_lock); 1411265526Strasz iscsi_session_terminate(is); 1412265526Strasz return (error); 1413265526Strasz } 1414265526Strasz#ifdef ICL_KERNEL_PROXY 1415255570Strasz } 1416255570Strasz#endif 1417255570Strasz 1418255570Strasz sx_sunlock(&sc->sc_lock); 1419255570Strasz 1420255570Strasz if (is->is_sim != NULL) { 1421255570Strasz /* 1422255570Strasz * When reconnecting, there already is SIM allocated for the session. 1423255570Strasz */ 1424255570Strasz KASSERT(is->is_simq_frozen, ("reconnect without frozen simq")); 1425255570Strasz ISCSI_SESSION_LOCK(is); 1426255570Strasz ISCSI_SESSION_DEBUG(is, "releasing"); 1427255570Strasz xpt_release_simq(is->is_sim, 1); 1428255570Strasz is->is_simq_frozen = false; 1429255570Strasz ISCSI_SESSION_UNLOCK(is); 1430255570Strasz 1431255570Strasz } else { 1432255570Strasz ISCSI_SESSION_LOCK(is); 1433255570Strasz is->is_devq = cam_simq_alloc(maxtags); 1434255570Strasz if (is->is_devq == NULL) { 1435255570Strasz ISCSI_SESSION_WARN(is, "failed to allocate simq"); 1436255570Strasz iscsi_session_terminate(is); 1437255570Strasz return (ENOMEM); 1438255570Strasz } 1439255570Strasz 1440255570Strasz is->is_sim = cam_sim_alloc(iscsi_action, iscsi_poll, "iscsi", 1441255570Strasz is, is->is_id /* unit */, &is->is_lock, 1442268704Smav 1, maxtags, is->is_devq); 1443255570Strasz if (is->is_sim == NULL) { 1444255570Strasz ISCSI_SESSION_UNLOCK(is); 1445255570Strasz ISCSI_SESSION_WARN(is, "failed to allocate SIM"); 1446255570Strasz cam_simq_free(is->is_devq); 1447255570Strasz iscsi_session_terminate(is); 1448255570Strasz return (ENOMEM); 1449255570Strasz } 1450255570Strasz 1451255570Strasz error = xpt_bus_register(is->is_sim, NULL, 0); 1452255570Strasz if (error != 0) { 1453255570Strasz ISCSI_SESSION_UNLOCK(is); 1454255570Strasz ISCSI_SESSION_WARN(is, "failed to register bus"); 1455255570Strasz iscsi_session_terminate(is); 1456255570Strasz return (ENOMEM); 1457255570Strasz } 1458255570Strasz 1459255570Strasz error = xpt_create_path(&is->is_path, /*periph*/NULL, 1460255570Strasz cam_sim_path(is->is_sim), CAM_TARGET_WILDCARD, 1461255570Strasz CAM_LUN_WILDCARD); 1462255570Strasz if (error != CAM_REQ_CMP) { 1463255570Strasz ISCSI_SESSION_UNLOCK(is); 1464255570Strasz ISCSI_SESSION_WARN(is, "failed to create path"); 1465255570Strasz iscsi_session_terminate(is); 1466255570Strasz return (ENOMEM); 1467255570Strasz } 1468255570Strasz ISCSI_SESSION_UNLOCK(is); 1469255570Strasz } 1470255570Strasz 1471255570Strasz return (0); 1472255570Strasz} 1473255570Strasz 1474255570Straszstatic int 1475255570Strasziscsi_ioctl_daemon_fail(struct iscsi_softc *sc, 1476255570Strasz struct iscsi_daemon_fail *fail) 1477255570Strasz{ 1478255570Strasz struct iscsi_session *is; 1479255570Strasz 1480255570Strasz sx_slock(&sc->sc_lock); 1481255570Strasz 1482255570Strasz TAILQ_FOREACH(is, &sc->sc_sessions, is_next) { 1483255570Strasz if (is->is_id == fail->idf_session_id) 1484255570Strasz break; 1485255570Strasz } 1486255570Strasz if (is == NULL) { 1487255570Strasz sx_sunlock(&sc->sc_lock); 1488255570Strasz return (ESRCH); 1489255570Strasz } 1490255570Strasz ISCSI_SESSION_LOCK(is); 1491255570Strasz ISCSI_SESSION_DEBUG(is, "iscsid(8) failed: %s", 1492255570Strasz fail->idf_reason); 1493255570Strasz strlcpy(is->is_reason, fail->idf_reason, sizeof(is->is_reason)); 1494255570Strasz //is->is_waiting_for_iscsid = false; 1495255570Strasz //is->is_login_phase = true; 1496255570Strasz //iscsi_session_reconnect(is); 1497255570Strasz ISCSI_SESSION_UNLOCK(is); 1498255570Strasz sx_sunlock(&sc->sc_lock); 1499255570Strasz 1500255570Strasz return (0); 1501255570Strasz} 1502255570Strasz 1503255570Strasz#ifdef ICL_KERNEL_PROXY 1504255570Straszstatic int 1505255570Strasziscsi_ioctl_daemon_connect(struct iscsi_softc *sc, 1506255570Strasz struct iscsi_daemon_connect *idc) 1507255570Strasz{ 1508255570Strasz struct iscsi_session *is; 1509255570Strasz struct sockaddr *from_sa, *to_sa; 1510255570Strasz int error; 1511255570Strasz 1512255570Strasz sx_slock(&sc->sc_lock); 1513255570Strasz TAILQ_FOREACH(is, &sc->sc_sessions, is_next) { 1514255570Strasz if (is->is_id == idc->idc_session_id) 1515255570Strasz break; 1516255570Strasz } 1517255570Strasz if (is == NULL) { 1518255570Strasz sx_sunlock(&sc->sc_lock); 1519255570Strasz return (ESRCH); 1520255570Strasz } 1521255570Strasz sx_sunlock(&sc->sc_lock); 1522255570Strasz 1523255570Strasz if (idc->idc_from_addrlen > 0) { 1524255570Strasz error = getsockaddr(&from_sa, (void *)idc->idc_from_addr, idc->idc_from_addrlen); 1525265526Strasz if (error != 0) { 1526265526Strasz ISCSI_SESSION_WARN(is, 1527265526Strasz "getsockaddr failed with error %d", error); 1528255570Strasz return (error); 1529265526Strasz } 1530255570Strasz } else { 1531255570Strasz from_sa = NULL; 1532255570Strasz } 1533255570Strasz error = getsockaddr(&to_sa, (void *)idc->idc_to_addr, idc->idc_to_addrlen); 1534255570Strasz if (error != 0) { 1535265526Strasz ISCSI_SESSION_WARN(is, "getsockaddr failed with error %d", 1536265526Strasz error); 1537255570Strasz free(from_sa, M_SONAME); 1538255570Strasz return (error); 1539255570Strasz } 1540255570Strasz 1541255570Strasz ISCSI_SESSION_LOCK(is); 1542255570Strasz is->is_waiting_for_iscsid = false; 1543255570Strasz is->is_login_phase = true; 1544255570Strasz is->is_timeout = 0; 1545255570Strasz ISCSI_SESSION_UNLOCK(is); 1546255570Strasz 1547255570Strasz error = icl_conn_connect(is->is_conn, idc->idc_iser, idc->idc_domain, 1548255570Strasz idc->idc_socktype, idc->idc_protocol, from_sa, to_sa); 1549255570Strasz free(from_sa, M_SONAME); 1550255570Strasz free(to_sa, M_SONAME); 1551255570Strasz 1552255570Strasz /* 1553255570Strasz * Digests are always disabled during login phase. 1554255570Strasz */ 1555255570Strasz is->is_conn->ic_header_crc32c = false; 1556255570Strasz is->is_conn->ic_data_crc32c = false; 1557255570Strasz 1558255570Strasz return (error); 1559255570Strasz} 1560255570Strasz 1561255570Straszstatic int 1562255570Strasziscsi_ioctl_daemon_send(struct iscsi_softc *sc, 1563255570Strasz struct iscsi_daemon_send *ids) 1564255570Strasz{ 1565255570Strasz struct iscsi_session *is; 1566255570Strasz struct icl_pdu *ip; 1567255570Strasz size_t datalen; 1568255570Strasz void *data; 1569255570Strasz int error; 1570255570Strasz 1571255570Strasz sx_slock(&sc->sc_lock); 1572255570Strasz TAILQ_FOREACH(is, &sc->sc_sessions, is_next) { 1573255570Strasz if (is->is_id == ids->ids_session_id) 1574255570Strasz break; 1575255570Strasz } 1576255570Strasz if (is == NULL) { 1577255570Strasz sx_sunlock(&sc->sc_lock); 1578255570Strasz return (ESRCH); 1579255570Strasz } 1580255570Strasz sx_sunlock(&sc->sc_lock); 1581255570Strasz 1582255570Strasz if (is->is_login_phase == false) 1583255570Strasz return (EBUSY); 1584255570Strasz 1585255570Strasz if (is->is_terminating || is->is_reconnecting) 1586255570Strasz return (EIO); 1587255570Strasz 1588255570Strasz datalen = ids->ids_data_segment_len; 1589255570Strasz if (datalen > ISCSI_MAX_DATA_SEGMENT_LENGTH) 1590255570Strasz return (EINVAL); 1591255570Strasz if (datalen > 0) { 1592255570Strasz data = malloc(datalen, M_ISCSI, M_WAITOK); 1593255570Strasz error = copyin(ids->ids_data_segment, data, datalen); 1594255570Strasz if (error != 0) { 1595255570Strasz free(data, M_ISCSI); 1596255570Strasz return (error); 1597255570Strasz } 1598255570Strasz } 1599255570Strasz 1600276618Smav ip = icl_pdu_new(is->is_conn, M_WAITOK); 1601255570Strasz memcpy(ip->ip_bhs, ids->ids_bhs, sizeof(*ip->ip_bhs)); 1602255570Strasz if (datalen > 0) { 1603255570Strasz error = icl_pdu_append_data(ip, data, datalen, M_WAITOK); 1604255570Strasz KASSERT(error == 0, ("icl_pdu_append_data(..., M_WAITOK) failed")); 1605255570Strasz free(data, M_ISCSI); 1606255570Strasz } 1607255570Strasz icl_pdu_queue(ip); 1608255570Strasz 1609255570Strasz return (0); 1610255570Strasz} 1611255570Strasz 1612255570Straszstatic int 1613255570Strasziscsi_ioctl_daemon_receive(struct iscsi_softc *sc, 1614255570Strasz struct iscsi_daemon_receive *idr) 1615255570Strasz{ 1616255570Strasz struct iscsi_session *is; 1617255570Strasz struct icl_pdu *ip; 1618255570Strasz void *data; 1619255570Strasz 1620255570Strasz sx_slock(&sc->sc_lock); 1621255570Strasz TAILQ_FOREACH(is, &sc->sc_sessions, is_next) { 1622255570Strasz if (is->is_id == idr->idr_session_id) 1623255570Strasz break; 1624255570Strasz } 1625255570Strasz if (is == NULL) { 1626255570Strasz sx_sunlock(&sc->sc_lock); 1627255570Strasz return (ESRCH); 1628255570Strasz } 1629255570Strasz sx_sunlock(&sc->sc_lock); 1630255570Strasz 1631255570Strasz if (is->is_login_phase == false) 1632255570Strasz return (EBUSY); 1633255570Strasz 1634255570Strasz ISCSI_SESSION_LOCK(is); 1635255570Strasz while (is->is_login_pdu == NULL && 1636255570Strasz is->is_terminating == false && 1637255570Strasz is->is_reconnecting == false) 1638255570Strasz cv_wait(&is->is_login_cv, &is->is_lock); 1639255570Strasz if (is->is_terminating || is->is_reconnecting) { 1640255570Strasz ISCSI_SESSION_UNLOCK(is); 1641255570Strasz return (EIO); 1642255570Strasz } 1643255570Strasz ip = is->is_login_pdu; 1644255570Strasz is->is_login_pdu = NULL; 1645255570Strasz ISCSI_SESSION_UNLOCK(is); 1646255570Strasz 1647255570Strasz if (ip->ip_data_len > idr->idr_data_segment_len) { 1648255570Strasz icl_pdu_free(ip); 1649255570Strasz return (EMSGSIZE); 1650255570Strasz } 1651255570Strasz 1652255570Strasz copyout(ip->ip_bhs, idr->idr_bhs, sizeof(*ip->ip_bhs)); 1653255570Strasz if (ip->ip_data_len > 0) { 1654255570Strasz data = malloc(ip->ip_data_len, M_ISCSI, M_WAITOK); 1655255570Strasz icl_pdu_get_data(ip, 0, data, ip->ip_data_len); 1656255570Strasz copyout(data, idr->idr_data_segment, ip->ip_data_len); 1657255570Strasz free(data, M_ISCSI); 1658255570Strasz } 1659255570Strasz 1660255570Strasz icl_pdu_free(ip); 1661255570Strasz 1662255570Strasz return (0); 1663255570Strasz} 1664255570Strasz#endif /* ICL_KERNEL_PROXY */ 1665255570Strasz 1666255570Straszstatic void 1667255570Strasziscsi_sanitize_session_conf(struct iscsi_session_conf *isc) 1668255570Strasz{ 1669255570Strasz /* 1670255570Strasz * Just make sure all the fields are null-terminated. 1671255570Strasz * 1672255570Strasz * XXX: This is not particularly secure. We should 1673255570Strasz * create our own conf and then copy in relevant 1674255570Strasz * fields. 1675255570Strasz */ 1676255570Strasz isc->isc_initiator[ISCSI_NAME_LEN - 1] = '\0'; 1677255570Strasz isc->isc_initiator_addr[ISCSI_ADDR_LEN - 1] = '\0'; 1678255570Strasz isc->isc_initiator_alias[ISCSI_ALIAS_LEN - 1] = '\0'; 1679255570Strasz isc->isc_target[ISCSI_NAME_LEN - 1] = '\0'; 1680255570Strasz isc->isc_target_addr[ISCSI_ADDR_LEN - 1] = '\0'; 1681255570Strasz isc->isc_user[ISCSI_NAME_LEN - 1] = '\0'; 1682255570Strasz isc->isc_secret[ISCSI_SECRET_LEN - 1] = '\0'; 1683255570Strasz isc->isc_mutual_user[ISCSI_NAME_LEN - 1] = '\0'; 1684255570Strasz isc->isc_mutual_secret[ISCSI_SECRET_LEN - 1] = '\0'; 1685255570Strasz} 1686255570Strasz 1687269065Smavstatic bool 1688269065Smaviscsi_valid_session_conf(const struct iscsi_session_conf *isc) 1689269065Smav{ 1690269065Smav 1691269065Smav if (isc->isc_initiator[0] == '\0') { 1692269065Smav ISCSI_DEBUG("empty isc_initiator"); 1693269065Smav return (false); 1694269065Smav } 1695269065Smav 1696269065Smav if (isc->isc_target_addr[0] == '\0') { 1697269065Smav ISCSI_DEBUG("empty isc_target_addr"); 1698269065Smav return (false); 1699269065Smav } 1700269065Smav 1701269065Smav if (isc->isc_discovery != 0 && isc->isc_target[0] != 0) { 1702269065Smav ISCSI_DEBUG("non-empty isc_target for discovery session"); 1703269065Smav return (false); 1704269065Smav } 1705269065Smav 1706269065Smav if (isc->isc_discovery == 0 && isc->isc_target[0] == 0) { 1707269065Smav ISCSI_DEBUG("empty isc_target for non-discovery session"); 1708269065Smav return (false); 1709269065Smav } 1710269065Smav 1711269065Smav return (true); 1712269065Smav} 1713269065Smav 1714255570Straszstatic int 1715255570Strasziscsi_ioctl_session_add(struct iscsi_softc *sc, struct iscsi_session_add *isa) 1716255570Strasz{ 1717255570Strasz struct iscsi_session *is; 1718255570Strasz const struct iscsi_session *is2; 1719255570Strasz int error; 1720255570Strasz 1721255570Strasz iscsi_sanitize_session_conf(&isa->isa_conf); 1722269065Smav if (iscsi_valid_session_conf(&isa->isa_conf) == false) 1723269065Smav return (EINVAL); 1724255570Strasz 1725255570Strasz is = malloc(sizeof(*is), M_ISCSI, M_ZERO | M_WAITOK); 1726255570Strasz memcpy(&is->is_conf, &isa->isa_conf, sizeof(is->is_conf)); 1727255570Strasz 1728255570Strasz sx_xlock(&sc->sc_lock); 1729255570Strasz 1730255570Strasz /* 1731255570Strasz * Prevent duplicates. 1732255570Strasz */ 1733255570Strasz TAILQ_FOREACH(is2, &sc->sc_sessions, is_next) { 1734255678Strasz if (!!is->is_conf.isc_discovery != 1735255678Strasz !!is2->is_conf.isc_discovery) 1736255678Strasz continue; 1737255678Strasz 1738255678Strasz if (strcmp(is->is_conf.isc_target_addr, 1739255678Strasz is2->is_conf.isc_target_addr) != 0) 1740255678Strasz continue; 1741255678Strasz 1742255678Strasz if (is->is_conf.isc_discovery == 0 && 1743255678Strasz strcmp(is->is_conf.isc_target, 1744255678Strasz is2->is_conf.isc_target) != 0) 1745255678Strasz continue; 1746255678Strasz 1747255678Strasz sx_xunlock(&sc->sc_lock); 1748255678Strasz free(is, M_ISCSI); 1749255678Strasz return (EBUSY); 1750255570Strasz } 1751255570Strasz 1752265496Strasz is->is_conn = icl_conn_new("iscsi", &is->is_lock); 1753255570Strasz is->is_conn->ic_receive = iscsi_receive_callback; 1754255570Strasz is->is_conn->ic_error = iscsi_error_callback; 1755255570Strasz is->is_conn->ic_prv0 = is; 1756255570Strasz TAILQ_INIT(&is->is_outstanding); 1757265500Strasz STAILQ_INIT(&is->is_postponed); 1758255570Strasz mtx_init(&is->is_lock, "iscsi_lock", NULL, MTX_DEF); 1759255570Strasz cv_init(&is->is_maintenance_cv, "iscsi_mt"); 1760255570Strasz#ifdef ICL_KERNEL_PROXY 1761255570Strasz cv_init(&is->is_login_cv, "iscsi_login"); 1762255570Strasz#endif 1763255570Strasz 1764255570Strasz is->is_softc = sc; 1765255570Strasz sc->sc_last_session_id++; 1766255570Strasz is->is_id = sc->sc_last_session_id; 1767268703Smav is->is_isid[0] = 0x80; /* RFC 3720, 10.12.5: 10b, "Random" ISID. */ 1768268703Smav arc4rand(&is->is_isid[1], 5, 0); 1769268703Smav is->is_tsih = 0; 1770255570Strasz callout_init(&is->is_callout, 1); 1771255570Strasz 1772255570Strasz error = kthread_add(iscsi_maintenance_thread, is, NULL, NULL, 0, 0, "iscsimt"); 1773255570Strasz if (error != 0) { 1774255570Strasz ISCSI_SESSION_WARN(is, "kthread_add(9) failed with error %d", error); 1775282954Strasz sx_xunlock(&sc->sc_lock); 1776255570Strasz return (error); 1777255570Strasz } 1778255570Strasz 1779282954Strasz callout_reset(&is->is_callout, 1 * hz, iscsi_callout, is); 1780282954Strasz TAILQ_INSERT_TAIL(&sc->sc_sessions, is, is_next); 1781282954Strasz 1782255570Strasz /* 1783255570Strasz * Trigger immediate reconnection. 1784255570Strasz */ 1785265521Strasz ISCSI_SESSION_LOCK(is); 1786255570Strasz is->is_waiting_for_iscsid = true; 1787255570Strasz strlcpy(is->is_reason, "Waiting for iscsid(8)", sizeof(is->is_reason)); 1788265521Strasz ISCSI_SESSION_UNLOCK(is); 1789255570Strasz cv_signal(&sc->sc_cv); 1790255570Strasz 1791255570Strasz sx_xunlock(&sc->sc_lock); 1792255570Strasz 1793255570Strasz return (0); 1794255570Strasz} 1795255570Strasz 1796255570Straszstatic bool 1797255570Strasziscsi_session_conf_matches(unsigned int id1, const struct iscsi_session_conf *c1, 1798255570Strasz unsigned int id2, const struct iscsi_session_conf *c2) 1799255570Strasz{ 1800274546Strasz 1801274546Strasz if (id2 != 0 && id2 != id1) 1802274546Strasz return (false); 1803255570Strasz if (c2->isc_target[0] != '\0' && 1804274546Strasz strcmp(c1->isc_target, c2->isc_target) != 0) 1805274546Strasz return (false); 1806255570Strasz if (c2->isc_target_addr[0] != '\0' && 1807274546Strasz strcmp(c1->isc_target_addr, c2->isc_target_addr) != 0) 1808274546Strasz return (false); 1809274546Strasz return (true); 1810255570Strasz} 1811255570Strasz 1812255570Straszstatic int 1813255570Strasziscsi_ioctl_session_remove(struct iscsi_softc *sc, 1814255570Strasz struct iscsi_session_remove *isr) 1815255570Strasz{ 1816255570Strasz struct iscsi_session *is, *tmp; 1817255570Strasz bool found = false; 1818255570Strasz 1819255570Strasz iscsi_sanitize_session_conf(&isr->isr_conf); 1820255570Strasz 1821255570Strasz sx_xlock(&sc->sc_lock); 1822255570Strasz TAILQ_FOREACH_SAFE(is, &sc->sc_sessions, is_next, tmp) { 1823255570Strasz ISCSI_SESSION_LOCK(is); 1824255570Strasz if (iscsi_session_conf_matches(is->is_id, &is->is_conf, 1825255570Strasz isr->isr_session_id, &isr->isr_conf)) { 1826255570Strasz found = true; 1827255570Strasz iscsi_session_logout(is); 1828255570Strasz iscsi_session_terminate(is); 1829255570Strasz } 1830255570Strasz ISCSI_SESSION_UNLOCK(is); 1831255570Strasz } 1832255570Strasz sx_xunlock(&sc->sc_lock); 1833255570Strasz 1834255570Strasz if (!found) 1835255570Strasz return (ESRCH); 1836255570Strasz 1837255570Strasz return (0); 1838255570Strasz} 1839255570Strasz 1840255570Straszstatic int 1841255570Strasziscsi_ioctl_session_list(struct iscsi_softc *sc, struct iscsi_session_list *isl) 1842255570Strasz{ 1843255570Strasz int error; 1844255570Strasz unsigned int i = 0; 1845255570Strasz struct iscsi_session *is; 1846255570Strasz struct iscsi_session_state iss; 1847255570Strasz 1848255570Strasz sx_slock(&sc->sc_lock); 1849255570Strasz TAILQ_FOREACH(is, &sc->sc_sessions, is_next) { 1850255570Strasz if (i >= isl->isl_nentries) { 1851255570Strasz sx_sunlock(&sc->sc_lock); 1852255570Strasz return (EMSGSIZE); 1853255570Strasz } 1854255570Strasz memset(&iss, 0, sizeof(iss)); 1855255570Strasz memcpy(&iss.iss_conf, &is->is_conf, sizeof(iss.iss_conf)); 1856255570Strasz iss.iss_id = is->is_id; 1857255570Strasz strlcpy(iss.iss_target_alias, is->is_target_alias, sizeof(iss.iss_target_alias)); 1858255570Strasz strlcpy(iss.iss_reason, is->is_reason, sizeof(iss.iss_reason)); 1859255570Strasz 1860255570Strasz if (is->is_conn->ic_header_crc32c) 1861255570Strasz iss.iss_header_digest = ISCSI_DIGEST_CRC32C; 1862255570Strasz else 1863255570Strasz iss.iss_header_digest = ISCSI_DIGEST_NONE; 1864255570Strasz 1865255570Strasz if (is->is_conn->ic_data_crc32c) 1866255570Strasz iss.iss_data_digest = ISCSI_DIGEST_CRC32C; 1867255570Strasz else 1868255570Strasz iss.iss_data_digest = ISCSI_DIGEST_NONE; 1869255570Strasz 1870255570Strasz iss.iss_max_data_segment_length = is->is_max_data_segment_length; 1871255570Strasz iss.iss_immediate_data = is->is_immediate_data; 1872255570Strasz iss.iss_connected = is->is_connected; 1873255570Strasz 1874255570Strasz error = copyout(&iss, isl->isl_pstates + i, sizeof(iss)); 1875255570Strasz if (error != 0) { 1876255570Strasz sx_sunlock(&sc->sc_lock); 1877255570Strasz return (error); 1878255570Strasz } 1879255570Strasz i++; 1880255570Strasz } 1881255570Strasz sx_sunlock(&sc->sc_lock); 1882255570Strasz 1883255570Strasz isl->isl_nentries = i; 1884255570Strasz 1885255570Strasz return (0); 1886255570Strasz} 1887269065Smav 1888255570Straszstatic int 1889269065Smaviscsi_ioctl_session_modify(struct iscsi_softc *sc, 1890269065Smav struct iscsi_session_modify *ism) 1891269065Smav{ 1892269065Smav struct iscsi_session *is; 1893269065Smav 1894269065Smav iscsi_sanitize_session_conf(&ism->ism_conf); 1895269065Smav if (iscsi_valid_session_conf(&ism->ism_conf) == false) 1896269065Smav return (EINVAL); 1897269065Smav 1898269065Smav sx_xlock(&sc->sc_lock); 1899269065Smav TAILQ_FOREACH(is, &sc->sc_sessions, is_next) { 1900269065Smav ISCSI_SESSION_LOCK(is); 1901269065Smav if (is->is_id == ism->ism_session_id) 1902269065Smav break; 1903269065Smav ISCSI_SESSION_UNLOCK(is); 1904269065Smav } 1905269065Smav if (is == NULL) { 1906269065Smav sx_xunlock(&sc->sc_lock); 1907269065Smav return (ESRCH); 1908269065Smav } 1909269065Smav sx_xunlock(&sc->sc_lock); 1910269065Smav 1911269065Smav memcpy(&is->is_conf, &ism->ism_conf, sizeof(is->is_conf)); 1912269065Smav ISCSI_SESSION_UNLOCK(is); 1913269065Smav 1914269065Smav iscsi_session_reconnect(is); 1915269065Smav 1916269065Smav return (0); 1917269065Smav} 1918269065Smav 1919269065Smavstatic int 1920255570Strasziscsi_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int mode, 1921255570Strasz struct thread *td) 1922255570Strasz{ 1923255570Strasz struct iscsi_softc *sc; 1924255570Strasz 1925255570Strasz sc = dev->si_drv1; 1926255570Strasz 1927255570Strasz switch (cmd) { 1928255570Strasz case ISCSIDWAIT: 1929255570Strasz return (iscsi_ioctl_daemon_wait(sc, 1930255570Strasz (struct iscsi_daemon_request *)arg)); 1931255570Strasz case ISCSIDHANDOFF: 1932255570Strasz return (iscsi_ioctl_daemon_handoff(sc, 1933255570Strasz (struct iscsi_daemon_handoff *)arg)); 1934255570Strasz case ISCSIDFAIL: 1935255570Strasz return (iscsi_ioctl_daemon_fail(sc, 1936255570Strasz (struct iscsi_daemon_fail *)arg)); 1937255570Strasz#ifdef ICL_KERNEL_PROXY 1938255570Strasz case ISCSIDCONNECT: 1939255570Strasz return (iscsi_ioctl_daemon_connect(sc, 1940255570Strasz (struct iscsi_daemon_connect *)arg)); 1941255570Strasz case ISCSIDSEND: 1942255570Strasz return (iscsi_ioctl_daemon_send(sc, 1943255570Strasz (struct iscsi_daemon_send *)arg)); 1944255570Strasz case ISCSIDRECEIVE: 1945255570Strasz return (iscsi_ioctl_daemon_receive(sc, 1946255570Strasz (struct iscsi_daemon_receive *)arg)); 1947255570Strasz#endif /* ICL_KERNEL_PROXY */ 1948255570Strasz case ISCSISADD: 1949255570Strasz return (iscsi_ioctl_session_add(sc, 1950255570Strasz (struct iscsi_session_add *)arg)); 1951255570Strasz case ISCSISREMOVE: 1952255570Strasz return (iscsi_ioctl_session_remove(sc, 1953255570Strasz (struct iscsi_session_remove *)arg)); 1954255570Strasz case ISCSISLIST: 1955255570Strasz return (iscsi_ioctl_session_list(sc, 1956255570Strasz (struct iscsi_session_list *)arg)); 1957269065Smav case ISCSISMODIFY: 1958269065Smav return (iscsi_ioctl_session_modify(sc, 1959269065Smav (struct iscsi_session_modify *)arg)); 1960255570Strasz default: 1961255570Strasz return (EINVAL); 1962255570Strasz } 1963255570Strasz} 1964255570Strasz 1965255570Straszstatic uint64_t 1966255570Strasziscsi_encode_lun(uint32_t lun) 1967255570Strasz{ 1968255570Strasz uint8_t encoded[8]; 1969255570Strasz uint64_t result; 1970255570Strasz 1971255570Strasz memset(encoded, 0, sizeof(encoded)); 1972255570Strasz 1973255570Strasz if (lun < 256) { 1974255570Strasz /* 1975255570Strasz * Peripheral device addressing. 1976255570Strasz */ 1977255570Strasz encoded[1] = lun; 1978255570Strasz } else if (lun < 16384) { 1979255570Strasz /* 1980255570Strasz * Flat space addressing. 1981255570Strasz */ 1982255570Strasz encoded[0] = 0x40; 1983255570Strasz encoded[0] |= (lun >> 8) & 0x3f; 1984255570Strasz encoded[1] = lun & 0xff; 1985255570Strasz } else { 1986255570Strasz /* 1987255570Strasz * Extended flat space addressing. 1988255570Strasz */ 1989255570Strasz encoded[0] = 0xd2; 1990255570Strasz encoded[1] = lun >> 16; 1991255570Strasz encoded[2] = lun >> 8; 1992255570Strasz encoded[3] = lun; 1993255570Strasz } 1994255570Strasz 1995255570Strasz memcpy(&result, encoded, sizeof(result)); 1996255570Strasz return (result); 1997255570Strasz} 1998255570Strasz 1999255570Straszstatic struct iscsi_outstanding * 2000255570Strasziscsi_outstanding_find(struct iscsi_session *is, uint32_t initiator_task_tag) 2001255570Strasz{ 2002255570Strasz struct iscsi_outstanding *io; 2003255570Strasz 2004255570Strasz ISCSI_SESSION_LOCK_ASSERT(is); 2005255570Strasz 2006255570Strasz TAILQ_FOREACH(io, &is->is_outstanding, io_next) { 2007255570Strasz if (io->io_initiator_task_tag == initiator_task_tag) 2008255570Strasz return (io); 2009255570Strasz } 2010255570Strasz return (NULL); 2011255570Strasz} 2012255570Strasz 2013268705Smavstatic struct iscsi_outstanding * 2014268705Smaviscsi_outstanding_find_ccb(struct iscsi_session *is, union ccb *ccb) 2015268705Smav{ 2016268705Smav struct iscsi_outstanding *io; 2017268705Smav 2018268705Smav ISCSI_SESSION_LOCK_ASSERT(is); 2019268705Smav 2020268705Smav TAILQ_FOREACH(io, &is->is_outstanding, io_next) { 2021268705Smav if (io->io_ccb == ccb) 2022268705Smav return (io); 2023268705Smav } 2024268705Smav return (NULL); 2025268705Smav} 2026268705Smav 2027268705Smavstatic struct iscsi_outstanding * 2028255570Strasziscsi_outstanding_add(struct iscsi_session *is, 2029255570Strasz uint32_t initiator_task_tag, union ccb *ccb) 2030255570Strasz{ 2031255570Strasz struct iscsi_outstanding *io; 2032255570Strasz 2033255570Strasz ISCSI_SESSION_LOCK_ASSERT(is); 2034255570Strasz 2035255570Strasz KASSERT(iscsi_outstanding_find(is, initiator_task_tag) == NULL, 2036255570Strasz ("initiator_task_tag 0x%x already added", initiator_task_tag)); 2037255570Strasz 2038255570Strasz io = uma_zalloc(iscsi_outstanding_zone, M_NOWAIT | M_ZERO); 2039255570Strasz if (io == NULL) { 2040255570Strasz ISCSI_SESSION_WARN(is, "failed to allocate %zd bytes", sizeof(*io)); 2041268705Smav return (NULL); 2042255570Strasz } 2043255570Strasz io->io_initiator_task_tag = initiator_task_tag; 2044255570Strasz io->io_ccb = ccb; 2045255570Strasz TAILQ_INSERT_TAIL(&is->is_outstanding, io, io_next); 2046268705Smav return (io); 2047255570Strasz} 2048255570Strasz 2049255570Straszstatic void 2050255570Strasziscsi_outstanding_remove(struct iscsi_session *is, struct iscsi_outstanding *io) 2051255570Strasz{ 2052255570Strasz 2053255570Strasz ISCSI_SESSION_LOCK_ASSERT(is); 2054255570Strasz 2055255570Strasz TAILQ_REMOVE(&is->is_outstanding, io, io_next); 2056255570Strasz uma_zfree(iscsi_outstanding_zone, io); 2057255570Strasz} 2058255570Strasz 2059255570Straszstatic void 2060268705Smaviscsi_action_abort(struct iscsi_session *is, union ccb *ccb) 2061268705Smav{ 2062268705Smav struct icl_pdu *request; 2063268705Smav struct iscsi_bhs_task_management_request *bhstmr; 2064268705Smav struct ccb_abort *cab = &ccb->cab; 2065268705Smav struct iscsi_outstanding *io, *aio; 2066268705Smav 2067268705Smav ISCSI_SESSION_LOCK_ASSERT(is); 2068268705Smav 2069268705Smav#if 0 2070268705Smav KASSERT(is->is_login_phase == false, ("%s called during Login Phase", __func__)); 2071268705Smav#else 2072268705Smav if (is->is_login_phase) { 2073268705Smav ccb->ccb_h.status = CAM_REQ_ABORTED; 2074268705Smav xpt_done(ccb); 2075268705Smav return; 2076268705Smav } 2077268705Smav#endif 2078268705Smav 2079268705Smav aio = iscsi_outstanding_find_ccb(is, cab->abort_ccb); 2080268705Smav if (aio == NULL) { 2081268705Smav ccb->ccb_h.status = CAM_REQ_CMP; 2082268705Smav xpt_done(ccb); 2083268705Smav return; 2084268705Smav } 2085268705Smav 2086276618Smav request = icl_pdu_new(is->is_conn, M_NOWAIT); 2087268705Smav if (request == NULL) { 2088268705Smav ccb->ccb_h.status = CAM_RESRC_UNAVAIL; 2089268705Smav xpt_done(ccb); 2090268705Smav return; 2091268705Smav } 2092268705Smav 2093268705Smav bhstmr = (struct iscsi_bhs_task_management_request *)request->ip_bhs; 2094268705Smav bhstmr->bhstmr_opcode = ISCSI_BHS_OPCODE_TASK_REQUEST; 2095268705Smav bhstmr->bhstmr_function = 0x80 | BHSTMR_FUNCTION_ABORT_TASK; 2096268705Smav 2097268707Smav bhstmr->bhstmr_lun = iscsi_encode_lun(ccb->ccb_h.target_lun); 2098268705Smav bhstmr->bhstmr_initiator_task_tag = is->is_initiator_task_tag; 2099268705Smav is->is_initiator_task_tag++; 2100268705Smav bhstmr->bhstmr_referenced_task_tag = aio->io_initiator_task_tag; 2101268705Smav 2102268705Smav io = iscsi_outstanding_add(is, bhstmr->bhstmr_initiator_task_tag, NULL); 2103268705Smav if (io == NULL) { 2104268705Smav icl_pdu_free(request); 2105268705Smav ccb->ccb_h.status = CAM_RESRC_UNAVAIL; 2106268705Smav xpt_done(ccb); 2107268705Smav return; 2108268705Smav } 2109268705Smav io->io_datasn = aio->io_initiator_task_tag; 2110268705Smav iscsi_pdu_queue_locked(request); 2111268705Smav} 2112268705Smav 2113268705Smavstatic void 2114255570Strasziscsi_action_scsiio(struct iscsi_session *is, union ccb *ccb) 2115255570Strasz{ 2116255570Strasz struct icl_pdu *request; 2117255570Strasz struct iscsi_bhs_scsi_command *bhssc; 2118255570Strasz struct ccb_scsiio *csio; 2119268705Smav struct iscsi_outstanding *io; 2120255570Strasz size_t len; 2121255570Strasz int error; 2122255570Strasz 2123255570Strasz ISCSI_SESSION_LOCK_ASSERT(is); 2124255570Strasz 2125255570Strasz#if 0 2126255570Strasz KASSERT(is->is_login_phase == false, ("%s called during Login Phase", __func__)); 2127255570Strasz#else 2128255570Strasz if (is->is_login_phase) { 2129255570Strasz ISCSI_SESSION_DEBUG(is, "called during login phase"); 2130255570Strasz if ((ccb->ccb_h.status & CAM_DEV_QFRZN) == 0) { 2131255570Strasz xpt_freeze_devq(ccb->ccb_h.path, 1); 2132255570Strasz ISCSI_SESSION_DEBUG(is, "freezing devq"); 2133255570Strasz } 2134255570Strasz ccb->ccb_h.status = CAM_REQ_ABORTED | CAM_DEV_QFRZN; 2135255570Strasz xpt_done(ccb); 2136255570Strasz return; 2137255570Strasz } 2138255570Strasz#endif 2139255570Strasz 2140276618Smav request = icl_pdu_new(is->is_conn, M_NOWAIT); 2141255570Strasz if (request == NULL) { 2142255570Strasz if ((ccb->ccb_h.status & CAM_DEV_QFRZN) == 0) { 2143255570Strasz xpt_freeze_devq(ccb->ccb_h.path, 1); 2144255570Strasz ISCSI_SESSION_DEBUG(is, "freezing devq"); 2145255570Strasz } 2146255570Strasz ccb->ccb_h.status = CAM_RESRC_UNAVAIL | CAM_DEV_QFRZN; 2147255570Strasz xpt_done(ccb); 2148255570Strasz return; 2149255570Strasz } 2150255570Strasz 2151255570Strasz csio = &ccb->csio; 2152255570Strasz bhssc = (struct iscsi_bhs_scsi_command *)request->ip_bhs; 2153255570Strasz bhssc->bhssc_opcode = ISCSI_BHS_OPCODE_SCSI_COMMAND; 2154255570Strasz bhssc->bhssc_flags |= BHSSC_FLAGS_F; 2155255570Strasz switch (csio->ccb_h.flags & CAM_DIR_MASK) { 2156255570Strasz case CAM_DIR_IN: 2157255570Strasz bhssc->bhssc_flags |= BHSSC_FLAGS_R; 2158255570Strasz break; 2159255570Strasz case CAM_DIR_OUT: 2160255570Strasz bhssc->bhssc_flags |= BHSSC_FLAGS_W; 2161255570Strasz break; 2162255570Strasz } 2163255570Strasz 2164268704Smav if ((ccb->ccb_h.flags & CAM_TAG_ACTION_VALID) != 0) { 2165268704Smav switch (csio->tag_action) { 2166268704Smav case MSG_HEAD_OF_Q_TAG: 2167268704Smav bhssc->bhssc_flags |= BHSSC_FLAGS_ATTR_HOQ; 2168268704Smav break; 2169268704Smav case MSG_ORDERED_Q_TAG: 2170268704Smav bhssc->bhssc_flags |= BHSSC_FLAGS_ATTR_ORDERED; 2171268704Smav break; 2172268704Smav case MSG_ACA_TASK: 2173268704Smav bhssc->bhssc_flags |= BHSSC_FLAGS_ATTR_ACA; 2174268704Smav break; 2175268704Smav case MSG_SIMPLE_Q_TAG: 2176268704Smav default: 2177268704Smav bhssc->bhssc_flags |= BHSSC_FLAGS_ATTR_SIMPLE; 2178268704Smav break; 2179268704Smav } 2180268704Smav } else 2181268704Smav bhssc->bhssc_flags |= BHSSC_FLAGS_ATTR_UNTAGGED; 2182255570Strasz 2183255570Strasz bhssc->bhssc_lun = iscsi_encode_lun(csio->ccb_h.target_lun); 2184255570Strasz bhssc->bhssc_initiator_task_tag = is->is_initiator_task_tag; 2185255570Strasz is->is_initiator_task_tag++; 2186255570Strasz bhssc->bhssc_expected_data_transfer_length = htonl(csio->dxfer_len); 2187255570Strasz KASSERT(csio->cdb_len <= sizeof(bhssc->bhssc_cdb), 2188255570Strasz ("unsupported CDB size %zd", (size_t)csio->cdb_len)); 2189255570Strasz 2190255570Strasz if (csio->ccb_h.flags & CAM_CDB_POINTER) 2191255570Strasz memcpy(&bhssc->bhssc_cdb, csio->cdb_io.cdb_ptr, csio->cdb_len); 2192255570Strasz else 2193255570Strasz memcpy(&bhssc->bhssc_cdb, csio->cdb_io.cdb_bytes, csio->cdb_len); 2194255570Strasz 2195268705Smav io = iscsi_outstanding_add(is, bhssc->bhssc_initiator_task_tag, ccb); 2196268705Smav if (io == NULL) { 2197255570Strasz icl_pdu_free(request); 2198255570Strasz if ((ccb->ccb_h.status & CAM_DEV_QFRZN) == 0) { 2199255570Strasz xpt_freeze_devq(ccb->ccb_h.path, 1); 2200255570Strasz ISCSI_SESSION_DEBUG(is, "freezing devq"); 2201255570Strasz } 2202255570Strasz ccb->ccb_h.status = CAM_RESRC_UNAVAIL | CAM_DEV_QFRZN; 2203255570Strasz xpt_done(ccb); 2204255570Strasz return; 2205255570Strasz } 2206255570Strasz 2207255570Strasz if (is->is_immediate_data && 2208255570Strasz (csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT) { 2209255570Strasz len = csio->dxfer_len; 2210255570Strasz //ISCSI_SESSION_DEBUG(is, "adding %zd of immediate data", len); 2211255570Strasz if (len > is->is_first_burst_length) { 2212255570Strasz ISCSI_SESSION_DEBUG(is, "len %zd -> %zd", len, is->is_first_burst_length); 2213255570Strasz len = is->is_first_burst_length; 2214255570Strasz } 2215276234Smav if (len > is->is_max_data_segment_length) { 2216276234Smav ISCSI_SESSION_DEBUG(is, "len %zd -> %zd", len, is->is_max_data_segment_length); 2217276234Smav len = is->is_max_data_segment_length; 2218276234Smav } 2219255570Strasz 2220255570Strasz error = icl_pdu_append_data(request, csio->data_ptr, len, M_NOWAIT); 2221255570Strasz if (error != 0) { 2222255570Strasz icl_pdu_free(request); 2223255570Strasz if ((ccb->ccb_h.status & CAM_DEV_QFRZN) == 0) { 2224255570Strasz xpt_freeze_devq(ccb->ccb_h.path, 1); 2225255570Strasz ISCSI_SESSION_DEBUG(is, "freezing devq"); 2226255570Strasz } 2227255570Strasz ccb->ccb_h.status = CAM_RESRC_UNAVAIL | CAM_DEV_QFRZN; 2228255570Strasz xpt_done(ccb); 2229255570Strasz return; 2230255570Strasz } 2231255570Strasz } 2232255570Strasz iscsi_pdu_queue_locked(request); 2233255570Strasz} 2234255570Strasz 2235255570Straszstatic void 2236255570Strasziscsi_action(struct cam_sim *sim, union ccb *ccb) 2237255570Strasz{ 2238255570Strasz struct iscsi_session *is; 2239255570Strasz 2240255570Strasz is = cam_sim_softc(sim); 2241255570Strasz 2242255570Strasz ISCSI_SESSION_LOCK_ASSERT(is); 2243255570Strasz 2244265523Strasz if (is->is_terminating || 2245265523Strasz (is->is_connected == false && fail_on_disconnection)) { 2246255570Strasz ccb->ccb_h.status = CAM_DEV_NOT_THERE; 2247255570Strasz xpt_done(ccb); 2248255570Strasz return; 2249255570Strasz } 2250255570Strasz 2251255570Strasz switch (ccb->ccb_h.func_code) { 2252255570Strasz case XPT_PATH_INQ: 2253255570Strasz { 2254255570Strasz struct ccb_pathinq *cpi = &ccb->cpi; 2255255570Strasz 2256255570Strasz cpi->version_num = 1; 2257255570Strasz cpi->hba_inquiry = PI_TAG_ABLE; 2258255570Strasz cpi->target_sprt = 0; 2259255570Strasz //cpi->hba_misc = PIM_NOBUSRESET; 2260255570Strasz cpi->hba_misc = 0; 2261255570Strasz cpi->hba_eng_cnt = 0; 2262255570Strasz cpi->max_target = 0; 2263255570Strasz cpi->max_lun = 255; 2264255570Strasz //cpi->initiator_id = 0; /* XXX */ 2265255570Strasz cpi->initiator_id = 64; /* XXX */ 2266255678Strasz strlcpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); 2267255678Strasz strlcpy(cpi->hba_vid, "iSCSI", HBA_IDLEN); 2268255678Strasz strlcpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); 2269255570Strasz cpi->unit_number = cam_sim_unit(sim); 2270255570Strasz cpi->bus_id = cam_sim_bus(sim); 2271255570Strasz cpi->base_transfer_speed = 150000; /* XXX */ 2272255570Strasz cpi->transport = XPORT_ISCSI; 2273255570Strasz cpi->transport_version = 0; 2274255570Strasz cpi->protocol = PROTO_SCSI; 2275255570Strasz cpi->protocol_version = SCSI_REV_SPC3; 2276255570Strasz cpi->maxio = MAXPHYS; 2277255570Strasz cpi->ccb_h.status = CAM_REQ_CMP; 2278255570Strasz break; 2279255570Strasz } 2280268704Smav case XPT_GET_TRAN_SETTINGS: 2281268704Smav { 2282268704Smav struct ccb_trans_settings *cts; 2283268704Smav struct ccb_trans_settings_scsi *scsi; 2284268704Smav 2285268704Smav cts = &ccb->cts; 2286268704Smav scsi = &cts->proto_specific.scsi; 2287268704Smav 2288268704Smav cts->protocol = PROTO_SCSI; 2289268704Smav cts->protocol_version = SCSI_REV_SPC3; 2290268704Smav cts->transport = XPORT_ISCSI; 2291268704Smav cts->transport_version = 0; 2292268704Smav scsi->valid = CTS_SCSI_VALID_TQ; 2293268704Smav scsi->flags = CTS_SCSI_FLAGS_TAG_ENB; 2294268704Smav cts->ccb_h.status = CAM_REQ_CMP; 2295268704Smav break; 2296268704Smav } 2297255570Strasz case XPT_CALC_GEOMETRY: 2298255570Strasz cam_calc_geometry(&ccb->ccg, /*extended*/1); 2299255570Strasz ccb->ccb_h.status = CAM_REQ_CMP; 2300255570Strasz break; 2301255570Strasz#if 0 2302255570Strasz /* 2303255570Strasz * XXX: What's the point? 2304255570Strasz */ 2305255570Strasz case XPT_RESET_BUS: 2306255570Strasz case XPT_TERM_IO: 2307255570Strasz ISCSI_SESSION_DEBUG(is, "faking success for reset, abort, or term_io"); 2308255570Strasz ccb->ccb_h.status = CAM_REQ_CMP; 2309255570Strasz break; 2310255570Strasz#endif 2311268705Smav case XPT_ABORT: 2312268705Smav iscsi_action_abort(is, ccb); 2313268705Smav return; 2314255570Strasz case XPT_SCSI_IO: 2315255570Strasz iscsi_action_scsiio(is, ccb); 2316255570Strasz return; 2317255570Strasz default: 2318255570Strasz#if 0 2319255570Strasz ISCSI_SESSION_DEBUG(is, "got unsupported code 0x%x", ccb->ccb_h.func_code); 2320255570Strasz#endif 2321255570Strasz ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; 2322255570Strasz break; 2323255570Strasz } 2324255570Strasz xpt_done(ccb); 2325255570Strasz} 2326255570Strasz 2327255570Straszstatic void 2328255570Strasziscsi_poll(struct cam_sim *sim) 2329255570Strasz{ 2330255570Strasz 2331255570Strasz KASSERT(0, ("%s: you're not supposed to be here", __func__)); 2332255570Strasz} 2333255570Strasz 2334255570Straszstatic void 2335294708Ssmhiscsi_terminate_sessions(struct iscsi_softc *sc) 2336255570Strasz{ 2337255570Strasz struct iscsi_session *is; 2338255570Strasz 2339294708Ssmh sx_slock(&sc->sc_lock); 2340294708Ssmh TAILQ_FOREACH(is, &sc->sc_sessions, is_next) 2341294708Ssmh iscsi_session_terminate(is); 2342294708Ssmh while(!TAILQ_EMPTY(&sc->sc_sessions)) { 2343294708Ssmh ISCSI_DEBUG("waiting for sessions to terminate"); 2344294708Ssmh cv_wait(&sc->sc_cv, &sc->sc_lock); 2345294708Ssmh } 2346294708Ssmh ISCSI_DEBUG("all sessions terminated"); 2347294708Ssmh sx_sunlock(&sc->sc_lock); 2348294708Ssmh} 2349255570Strasz 2350294708Ssmhstatic void 2351294708Ssmhiscsi_shutdown_pre(struct iscsi_softc *sc) 2352294708Ssmh{ 2353294708Ssmh struct iscsi_session *is; 2354294708Ssmh 2355294708Ssmh if (!fail_on_shutdown) 2356294708Ssmh return; 2357294708Ssmh 2358289509Strasz /* 2359289509Strasz * If we have any sessions waiting for reconnection, request 2360289509Strasz * maintenance thread to fail them immediately instead of waiting 2361289509Strasz * for reconnect timeout. 2362294708Ssmh * 2363294708Ssmh * This prevents LUNs with mounted filesystems that are supported 2364294708Ssmh * by disconnected iSCSI sessions from hanging, however it will 2365294708Ssmh * fail all queued BIOs. 2366289509Strasz */ 2367294708Ssmh ISCSI_DEBUG("forcing failing all disconnected sessions due to shutdown"); 2368294708Ssmh 2369294708Ssmh fail_on_disconnection = 1; 2370294708Ssmh 2371255570Strasz sx_slock(&sc->sc_lock); 2372289509Strasz TAILQ_FOREACH(is, &sc->sc_sessions, is_next) { 2373289509Strasz ISCSI_SESSION_LOCK(is); 2374294708Ssmh if (!is->is_connected) { 2375294708Ssmh ISCSI_SESSION_DEBUG(is, "force failing disconnected session early"); 2376289509Strasz iscsi_session_reconnect(is); 2377294708Ssmh } 2378289509Strasz ISCSI_SESSION_UNLOCK(is); 2379289509Strasz } 2380255570Strasz sx_sunlock(&sc->sc_lock); 2381255570Strasz} 2382255570Strasz 2383294708Ssmhstatic void 2384294708Ssmhiscsi_shutdown_post(struct iscsi_softc *sc) 2385294708Ssmh{ 2386294708Ssmh 2387325226Savg if (panicstr == NULL) { 2388325226Savg ISCSI_DEBUG("removing all sessions due to shutdown"); 2389325226Savg iscsi_terminate_sessions(sc); 2390325226Savg } 2391294708Ssmh} 2392294708Ssmh 2393255570Straszstatic int 2394255570Strasziscsi_load(void) 2395255570Strasz{ 2396255570Strasz int error; 2397255570Strasz 2398255570Strasz sc = malloc(sizeof(*sc), M_ISCSI, M_ZERO | M_WAITOK); 2399255570Strasz sx_init(&sc->sc_lock, "iscsi"); 2400255570Strasz TAILQ_INIT(&sc->sc_sessions); 2401255570Strasz cv_init(&sc->sc_cv, "iscsi_cv"); 2402255570Strasz 2403255570Strasz iscsi_outstanding_zone = uma_zcreate("iscsi_outstanding", 2404255570Strasz sizeof(struct iscsi_outstanding), NULL, NULL, NULL, NULL, 2405256058Strasz UMA_ALIGN_PTR, 0); 2406255570Strasz 2407255570Strasz error = make_dev_p(MAKEDEV_CHECKNAME, &sc->sc_cdev, &iscsi_cdevsw, 2408255570Strasz NULL, UID_ROOT, GID_WHEEL, 0600, "iscsi"); 2409255570Strasz if (error != 0) { 2410255570Strasz ISCSI_WARN("failed to create device node, error %d", error); 2411255570Strasz return (error); 2412255570Strasz } 2413255570Strasz sc->sc_cdev->si_drv1 = sc; 2414255570Strasz 2415294708Ssmh sc->sc_shutdown_pre_eh = EVENTHANDLER_REGISTER(shutdown_pre_sync, 2416294708Ssmh iscsi_shutdown_pre, sc, SHUTDOWN_PRI_FIRST); 2417294708Ssmh /* 2418294708Ssmh * shutdown_post_sync needs to run after filesystem shutdown and before 2419294708Ssmh * CAM shutdown - otherwise when rebooting with an iSCSI session that is 2420294708Ssmh * disconnected but has outstanding requests, dashutdown() will hang on 2421294708Ssmh * cam_periph_runccb(). 2422294708Ssmh */ 2423294708Ssmh sc->sc_shutdown_post_eh = EVENTHANDLER_REGISTER(shutdown_post_sync, 2424294708Ssmh iscsi_shutdown_post, sc, SHUTDOWN_PRI_DEFAULT - 1); 2425255570Strasz 2426255570Strasz return (0); 2427255570Strasz} 2428255570Strasz 2429255570Straszstatic int 2430255570Strasziscsi_unload(void) 2431255570Strasz{ 2432255570Strasz 2433255857Strasz if (sc->sc_cdev != NULL) { 2434255857Strasz ISCSI_DEBUG("removing device node"); 2435255857Strasz destroy_dev(sc->sc_cdev); 2436255857Strasz ISCSI_DEBUG("device node removed"); 2437255857Strasz } 2438255570Strasz 2439294708Ssmh if (sc->sc_shutdown_pre_eh != NULL) 2440294708Ssmh EVENTHANDLER_DEREGISTER(shutdown_pre_sync, sc->sc_shutdown_pre_eh); 2441294708Ssmh if (sc->sc_shutdown_post_eh != NULL) 2442294708Ssmh EVENTHANDLER_DEREGISTER(shutdown_post_sync, sc->sc_shutdown_post_eh); 2443255570Strasz 2444294708Ssmh iscsi_terminate_sessions(sc); 2445255570Strasz 2446255570Strasz uma_zdestroy(iscsi_outstanding_zone); 2447255570Strasz sx_destroy(&sc->sc_lock); 2448255570Strasz cv_destroy(&sc->sc_cv); 2449255570Strasz free(sc, M_ISCSI); 2450255570Strasz return (0); 2451255570Strasz} 2452255570Strasz 2453255570Straszstatic int 2454255570Strasziscsi_quiesce(void) 2455255570Strasz{ 2456255570Strasz sx_slock(&sc->sc_lock); 2457255570Strasz if (!TAILQ_EMPTY(&sc->sc_sessions)) { 2458255570Strasz sx_sunlock(&sc->sc_lock); 2459255570Strasz return (EBUSY); 2460255570Strasz } 2461255570Strasz sx_sunlock(&sc->sc_lock); 2462255570Strasz return (0); 2463255570Strasz} 2464255570Strasz 2465255570Straszstatic int 2466255570Strasziscsi_modevent(module_t mod, int what, void *arg) 2467255570Strasz{ 2468255570Strasz int error; 2469255570Strasz 2470255570Strasz switch (what) { 2471255570Strasz case MOD_LOAD: 2472255570Strasz error = iscsi_load(); 2473255570Strasz break; 2474255570Strasz case MOD_UNLOAD: 2475255570Strasz error = iscsi_unload(); 2476255570Strasz break; 2477255570Strasz case MOD_QUIESCE: 2478255570Strasz error = iscsi_quiesce(); 2479255570Strasz break; 2480255570Strasz default: 2481255570Strasz error = EINVAL; 2482255570Strasz break; 2483255570Strasz } 2484255570Strasz return (error); 2485255570Strasz} 2486255570Strasz 2487255570Straszmoduledata_t iscsi_data = { 2488255570Strasz "iscsi", 2489255570Strasz iscsi_modevent, 2490255570Strasz 0 2491255570Strasz}; 2492255570Strasz 2493255570StraszDECLARE_MODULE(iscsi, iscsi_data, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 2494255570StraszMODULE_DEPEND(iscsi, cam, 1, 1, 1); 2495255570StraszMODULE_DEPEND(iscsi, icl, 1, 1, 1); 2496