iscsi.c revision 265495
1169689Skan/*- 2169689Skan * Copyright (c) 2012 The FreeBSD Foundation 3169689Skan * All rights reserved. 4169689Skan * 5169689Skan * This software was developed by Edward Tomasz Napierala under sponsorship 6169689Skan * from the FreeBSD Foundation. 7169689Skan * 8169689Skan * Redistribution and use in source and binary forms, with or without 9169689Skan * modification, are permitted provided that the following conditions 10169689Skan * are met: 11169689Skan * 1. Redistributions of source code must retain the above copyright 12169689Skan * notice, this list of conditions and the following disclaimer. 13169689Skan * 2. Redistributions in binary form must reproduce the above copyright 14169689Skan * notice, this list of conditions and the following disclaimer in the 15169689Skan * documentation and/or other materials provided with the distribution. 16169689Skan * 17169689Skan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18169689Skan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19169689Skan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20169689Skan * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21169689Skan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22169689Skan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23169689Skan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24169689Skan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25169689Skan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26169689Skan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27169689Skan * SUCH DAMAGE. 28169689Skan * 29169689Skan * $FreeBSD: stable/10/sys/dev/iscsi/iscsi.c 265495 2014-05-07 06:29:01Z trasz $ 30169689Skan */ 31169689Skan 32169689Skan#include <sys/param.h> 33169689Skan#include <sys/condvar.h> 34169689Skan#include <sys/conf.h> 35169689Skan#include <sys/eventhandler.h> 36169689Skan#include <sys/file.h> 37169689Skan#include <sys/kernel.h> 38169689Skan#include <sys/kthread.h> 39169689Skan#include <sys/lock.h> 40169689Skan#include <sys/malloc.h> 41169689Skan#include <sys/mutex.h> 42169689Skan#include <sys/module.h> 43169689Skan#include <sys/sysctl.h> 44169689Skan#include <sys/systm.h> 45169689Skan#include <sys/sx.h> 46169689Skan#include <vm/uma.h> 47169689Skan 48169689Skan#include <cam/cam.h> 49169689Skan#include <cam/cam_ccb.h> 50169689Skan#include <cam/cam_xpt.h> 51169689Skan#include <cam/cam_debug.h> 52169689Skan#include <cam/cam_sim.h> 53169689Skan#include <cam/cam_xpt_sim.h> 54169689Skan#include <cam/cam_xpt_periph.h> 55169689Skan#include <cam/cam_periph.h> 56169689Skan#include <cam/scsi/scsi_all.h> 57169689Skan#include <cam/scsi/scsi_message.h> 58169689Skan 59169689Skan#include "iscsi_ioctl.h" 60169689Skan#include "iscsi.h" 61169689Skan#include "icl.h" 62169689Skan#include "iscsi_proto.h" 63169689Skan 64169689Skan#ifdef ICL_KERNEL_PROXY 65169689Skan#include <sys/socketvar.h> 66169689Skan#endif 67169689Skan 68169689Skan/* 69169689Skan * XXX: This is global so the iscsi_unload() can access it. 70169689Skan * Think about how to do this properly. 71169689Skan */ 72169689Skanstatic struct iscsi_softc *sc; 73169689Skan 74169689SkanSYSCTL_NODE(_kern, OID_AUTO, iscsi, CTLFLAG_RD, 0, "iSCSI initiator"); 75169689Skanstatic int debug = 1; 76169689SkanTUNABLE_INT("kern.iscsi.debug", &debug); 77169689SkanSYSCTL_INT(_kern_iscsi, OID_AUTO, debug, CTLFLAG_RW, 78169689Skan &debug, 2, "Enable debug messages"); 79169689Skanstatic int ping_timeout = 5; 80169689SkanTUNABLE_INT("kern.iscsi.ping_timeout", &ping_timeout); 81169689SkanSYSCTL_INT(_kern_iscsi, OID_AUTO, ping_timeout, CTLFLAG_RW, &ping_timeout, 82169689Skan 5, "Timeout for ping (NOP-Out) requests, in seconds"); 83169689Skanstatic int iscsid_timeout = 60; 84169689SkanTUNABLE_INT("kern.iscsi.iscsid_timeout", &iscsid_timeout); 85169689SkanSYSCTL_INT(_kern_iscsi, OID_AUTO, iscsid_timeout, CTLFLAG_RW, &iscsid_timeout, 86169689Skan 60, "Time to wait for iscsid(8) to handle reconnection, in seconds"); 87169689Skanstatic int login_timeout = 60; 88169689SkanTUNABLE_INT("kern.iscsi.login_timeout", &login_timeout); 89169689SkanSYSCTL_INT(_kern_iscsi, OID_AUTO, login_timeout, CTLFLAG_RW, &login_timeout, 90169689Skan 60, "Time to wait for iscsid(8) to finish Login Phase, in seconds"); 91169689Skanstatic int maxtags = 255; 92169689SkanTUNABLE_INT("kern.iscsi.maxtags", &maxtags); 93169689SkanSYSCTL_INT(_kern_iscsi, OID_AUTO, maxtags, CTLFLAG_RW, &maxtags, 94169689Skan 255, "Max number of IO requests queued"); 95169689Skan 96169689Skanstatic MALLOC_DEFINE(M_ISCSI, "iSCSI", "iSCSI initiator"); 97169689Skanstatic uma_zone_t iscsi_outstanding_zone; 98169689Skan 99169689Skan#define CONN_SESSION(X) ((struct iscsi_session *)X->ic_prv0) 100169689Skan#define PDU_SESSION(X) (CONN_SESSION(X->ip_conn)) 101169689Skan 102169689Skan#define ISCSI_DEBUG(X, ...) \ 103169689Skan if (debug > 1) { \ 104169689Skan printf("%s: " X "\n", __func__, ## __VA_ARGS__);\ 105169689Skan } while (0) 106169689Skan 107169689Skan#define ISCSI_WARN(X, ...) \ 108169689Skan if (debug > 0) { \ 109169689Skan printf("WARNING: %s: " X "\n", \ 110169689Skan __func__, ## __VA_ARGS__); \ 111169689Skan } while (0) 112169689Skan 113169689Skan#define ISCSI_SESSION_DEBUG(S, X, ...) \ 114169689Skan if (debug > 1) { \ 115169689Skan printf("%s: %s (%s): " X "\n", \ 116169689Skan __func__, S->is_conf.isc_target_addr, \ 117169689Skan S->is_conf.isc_target, ## __VA_ARGS__); \ 118169689Skan } while (0) 119169689Skan 120169689Skan#define ISCSI_SESSION_WARN(S, X, ...) \ 121169689Skan if (debug > 0) { \ 122169689Skan printf("WARNING: %s (%s): " X "\n", \ 123169689Skan S->is_conf.isc_target_addr, \ 124169689Skan S->is_conf.isc_target, ## __VA_ARGS__); \ 125169689Skan } while (0) 126169689Skan 127169689Skan#define ISCSI_SESSION_LOCK(X) mtx_lock(&X->is_lock) 128169689Skan#define ISCSI_SESSION_UNLOCK(X) mtx_unlock(&X->is_lock) 129169689Skan#define ISCSI_SESSION_LOCK_ASSERT(X) mtx_assert(&X->is_lock, MA_OWNED) 130169689Skan 131169689Skanstatic int iscsi_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, 132169689Skan int mode, struct thread *td); 133169689Skan 134169689Skanstatic struct cdevsw iscsi_cdevsw = { 135169689Skan .d_version = D_VERSION, 136169689Skan .d_ioctl = iscsi_ioctl, 137169689Skan .d_name = "iscsi", 138169689Skan}; 139169689Skan 140169689Skanstatic void iscsi_pdu_queue_locked(struct icl_pdu *request); 141169689Skanstatic void iscsi_pdu_queue(struct icl_pdu *request); 142169689Skanstatic void iscsi_pdu_update_statsn(const struct icl_pdu *response); 143169689Skanstatic void iscsi_pdu_handle_nop_in(struct icl_pdu *response); 144169689Skanstatic void iscsi_pdu_handle_scsi_response(struct icl_pdu *response); 145169689Skanstatic void iscsi_pdu_handle_data_in(struct icl_pdu *response); 146169689Skanstatic void iscsi_pdu_handle_logout_response(struct icl_pdu *response); 147169689Skanstatic void iscsi_pdu_handle_r2t(struct icl_pdu *response); 148169689Skanstatic void iscsi_pdu_handle_async_message(struct icl_pdu *response); 149169689Skanstatic void iscsi_pdu_handle_reject(struct icl_pdu *response); 150169689Skanstatic void iscsi_session_reconnect(struct iscsi_session *is); 151169689Skanstatic void iscsi_session_terminate(struct iscsi_session *is); 152169689Skanstatic void iscsi_action(struct cam_sim *sim, union ccb *ccb); 153169689Skanstatic void iscsi_poll(struct cam_sim *sim); 154169689Skanstatic struct iscsi_outstanding *iscsi_outstanding_find(struct iscsi_session *is, 155169689Skan uint32_t initiator_task_tag); 156169689Skanstatic int iscsi_outstanding_add(struct iscsi_session *is, 157169689Skan uint32_t initiator_task_tag, union ccb *ccb); 158169689Skanstatic void iscsi_outstanding_remove(struct iscsi_session *is, 159169689Skan struct iscsi_outstanding *io); 160169689Skan 161169689Skanstatic bool 162169689Skaniscsi_pdu_prepare(struct icl_pdu *request) 163169689Skan{ 164169689Skan struct iscsi_session *is; 165169689Skan struct iscsi_bhs_scsi_command *bhssc; 166169689Skan 167169689Skan is = PDU_SESSION(request); 168169689Skan 169169689Skan ISCSI_SESSION_LOCK_ASSERT(is); 170169689Skan 171169689Skan /* 172169689Skan * We're only using fields common for all the request 173169689Skan * (initiator -> target) PDUs. 174169689Skan */ 175169689Skan bhssc = (struct iscsi_bhs_scsi_command *)request->ip_bhs; 176169689Skan 177169689Skan /* 178169689Skan * Data-Out PDU does not contain CmdSN. 179169689Skan */ 180169689Skan if (bhssc->bhssc_opcode != ISCSI_BHS_OPCODE_SCSI_DATA_OUT) { 181169689Skan if (is->is_cmdsn > is->is_maxcmdsn && 182169689Skan (bhssc->bhssc_opcode & ISCSI_BHS_OPCODE_IMMEDIATE) == 0) { 183169689Skan /* 184169689Skan * Current MaxCmdSN prevents us from sending any more 185169689Skan * SCSI Command PDUs to the target; postpone the PDU. 186169689Skan * It will get resent by either iscsi_pdu_queue(), 187169689Skan * or by maintenance thread. 188169689Skan */ 189169689Skan#if 0 190169689Skan ISCSI_SESSION_DEBUG(is, "postponing send, CmdSN %d, ExpCmdSN %d, MaxCmdSN %d, opcode 0x%x", 191169689Skan is->is_cmdsn, is->is_expcmdsn, is->is_maxcmdsn, bhssc->bhssc_opcode); 192169689Skan#endif 193169689Skan return (true); 194169689Skan } 195169689Skan bhssc->bhssc_cmdsn = htonl(is->is_cmdsn); 196169689Skan if ((bhssc->bhssc_opcode & ISCSI_BHS_OPCODE_IMMEDIATE) == 0) 197169689Skan is->is_cmdsn++; 198169689Skan } 199169689Skan bhssc->bhssc_expstatsn = htonl(is->is_statsn + 1); 200169689Skan 201169689Skan return (false); 202169689Skan} 203169689Skan 204169689Skanstatic void 205169689Skaniscsi_session_send_postponed(struct iscsi_session *is) 206169689Skan{ 207169689Skan struct icl_pdu *request; 208169689Skan bool postpone; 209169689Skan 210169689Skan ISCSI_SESSION_LOCK_ASSERT(is); 211169689Skan 212169689Skan while (!TAILQ_EMPTY(&is->is_postponed)) { 213169689Skan request = TAILQ_FIRST(&is->is_postponed); 214169689Skan postpone = iscsi_pdu_prepare(request); 215169689Skan if (postpone) 216169689Skan break; 217169689Skan TAILQ_REMOVE(&is->is_postponed, request, ip_next); 218169689Skan icl_pdu_queue(request); 219169689Skan } 220169689Skan} 221169689Skan 222169689Skanstatic void 223169689Skaniscsi_pdu_queue_locked(struct icl_pdu *request) 224169689Skan{ 225169689Skan struct iscsi_session *is; 226169689Skan bool postpone; 227169689Skan 228169689Skan is = PDU_SESSION(request); 229169689Skan ISCSI_SESSION_LOCK_ASSERT(is); 230169689Skan iscsi_session_send_postponed(is); 231169689Skan postpone = iscsi_pdu_prepare(request); 232169689Skan if (postpone) { 233169689Skan TAILQ_INSERT_TAIL(&is->is_postponed, request, ip_next); 234169689Skan return; 235169689Skan } 236169689Skan icl_pdu_queue(request); 237169689Skan} 238169689Skan 239169689Skanstatic void 240169689Skaniscsi_pdu_queue(struct icl_pdu *request) 241169689Skan{ 242169689Skan struct iscsi_session *is; 243169689Skan 244169689Skan is = PDU_SESSION(request); 245169689Skan ISCSI_SESSION_LOCK(is); 246169689Skan iscsi_pdu_queue_locked(request); 247169689Skan ISCSI_SESSION_UNLOCK(is); 248169689Skan} 249169689Skan 250169689Skanstatic void 251169689Skaniscsi_session_logout(struct iscsi_session *is) 252169689Skan{ 253169689Skan struct icl_pdu *request; 254169689Skan struct iscsi_bhs_logout_request *bhslr; 255169689Skan 256169689Skan request = icl_pdu_new_bhs(is->is_conn, M_NOWAIT); 257169689Skan if (request == NULL) 258169689Skan return; 259169689Skan 260169689Skan bhslr = (struct iscsi_bhs_logout_request *)request->ip_bhs; 261169689Skan bhslr->bhslr_opcode = ISCSI_BHS_OPCODE_LOGOUT_REQUEST; 262169689Skan bhslr->bhslr_reason = BHSLR_REASON_CLOSE_SESSION; 263169689Skan iscsi_pdu_queue_locked(request); 264169689Skan} 265169689Skan 266169689Skanstatic void 267169689Skaniscsi_session_terminate_tasks(struct iscsi_session *is, bool requeue) 268169689Skan{ 269169689Skan struct iscsi_outstanding *io, *tmp; 270169689Skan 271169689Skan ISCSI_SESSION_LOCK_ASSERT(is); 272169689Skan 273169689Skan TAILQ_FOREACH_SAFE(io, &is->is_outstanding, io_next, tmp) { 274169689Skan if (requeue) { 275169689Skan io->io_ccb->ccb_h.status &= ~CAM_SIM_QUEUED; 276169689Skan io->io_ccb->ccb_h.status |= CAM_REQUEUE_REQ; 277169689Skan } else { 278169689Skan io->io_ccb->ccb_h.status = CAM_REQ_ABORTED; 279169689Skan } 280169689Skan 281169689Skan if ((io->io_ccb->ccb_h.status & CAM_DEV_QFRZN) == 0) { 282169689Skan xpt_freeze_devq(io->io_ccb->ccb_h.path, 1); 283169689Skan ISCSI_SESSION_DEBUG(is, "freezing devq"); 284169689Skan } 285169689Skan io->io_ccb->ccb_h.status |= CAM_DEV_QFRZN; 286169689Skan xpt_done(io->io_ccb); 287169689Skan iscsi_outstanding_remove(is, io); 288169689Skan } 289169689Skan} 290169689Skan 291169689Skanstatic void 292169689Skaniscsi_maintenance_thread_reconnect(struct iscsi_session *is) 293169689Skan{ 294169689Skan struct icl_pdu *pdu; 295169689Skan 296169689Skan icl_conn_shutdown(is->is_conn); 297169689Skan icl_conn_close(is->is_conn); 298169689Skan 299169689Skan ISCSI_SESSION_LOCK(is); 300169689Skan 301169689Skan#ifdef ICL_KERNEL_PROXY 302169689Skan if (is->is_login_pdu != NULL) { 303169689Skan icl_pdu_free(is->is_login_pdu); 304169689Skan is->is_login_pdu = NULL; 305169689Skan } 306169689Skan cv_signal(&is->is_login_cv); 307169689Skan#endif 308169689Skan 309169689Skan /* 310169689Skan * Don't queue any new PDUs. 311169689Skan */ 312169689Skan if (is->is_sim != NULL && is->is_simq_frozen == false) { 313169689Skan ISCSI_SESSION_DEBUG(is, "freezing"); 314169689Skan xpt_freeze_simq(is->is_sim, 1); 315169689Skan is->is_simq_frozen = true; 316169689Skan } 317169689Skan 318169689Skan /* 319169689Skan * Remove postponed PDUs. 320169689Skan */ 321169689Skan while (!TAILQ_EMPTY(&is->is_postponed)) { 322169689Skan pdu = TAILQ_FIRST(&is->is_postponed); 323169689Skan TAILQ_REMOVE(&is->is_postponed, pdu, ip_next); 324169689Skan icl_pdu_free(pdu); 325169689Skan } 326169689Skan 327169689Skan /* 328169689Skan * Terminate SCSI tasks, asking CAM to requeue them. 329169689Skan */ 330169689Skan //ISCSI_SESSION_DEBUG(is, "terminating tasks"); 331169689Skan iscsi_session_terminate_tasks(is, true); 332169689Skan 333169689Skan KASSERT(TAILQ_EMPTY(&is->is_outstanding), 334169689Skan ("destroying session with active tasks")); 335169689Skan KASSERT(TAILQ_EMPTY(&is->is_postponed), 336169689Skan ("destroying session with postponed PDUs")); 337169689Skan 338169689Skan /* 339169689Skan * Request immediate reconnection from iscsid(8). 340169689Skan */ 341169689Skan //ISCSI_SESSION_DEBUG(is, "waking up iscsid(8)"); 342169689Skan is->is_connected = false; 343169689Skan is->is_reconnecting = false; 344169689Skan is->is_login_phase = false; 345169689Skan is->is_waiting_for_iscsid = true; 346169689Skan strlcpy(is->is_reason, "Waiting for iscsid(8)", sizeof(is->is_reason)); 347169689Skan is->is_timeout = 0; 348169689Skan ISCSI_SESSION_UNLOCK(is); 349169689Skan cv_signal(&is->is_softc->sc_cv); 350169689Skan} 351169689Skan 352169689Skanstatic void 353169689Skaniscsi_maintenance_thread_terminate(struct iscsi_session *is) 354169689Skan{ 355169689Skan struct iscsi_softc *sc; 356169689Skan struct icl_pdu *pdu; 357169689Skan 358169689Skan sc = is->is_softc; 359169689Skan sx_xlock(&sc->sc_lock); 360169689Skan TAILQ_REMOVE(&sc->sc_sessions, is, is_next); 361169689Skan sx_xunlock(&sc->sc_lock); 362169689Skan 363169689Skan icl_conn_close(is->is_conn); 364169689Skan 365169689Skan ISCSI_SESSION_LOCK(is); 366169689Skan 367169689Skan KASSERT(is->is_terminating, ("is_terminating == false")); 368169689Skan 369169689Skan#ifdef ICL_KERNEL_PROXY 370169689Skan if (is->is_login_pdu != NULL) { 371169689Skan icl_pdu_free(is->is_login_pdu); 372169689Skan is->is_login_pdu = NULL; 373169689Skan } 374169689Skan cv_signal(&is->is_login_cv); 375169689Skan#endif 376169689Skan 377169689Skan /* 378169689Skan * Don't queue any new PDUs. 379169689Skan */ 380169689Skan callout_drain(&is->is_callout); 381169689Skan if (is->is_sim != NULL && is->is_simq_frozen == false) { 382169689Skan ISCSI_SESSION_DEBUG(is, "freezing"); 383169689Skan xpt_freeze_simq(is->is_sim, 1); 384169689Skan is->is_simq_frozen = true; 385169689Skan } 386169689Skan 387169689Skan /* 388169689Skan * Remove postponed PDUs. 389169689Skan */ 390169689Skan while (!TAILQ_EMPTY(&is->is_postponed)) { 391169689Skan pdu = TAILQ_FIRST(&is->is_postponed); 392169689Skan TAILQ_REMOVE(&is->is_postponed, pdu, ip_next); 393169689Skan icl_pdu_free(pdu); 394169689Skan } 395169689Skan 396169689Skan /* 397169689Skan * Forcibly terminate SCSI tasks. 398169689Skan */ 399169689Skan ISCSI_SESSION_DEBUG(is, "terminating tasks"); 400169689Skan iscsi_session_terminate_tasks(is, false); 401169689Skan 402169689Skan /* 403169689Skan * Deregister CAM. 404169689Skan */ 405169689Skan if (is->is_sim != NULL) { 406169689Skan ISCSI_SESSION_DEBUG(is, "deregistering SIM"); 407169689Skan xpt_async(AC_LOST_DEVICE, is->is_path, NULL); 408169689Skan 409169689Skan if (is->is_simq_frozen) { 410169689Skan xpt_release_simq(is->is_sim, 1); 411169689Skan is->is_simq_frozen = false; 412169689Skan } 413169689Skan 414169689Skan xpt_free_path(is->is_path); 415169689Skan xpt_bus_deregister(cam_sim_path(is->is_sim)); 416169689Skan cam_sim_free(is->is_sim, TRUE /*free_devq*/); 417169689Skan is->is_sim = NULL; 418169689Skan } 419169689Skan 420169689Skan KASSERT(TAILQ_EMPTY(&is->is_outstanding), 421169689Skan ("destroying session with active tasks")); 422169689Skan KASSERT(TAILQ_EMPTY(&is->is_postponed), 423169689Skan ("destroying session with postponed PDUs")); 424169689Skan 425169689Skan ISCSI_SESSION_UNLOCK(is); 426169689Skan 427169689Skan icl_conn_free(is->is_conn); 428169689Skan mtx_destroy(&is->is_lock); 429169689Skan cv_destroy(&is->is_maintenance_cv); 430169689Skan#ifdef ICL_KERNEL_PROXY 431169689Skan cv_destroy(&is->is_login_cv); 432169689Skan#endif 433169689Skan ISCSI_SESSION_DEBUG(is, "terminated"); 434169689Skan free(is, M_ISCSI); 435169689Skan 436169689Skan /* 437169689Skan * The iscsi_unload() routine might be waiting. 438169689Skan */ 439169689Skan cv_signal(&sc->sc_cv); 440169689Skan} 441169689Skan 442169689Skanstatic void 443169689Skaniscsi_maintenance_thread(void *arg) 444169689Skan{ 445169689Skan struct iscsi_session *is; 446169689Skan 447169689Skan is = arg; 448169689Skan 449169689Skan for (;;) { 450169689Skan ISCSI_SESSION_LOCK(is); 451169689Skan if (is->is_reconnecting == false && 452169689Skan is->is_terminating == false && 453169689Skan TAILQ_EMPTY(&is->is_postponed)) 454169689Skan cv_wait(&is->is_maintenance_cv, &is->is_lock); 455169689Skan 456169689Skan if (is->is_reconnecting) { 457169689Skan ISCSI_SESSION_UNLOCK(is); 458169689Skan iscsi_maintenance_thread_reconnect(is); 459169689Skan continue; 460169689Skan } 461169689Skan 462169689Skan if (is->is_terminating) { 463169689Skan ISCSI_SESSION_UNLOCK(is); 464169689Skan iscsi_maintenance_thread_terminate(is); 465169689Skan kthread_exit(); 466169689Skan return; 467169689Skan } 468169689Skan 469169689Skan iscsi_session_send_postponed(is); 470169689Skan ISCSI_SESSION_UNLOCK(is); 471169689Skan } 472169689Skan} 473169689Skan 474169689Skanstatic void 475169689Skaniscsi_session_reconnect(struct iscsi_session *is) 476169689Skan{ 477169689Skan 478169689Skan /* 479169689Skan * XXX: We can't use locking here, because 480169689Skan * it's being called from various contexts. 481169689Skan * Hope it doesn't break anything. 482169689Skan */ 483169689Skan if (is->is_reconnecting) 484169689Skan return; 485169689Skan 486169689Skan is->is_reconnecting = true; 487169689Skan cv_signal(&is->is_maintenance_cv); 488169689Skan} 489169689Skan 490169689Skanstatic void 491169689Skaniscsi_session_terminate(struct iscsi_session *is) 492169689Skan{ 493169689Skan if (is->is_terminating) 494169689Skan return; 495169689Skan 496169689Skan is->is_terminating = true; 497169689Skan 498169689Skan#if 0 499169689Skan iscsi_session_logout(is); 500169689Skan#endif 501169689Skan cv_signal(&is->is_maintenance_cv); 502169689Skan} 503169689Skan 504169689Skanstatic void 505169689Skaniscsi_callout(void *context) 506169689Skan{ 507169689Skan struct icl_pdu *request; 508169689Skan struct iscsi_bhs_nop_out *bhsno; 509169689Skan struct iscsi_session *is; 510169689Skan bool reconnect_needed = false; 511169689Skan 512169689Skan is = context; 513169689Skan 514169689Skan if (is->is_terminating) 515169689Skan return; 516169689Skan 517169689Skan callout_schedule(&is->is_callout, 1 * hz); 518169689Skan 519169689Skan ISCSI_SESSION_LOCK(is); 520169689Skan is->is_timeout++; 521169689Skan 522169689Skan if (is->is_waiting_for_iscsid) { 523169689Skan if (is->is_timeout > iscsid_timeout) { 524169689Skan ISCSI_SESSION_WARN(is, "timed out waiting for iscsid(8) " 525169689Skan "for %d seconds; reconnecting", 526169689Skan is->is_timeout); 527169689Skan reconnect_needed = true; 528169689Skan } 529169689Skan goto out; 530169689Skan } 531169689Skan 532169689Skan if (is->is_login_phase) { 533169689Skan if (is->is_timeout > login_timeout) { 534169689Skan ISCSI_SESSION_WARN(is, "login timed out after %d seconds; " 535169689Skan "reconnecting", is->is_timeout); 536169689Skan reconnect_needed = true; 537169689Skan } 538169689Skan goto out; 539169689Skan } 540169689Skan 541169689Skan if (is->is_timeout >= ping_timeout) { 542169689Skan ISCSI_SESSION_WARN(is, "no ping reply (NOP-In) after %d seconds; " 543169689Skan "reconnecting", ping_timeout); 544169689Skan reconnect_needed = true; 545169689Skan goto out; 546169689Skan } 547169689Skan 548169689Skan ISCSI_SESSION_UNLOCK(is); 549169689Skan 550169689Skan /* 551169689Skan * If the ping was reset less than one second ago - which means 552169689Skan * that we've received some PDU during the last second - assume 553169689Skan * the traffic flows correctly and don't bother sending a NOP-Out. 554169689Skan * 555169689Skan * (It's 2 - one for one second, and one for incrementing is_timeout 556169689Skan * earlier in this routine.) 557169689Skan */ 558169689Skan if (is->is_timeout < 2) 559169689Skan return; 560169689Skan 561169689Skan request = icl_pdu_new_bhs(is->is_conn, M_NOWAIT); 562169689Skan if (request == NULL) { 563169689Skan ISCSI_SESSION_WARN(is, "failed to allocate PDU"); 564169689Skan return; 565169689Skan } 566169689Skan bhsno = (struct iscsi_bhs_nop_out *)request->ip_bhs; 567169689Skan bhsno->bhsno_opcode = ISCSI_BHS_OPCODE_NOP_OUT | 568169689Skan ISCSI_BHS_OPCODE_IMMEDIATE; 569169689Skan bhsno->bhsno_flags = 0x80; 570169689Skan bhsno->bhsno_target_transfer_tag = 0xffffffff; 571169689Skan iscsi_pdu_queue(request); 572169689Skan return; 573169689Skan 574169689Skanout: 575169689Skan ISCSI_SESSION_UNLOCK(is); 576169689Skan 577169689Skan if (reconnect_needed) 578169689Skan iscsi_session_reconnect(is); 579169689Skan} 580169689Skan 581169689Skanstatic void 582169689Skaniscsi_pdu_update_statsn(const struct icl_pdu *response) 583169689Skan{ 584169689Skan const struct iscsi_bhs_data_in *bhsdi; 585169689Skan struct iscsi_session *is; 586169689Skan uint32_t expcmdsn, maxcmdsn; 587169689Skan 588169689Skan is = PDU_SESSION(response); 589169689Skan 590169689Skan ISCSI_SESSION_LOCK_ASSERT(is); 591169689Skan 592169689Skan /* 593169689Skan * We're only using fields common for all the response 594169689Skan * (target -> initiator) PDUs. 595169689Skan */ 596169689Skan bhsdi = (const struct iscsi_bhs_data_in *)response->ip_bhs; 597169689Skan /* 598169689Skan * Ok, I lied. In case of Data-In, "The fields StatSN, Status, 599169689Skan * and Residual Count only have meaningful content if the S bit 600169689Skan * is set to 1", so we also need to check the bit specific for 601169689Skan * Data-In PDU. 602169689Skan */ 603169689Skan if (bhsdi->bhsdi_opcode != ISCSI_BHS_OPCODE_SCSI_DATA_IN || 604169689Skan (bhsdi->bhsdi_flags & BHSDI_FLAGS_S) != 0) { 605169689Skan if (ntohl(bhsdi->bhsdi_statsn) < is->is_statsn) { 606169689Skan ISCSI_SESSION_WARN(is, 607169689Skan "PDU StatSN %d >= session StatSN %d, opcode 0x%x", 608169689Skan is->is_statsn, ntohl(bhsdi->bhsdi_statsn), 609169689Skan bhsdi->bhsdi_opcode); 610169689Skan } 611169689Skan is->is_statsn = ntohl(bhsdi->bhsdi_statsn); 612169689Skan } 613169689Skan 614169689Skan expcmdsn = ntohl(bhsdi->bhsdi_expcmdsn); 615169689Skan maxcmdsn = ntohl(bhsdi->bhsdi_maxcmdsn); 616169689Skan 617169689Skan /* 618169689Skan * XXX: Compare using Serial Arithmetic Sense. 619169689Skan */ 620169689Skan if (maxcmdsn + 1 < expcmdsn) { 621169689Skan ISCSI_SESSION_DEBUG(is, "PDU MaxCmdSN %d + 1 < PDU ExpCmdSN %d; ignoring", 622169689Skan maxcmdsn, expcmdsn); 623169689Skan } else { 624169689Skan if (maxcmdsn > is->is_maxcmdsn) { 625169689Skan is->is_maxcmdsn = maxcmdsn; 626169689Skan 627169689Skan /* 628169689Skan * Command window increased; kick the maintanance thread 629169689Skan * to send out postponed commands. 630169689Skan */ 631169689Skan if (!TAILQ_EMPTY(&is->is_postponed)) 632169689Skan cv_signal(&is->is_maintenance_cv); 633169689Skan } else if (maxcmdsn < is->is_maxcmdsn) { 634169689Skan ISCSI_SESSION_DEBUG(is, "PDU MaxCmdSN %d < session MaxCmdSN %d; ignoring", 635169689Skan maxcmdsn, is->is_maxcmdsn); 636169689Skan } 637169689Skan 638169689Skan if (expcmdsn > is->is_expcmdsn) { 639169689Skan is->is_expcmdsn = expcmdsn; 640169689Skan } else if (expcmdsn < is->is_expcmdsn) { 641169689Skan ISCSI_SESSION_DEBUG(is, "PDU ExpCmdSN %d < session ExpCmdSN %d; ignoring", 642169689Skan expcmdsn, is->is_expcmdsn); 643169689Skan } 644169689Skan } 645169689Skan 646169689Skan /* 647169689Skan * Every incoming PDU - not just NOP-In - resets the ping timer. 648169689Skan * The purpose of the timeout is to reset the connection when it stalls; 649169689Skan * we don't want this to happen when NOP-In or NOP-Out ends up delayed 650169689Skan * in some queue. 651169689Skan */ 652169689Skan is->is_timeout = 0; 653169689Skan} 654169689Skan 655169689Skanstatic void 656169689Skaniscsi_receive_callback(struct icl_pdu *response) 657169689Skan{ 658169689Skan struct iscsi_session *is; 659169689Skan 660169689Skan is = PDU_SESSION(response); 661169689Skan 662169689Skan ISCSI_SESSION_LOCK(is); 663169689Skan 664169689Skan#ifdef ICL_KERNEL_PROXY 665169689Skan if (is->is_login_phase) { 666169689Skan if (is->is_login_pdu == NULL) 667169689Skan is->is_login_pdu = response; 668169689Skan else 669169689Skan icl_pdu_free(response); 670169689Skan ISCSI_SESSION_UNLOCK(is); 671169689Skan cv_signal(&is->is_login_cv); 672169689Skan return; 673169689Skan } 674169689Skan#endif 675169689Skan 676169689Skan iscsi_pdu_update_statsn(response); 677169689Skan 678169689Skan /* 679169689Skan * The handling routine is responsible for freeing the PDU 680169689Skan * when it's no longer needed. 681169689Skan */ 682169689Skan switch (response->ip_bhs->bhs_opcode) { 683169689Skan case ISCSI_BHS_OPCODE_NOP_IN: 684169689Skan iscsi_pdu_handle_nop_in(response); 685169689Skan break; 686169689Skan case ISCSI_BHS_OPCODE_SCSI_RESPONSE: 687169689Skan iscsi_pdu_handle_scsi_response(response); 688169689Skan break; 689169689Skan case ISCSI_BHS_OPCODE_SCSI_DATA_IN: 690169689Skan iscsi_pdu_handle_data_in(response); 691169689Skan break; 692169689Skan case ISCSI_BHS_OPCODE_LOGOUT_RESPONSE: 693169689Skan iscsi_pdu_handle_logout_response(response); 694169689Skan break; 695169689Skan case ISCSI_BHS_OPCODE_R2T: 696169689Skan iscsi_pdu_handle_r2t(response); 697169689Skan break; 698169689Skan case ISCSI_BHS_OPCODE_ASYNC_MESSAGE: 699169689Skan iscsi_pdu_handle_async_message(response); 700169689Skan break; 701169689Skan case ISCSI_BHS_OPCODE_REJECT: 702169689Skan iscsi_pdu_handle_reject(response); 703169689Skan break; 704169689Skan default: 705169689Skan ISCSI_SESSION_WARN(is, "received PDU with unsupported " 706169689Skan "opcode 0x%x; reconnecting", 707169689Skan response->ip_bhs->bhs_opcode); 708169689Skan iscsi_session_reconnect(is); 709169689Skan icl_pdu_free(response); 710169689Skan } 711169689Skan 712169689Skan ISCSI_SESSION_UNLOCK(is); 713169689Skan} 714169689Skan 715169689Skanstatic void 716169689Skaniscsi_error_callback(struct icl_conn *ic) 717169689Skan{ 718169689Skan struct iscsi_session *is; 719169689Skan 720169689Skan is = CONN_SESSION(ic); 721169689Skan 722169689Skan ISCSI_SESSION_WARN(is, "connection error; reconnecting"); 723169689Skan iscsi_session_reconnect(is); 724169689Skan} 725169689Skan 726169689Skanstatic void 727169689Skaniscsi_pdu_handle_nop_in(struct icl_pdu *response) 728169689Skan{ 729169689Skan struct iscsi_session *is; 730169689Skan struct iscsi_bhs_nop_out *bhsno; 731169689Skan struct iscsi_bhs_nop_in *bhsni; 732169689Skan struct icl_pdu *request; 733169689Skan void *data = NULL; 734169689Skan size_t datasize; 735169689Skan int error; 736169689Skan 737169689Skan is = PDU_SESSION(response); 738169689Skan bhsni = (struct iscsi_bhs_nop_in *)response->ip_bhs; 739169689Skan 740169689Skan if (bhsni->bhsni_target_transfer_tag == 0xffffffff) { 741169689Skan /* 742169689Skan * Nothing to do; iscsi_pdu_update_statsn() already 743169689Skan * zeroed the timeout. 744169689Skan */ 745169689Skan icl_pdu_free(response); 746169689Skan return; 747169689Skan } 748169689Skan 749169689Skan datasize = icl_pdu_data_segment_length(response); 750169689Skan if (datasize > 0) { 751169689Skan data = malloc(datasize, M_ISCSI, M_NOWAIT | M_ZERO); 752169689Skan if (data == NULL) { 753169689Skan ISCSI_SESSION_WARN(is, "failed to allocate memory; " 754169689Skan "reconnecting"); 755169689Skan icl_pdu_free(response); 756169689Skan iscsi_session_reconnect(is); 757169689Skan return; 758169689Skan } 759169689Skan icl_pdu_get_data(response, 0, data, datasize); 760169689Skan } 761169689Skan 762169689Skan request = icl_pdu_new_bhs(response->ip_conn, M_NOWAIT); 763169689Skan if (request == NULL) { 764169689Skan ISCSI_SESSION_WARN(is, "failed to allocate memory; " 765169689Skan "reconnecting"); 766169689Skan free(data, M_ISCSI); 767169689Skan icl_pdu_free(response); 768169689Skan iscsi_session_reconnect(is); 769169689Skan return; 770169689Skan } 771169689Skan bhsno = (struct iscsi_bhs_nop_out *)request->ip_bhs; 772169689Skan bhsno->bhsno_opcode = ISCSI_BHS_OPCODE_NOP_OUT | 773169689Skan ISCSI_BHS_OPCODE_IMMEDIATE; 774169689Skan bhsno->bhsno_flags = 0x80; 775169689Skan bhsno->bhsno_initiator_task_tag = 0xffffffff; 776169689Skan bhsno->bhsno_target_transfer_tag = bhsni->bhsni_target_transfer_tag; 777169689Skan if (datasize > 0) { 778169689Skan error = icl_pdu_append_data(request, data, datasize, M_NOWAIT); 779169689Skan if (error != 0) { 780169689Skan ISCSI_SESSION_WARN(is, "failed to allocate memory; " 781169689Skan "reconnecting"); 782169689Skan free(data, M_ISCSI); 783169689Skan icl_pdu_free(request); 784169689Skan icl_pdu_free(response); 785169689Skan iscsi_session_reconnect(is); 786169689Skan return; 787169689Skan } 788169689Skan free(data, M_ISCSI); 789169689Skan } 790169689Skan 791169689Skan icl_pdu_free(response); 792169689Skan iscsi_pdu_queue_locked(request); 793169689Skan} 794169689Skan 795169689Skanstatic void 796169689Skaniscsi_pdu_handle_scsi_response(struct icl_pdu *response) 797169689Skan{ 798169689Skan struct iscsi_bhs_scsi_response *bhssr; 799169689Skan struct iscsi_outstanding *io; 800169689Skan struct iscsi_session *is; 801169689Skan struct ccb_scsiio *csio; 802169689Skan size_t data_segment_len; 803169689Skan uint16_t sense_len; 804169689Skan 805169689Skan is = PDU_SESSION(response); 806169689Skan 807169689Skan bhssr = (struct iscsi_bhs_scsi_response *)response->ip_bhs; 808169689Skan io = iscsi_outstanding_find(is, bhssr->bhssr_initiator_task_tag); 809169689Skan if (io == NULL) { 810169689Skan ISCSI_SESSION_WARN(is, "bad itt 0x%x", bhssr->bhssr_initiator_task_tag); 811169689Skan icl_pdu_free(response); 812169689Skan iscsi_session_reconnect(is); 813169689Skan return; 814169689Skan } 815169689Skan 816169689Skan if (bhssr->bhssr_response != BHSSR_RESPONSE_COMMAND_COMPLETED) { 817169689Skan ISCSI_SESSION_WARN(is, "service response 0x%x", bhssr->bhssr_response); 818169689Skan if ((io->io_ccb->ccb_h.status & CAM_DEV_QFRZN) == 0) { 819169689Skan xpt_freeze_devq(io->io_ccb->ccb_h.path, 1); 820169689Skan ISCSI_SESSION_DEBUG(is, "freezing devq"); 821169689Skan } 822169689Skan io->io_ccb->ccb_h.status = CAM_REQ_CMP_ERR | CAM_DEV_QFRZN; 823169689Skan } else if (bhssr->bhssr_status == 0) { 824169689Skan io->io_ccb->ccb_h.status = CAM_REQ_CMP; 825169689Skan } else { 826169689Skan if ((io->io_ccb->ccb_h.status & CAM_DEV_QFRZN) == 0) { 827169689Skan xpt_freeze_devq(io->io_ccb->ccb_h.path, 1); 828169689Skan ISCSI_SESSION_DEBUG(is, "freezing devq"); 829169689Skan } 830169689Skan io->io_ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR | CAM_DEV_QFRZN; 831169689Skan io->io_ccb->csio.scsi_status = bhssr->bhssr_status; 832169689Skan } 833169689Skan 834169689Skan if (bhssr->bhssr_flags & BHSSR_FLAGS_RESIDUAL_OVERFLOW) { 835169689Skan ISCSI_SESSION_WARN(is, "target indicated residual overflow"); 836169689Skan icl_pdu_free(response); 837169689Skan iscsi_session_reconnect(is); 838169689Skan return; 839169689Skan } 840169689Skan 841169689Skan csio = &io->io_ccb->csio; 842169689Skan 843169689Skan data_segment_len = icl_pdu_data_segment_length(response); 844169689Skan if (data_segment_len > 0) { 845169689Skan if (data_segment_len < sizeof(sense_len)) { 846169689Skan ISCSI_SESSION_WARN(is, "truncated data segment (%zd bytes)", 847169689Skan data_segment_len); 848169689Skan if ((io->io_ccb->ccb_h.status & CAM_DEV_QFRZN) == 0) { 849169689Skan xpt_freeze_devq(io->io_ccb->ccb_h.path, 1); 850169689Skan ISCSI_SESSION_DEBUG(is, "freezing devq"); 851169689Skan } 852169689Skan io->io_ccb->ccb_h.status = CAM_REQ_CMP_ERR | CAM_DEV_QFRZN; 853169689Skan goto out; 854169689Skan } 855169689Skan icl_pdu_get_data(response, 0, &sense_len, sizeof(sense_len)); 856169689Skan sense_len = ntohs(sense_len); 857169689Skan#if 0 858169689Skan ISCSI_SESSION_DEBUG(is, "sense_len %d, data len %zd", 859169689Skan sense_len, data_segment_len); 860169689Skan#endif 861169689Skan if (sizeof(sense_len) + sense_len > data_segment_len) { 862169689Skan ISCSI_SESSION_WARN(is, "truncated data segment " 863169689Skan "(%zd bytes, should be %zd)", 864169689Skan data_segment_len, sizeof(sense_len) + sense_len); 865169689Skan if ((io->io_ccb->ccb_h.status & CAM_DEV_QFRZN) == 0) { 866169689Skan xpt_freeze_devq(io->io_ccb->ccb_h.path, 1); 867169689Skan ISCSI_SESSION_DEBUG(is, "freezing devq"); 868169689Skan } 869169689Skan io->io_ccb->ccb_h.status = CAM_REQ_CMP_ERR | CAM_DEV_QFRZN; 870169689Skan goto out; 871169689Skan } else if (sizeof(sense_len) + sense_len < data_segment_len) 872169689Skan ISCSI_SESSION_WARN(is, "oversize data segment " 873169689Skan "(%zd bytes, should be %zd)", 874169689Skan data_segment_len, sizeof(sense_len) + sense_len); 875169689Skan if (sense_len > csio->sense_len) { 876169689Skan ISCSI_SESSION_DEBUG(is, "truncating sense from %d to %d", 877169689Skan sense_len, csio->sense_len); 878169689Skan sense_len = csio->sense_len; 879169689Skan } 880169689Skan icl_pdu_get_data(response, sizeof(sense_len), &csio->sense_data, sense_len); 881169689Skan csio->sense_resid = csio->sense_len - sense_len; 882169689Skan io->io_ccb->ccb_h.status |= CAM_AUTOSNS_VALID; 883169689Skan } 884169689Skan 885169689Skanout: 886169689Skan if (bhssr->bhssr_flags & BHSSR_FLAGS_RESIDUAL_UNDERFLOW) 887169689Skan csio->resid = ntohl(bhssr->bhssr_residual_count); 888169689Skan 889169689Skan if ((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { 890169689Skan KASSERT(io->io_received <= csio->dxfer_len, 891169689Skan ("io->io_received > csio->dxfer_len")); 892169689Skan if (io->io_received < csio->dxfer_len) { 893169689Skan if (csio->resid != csio->dxfer_len - io->io_received) { 894169689Skan ISCSI_SESSION_WARN(is, "underflow mismatch: " 895169689Skan "target indicates %d, we calculated %zd", 896169689Skan csio->resid, 897169689Skan csio->dxfer_len - io->io_received); 898169689Skan } 899169689Skan csio->resid = csio->dxfer_len - io->io_received; 900169689Skan } 901169689Skan } 902169689Skan 903169689Skan xpt_done(io->io_ccb); 904169689Skan iscsi_outstanding_remove(is, io); 905169689Skan icl_pdu_free(response); 906169689Skan} 907169689Skan 908169689Skanstatic void 909169689Skaniscsi_pdu_handle_data_in(struct icl_pdu *response) 910169689Skan{ 911169689Skan struct iscsi_bhs_data_in *bhsdi; 912169689Skan struct iscsi_outstanding *io; 913169689Skan struct iscsi_session *is; 914169689Skan struct ccb_scsiio *csio; 915169689Skan size_t data_segment_len; 916169689Skan 917169689Skan is = PDU_SESSION(response); 918169689Skan bhsdi = (struct iscsi_bhs_data_in *)response->ip_bhs; 919169689Skan io = iscsi_outstanding_find(is, bhsdi->bhsdi_initiator_task_tag); 920169689Skan if (io == NULL) { 921169689Skan ISCSI_SESSION_WARN(is, "bad itt 0x%x", bhsdi->bhsdi_initiator_task_tag); 922169689Skan icl_pdu_free(response); 923169689Skan iscsi_session_reconnect(is); 924169689Skan return; 925169689Skan } 926169689Skan 927169689Skan data_segment_len = icl_pdu_data_segment_length(response); 928169689Skan if (data_segment_len == 0) { 929169689Skan /* 930169689Skan * "The sending of 0 length data segments should be avoided, 931169689Skan * but initiators and targets MUST be able to properly receive 932169689Skan * 0 length data segments." 933169689Skan */ 934169689Skan icl_pdu_free(response); 935169689Skan return; 936169689Skan } 937169689Skan 938169689Skan /* 939169689Skan * We need to track this for security reasons - without it, malicious target 940169689Skan * could respond to SCSI READ without sending Data-In PDUs, which would result 941169689Skan * in read operation on the initiator side returning random kernel data. 942169689Skan */ 943169689Skan if (ntohl(bhsdi->bhsdi_buffer_offset) != io->io_received) { 944169689Skan ISCSI_SESSION_WARN(is, "data out of order; expected offset %zd, got %zd", 945169689Skan io->io_received, (size_t)ntohl(bhsdi->bhsdi_buffer_offset)); 946169689Skan icl_pdu_free(response); 947169689Skan iscsi_session_reconnect(is); 948169689Skan return; 949169689Skan } 950169689Skan 951169689Skan csio = &io->io_ccb->csio; 952169689Skan 953169689Skan if (io->io_received + data_segment_len > csio->dxfer_len) { 954169689Skan ISCSI_SESSION_WARN(is, "oversize data segment (%zd bytes " 955169689Skan "at offset %zd, buffer is %d)", 956169689Skan data_segment_len, io->io_received, csio->dxfer_len); 957169689Skan icl_pdu_free(response); 958169689Skan iscsi_session_reconnect(is); 959169689Skan return; 960169689Skan } 961169689Skan 962169689Skan icl_pdu_get_data(response, 0, csio->data_ptr + io->io_received, data_segment_len); 963169689Skan io->io_received += data_segment_len; 964169689Skan 965169689Skan /* 966169689Skan * XXX: Check DataSN. 967169689Skan * XXX: Check F. 968169689Skan */ 969169689Skan if ((bhsdi->bhsdi_flags & BHSDI_FLAGS_S) == 0) { 970169689Skan /* 971169689Skan * Nothing more to do. 972169689Skan */ 973169689Skan icl_pdu_free(response); 974169689Skan return; 975169689Skan } 976169689Skan 977169689Skan //ISCSI_SESSION_DEBUG(is, "got S flag; status 0x%x", bhsdi->bhsdi_status); 978169689Skan if (bhsdi->bhsdi_status == 0) { 979169689Skan io->io_ccb->ccb_h.status = CAM_REQ_CMP; 980169689Skan } else { 981169689Skan if ((io->io_ccb->ccb_h.status & CAM_DEV_QFRZN) == 0) { 982169689Skan xpt_freeze_devq(io->io_ccb->ccb_h.path, 1); 983169689Skan ISCSI_SESSION_DEBUG(is, "freezing devq"); 984169689Skan } 985169689Skan io->io_ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR | CAM_DEV_QFRZN; 986169689Skan csio->scsi_status = bhsdi->bhsdi_status; 987169689Skan } 988169689Skan 989169689Skan if ((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { 990169689Skan KASSERT(io->io_received <= csio->dxfer_len, 991169689Skan ("io->io_received > csio->dxfer_len")); 992169689Skan if (io->io_received < csio->dxfer_len) { 993169689Skan csio->resid = ntohl(bhsdi->bhsdi_residual_count); 994169689Skan if (csio->resid != csio->dxfer_len - io->io_received) { 995169689Skan ISCSI_SESSION_WARN(is, "underflow mismatch: " 996169689Skan "target indicates %d, we calculated %zd", 997169689Skan csio->resid, 998169689Skan csio->dxfer_len - io->io_received); 999169689Skan } 1000169689Skan csio->resid = csio->dxfer_len - io->io_received; 1001169689Skan } 1002169689Skan } 1003169689Skan 1004169689Skan xpt_done(io->io_ccb); 1005169689Skan iscsi_outstanding_remove(is, io); 1006169689Skan icl_pdu_free(response); 1007169689Skan} 1008169689Skan 1009169689Skanstatic void 1010169689Skaniscsi_pdu_handle_logout_response(struct icl_pdu *response) 1011169689Skan{ 1012169689Skan 1013169689Skan ISCSI_SESSION_DEBUG(PDU_SESSION(response), "logout response"); 1014169689Skan icl_pdu_free(response); 1015169689Skan} 1016169689Skan 1017169689Skanstatic void 1018169689Skaniscsi_pdu_handle_r2t(struct icl_pdu *response) 1019169689Skan{ 1020169689Skan struct icl_pdu *request; 1021169689Skan struct iscsi_session *is; 1022169689Skan struct iscsi_bhs_r2t *bhsr2t; 1023169689Skan struct iscsi_bhs_data_out *bhsdo; 1024169689Skan struct iscsi_outstanding *io; 1025169689Skan struct ccb_scsiio *csio; 1026169689Skan size_t off, len, total_len; 1027169689Skan int error; 1028169689Skan 1029169689Skan is = PDU_SESSION(response); 1030169689Skan 1031169689Skan bhsr2t = (struct iscsi_bhs_r2t *)response->ip_bhs; 1032169689Skan io = iscsi_outstanding_find(is, bhsr2t->bhsr2t_initiator_task_tag); 1033169689Skan if (io == NULL) { 1034169689Skan ISCSI_SESSION_WARN(is, "bad itt 0x%x; reconnecting", 1035169689Skan bhsr2t->bhsr2t_initiator_task_tag); 1036169689Skan icl_pdu_free(response); 1037169689Skan iscsi_session_reconnect(is); 1038169689Skan return; 1039169689Skan } 1040169689Skan 1041169689Skan csio = &io->io_ccb->csio; 1042169689Skan 1043169689Skan if ((csio->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_OUT) { 1044169689Skan ISCSI_SESSION_WARN(is, "received R2T for read command; reconnecting"); 1045169689Skan icl_pdu_free(response); 1046169689Skan iscsi_session_reconnect(is); 1047169689Skan return; 1048169689Skan } 1049169689Skan 1050169689Skan /* 1051169689Skan * XXX: Verify R2TSN. 1052169689Skan */ 1053169689Skan 1054169689Skan io->io_datasn = 0; 1055169689Skan 1056169689Skan off = ntohl(bhsr2t->bhsr2t_buffer_offset); 1057169689Skan if (off > csio->dxfer_len) { 1058169689Skan ISCSI_SESSION_WARN(is, "target requested invalid offset " 1059169689Skan "%zd, buffer is is %d; reconnecting", off, csio->dxfer_len); 1060169689Skan icl_pdu_free(response); 1061169689Skan iscsi_session_reconnect(is); 1062169689Skan return; 1063169689Skan } 1064169689Skan 1065169689Skan total_len = ntohl(bhsr2t->bhsr2t_desired_data_transfer_length); 1066169689Skan if (total_len == 0 || total_len > csio->dxfer_len) { 1067169689Skan ISCSI_SESSION_WARN(is, "target requested invalid length " 1068169689Skan "%zd, buffer is %d; reconnecting", total_len, csio->dxfer_len); 1069169689Skan icl_pdu_free(response); 1070169689Skan iscsi_session_reconnect(is); 1071169689Skan return; 1072169689Skan } 1073169689Skan 1074169689Skan //ISCSI_SESSION_DEBUG(is, "r2t; off %zd, len %zd", off, total_len); 1075169689Skan 1076169689Skan for (;;) { 1077169689Skan len = total_len; 1078169689Skan 1079169689Skan if (len > is->is_max_data_segment_length) 1080169689Skan len = is->is_max_data_segment_length; 1081169689Skan 1082169689Skan if (off + len > csio->dxfer_len) { 1083169689Skan ISCSI_SESSION_WARN(is, "target requested invalid " 1084169689Skan "length/offset %zd, buffer is %d; reconnecting", 1085169689Skan off + len, csio->dxfer_len); 1086169689Skan icl_pdu_free(response); 1087169689Skan iscsi_session_reconnect(is); 1088169689Skan return; 1089169689Skan } 1090169689Skan 1091169689Skan request = icl_pdu_new_bhs(response->ip_conn, M_NOWAIT); 1092169689Skan if (request == NULL) { 1093169689Skan icl_pdu_free(response); 1094169689Skan iscsi_session_reconnect(is); 1095169689Skan return; 1096169689Skan } 1097169689Skan 1098169689Skan bhsdo = (struct iscsi_bhs_data_out *)request->ip_bhs; 1099169689Skan bhsdo->bhsdo_opcode = ISCSI_BHS_OPCODE_SCSI_DATA_OUT; 1100169689Skan bhsdo->bhsdo_lun = bhsr2t->bhsr2t_lun; 1101169689Skan bhsdo->bhsdo_initiator_task_tag = 1102169689Skan bhsr2t->bhsr2t_initiator_task_tag; 1103169689Skan bhsdo->bhsdo_target_transfer_tag = 1104169689Skan bhsr2t->bhsr2t_target_transfer_tag; 1105169689Skan bhsdo->bhsdo_datasn = htonl(io->io_datasn++); 1106169689Skan bhsdo->bhsdo_buffer_offset = htonl(off); 1107169689Skan error = icl_pdu_append_data(request, csio->data_ptr + off, len, 1108169689Skan M_NOWAIT); 1109169689Skan if (error != 0) { 1110169689Skan ISCSI_SESSION_WARN(is, "failed to allocate memory; " 1111169689Skan "reconnecting"); 1112169689Skan icl_pdu_free(request); 1113169689Skan icl_pdu_free(response); 1114169689Skan iscsi_session_reconnect(is); 1115169689Skan return; 1116169689Skan } 1117169689Skan 1118169689Skan off += len; 1119169689Skan total_len -= len; 1120169689Skan 1121169689Skan if (total_len == 0) { 1122169689Skan bhsdo->bhsdo_flags |= BHSDO_FLAGS_F; 1123169689Skan //ISCSI_SESSION_DEBUG(is, "setting F, off %zd", off); 1124169689Skan } else { 1125169689Skan //ISCSI_SESSION_DEBUG(is, "not finished, off %zd", off); 1126169689Skan } 1127169689Skan 1128169689Skan iscsi_pdu_queue_locked(request); 1129169689Skan 1130169689Skan if (total_len == 0) 1131169689Skan break; 1132169689Skan } 1133169689Skan 1134169689Skan icl_pdu_free(response); 1135169689Skan} 1136169689Skan 1137169689Skanstatic void 1138169689Skaniscsi_pdu_handle_async_message(struct icl_pdu *response) 1139169689Skan{ 1140169689Skan struct iscsi_bhs_asynchronous_message *bhsam; 1141169689Skan struct iscsi_session *is; 1142169689Skan 1143169689Skan is = PDU_SESSION(response); 1144169689Skan bhsam = (struct iscsi_bhs_asynchronous_message *)response->ip_bhs; 1145169689Skan switch (bhsam->bhsam_async_event) { 1146169689Skan case BHSAM_EVENT_TARGET_REQUESTS_LOGOUT: 1147169689Skan ISCSI_SESSION_WARN(is, "target requests logout; removing session"); 1148169689Skan iscsi_session_logout(is); 1149169689Skan iscsi_session_terminate(is); 1150169689Skan break; 1151169689Skan case BHSAM_EVENT_TARGET_TERMINATES_CONNECTION: 1152169689Skan ISCSI_SESSION_WARN(is, "target indicates it will drop drop the connection"); 1153169689Skan break; 1154169689Skan case BHSAM_EVENT_TARGET_TERMINATES_SESSION: 1155169689Skan ISCSI_SESSION_WARN(is, "target indicates it will drop drop the session"); 1156169689Skan break; 1157169689Skan default: 1158169689Skan /* 1159169689Skan * XXX: Technically, we're obligated to also handle 1160169689Skan * parameter renegotiation. 1161169689Skan */ 1162169689Skan ISCSI_SESSION_WARN(is, "ignoring AsyncEvent %d", bhsam->bhsam_async_event); 1163169689Skan break; 1164169689Skan } 1165169689Skan 1166169689Skan icl_pdu_free(response); 1167169689Skan} 1168169689Skan 1169169689Skanstatic void 1170169689Skaniscsi_pdu_handle_reject(struct icl_pdu *response) 1171169689Skan{ 1172169689Skan struct iscsi_bhs_reject *bhsr; 1173169689Skan struct iscsi_session *is; 1174169689Skan 1175169689Skan is = PDU_SESSION(response); 1176169689Skan bhsr = (struct iscsi_bhs_reject *)response->ip_bhs; 1177169689Skan ISCSI_SESSION_WARN(is, "received Reject PDU, reason 0x%x; protocol error?", 1178169689Skan bhsr->bhsr_reason); 1179169689Skan 1180169689Skan icl_pdu_free(response); 1181169689Skan} 1182169689Skan 1183169689Skanstatic int 1184169689Skaniscsi_ioctl_daemon_wait(struct iscsi_softc *sc, 1185169689Skan struct iscsi_daemon_request *request) 1186169689Skan{ 1187169689Skan struct iscsi_session *is; 1188169689Skan int error; 1189169689Skan 1190169689Skan sx_slock(&sc->sc_lock); 1191169689Skan for (;;) { 1192169689Skan TAILQ_FOREACH(is, &sc->sc_sessions, is_next) { 1193169689Skan if (is->is_waiting_for_iscsid) 1194169689Skan break; 1195169689Skan } 1196169689Skan 1197169689Skan if (is == NULL) { 1198169689Skan /* 1199169689Skan * No session requires attention from iscsid(8); wait. 1200169689Skan */ 1201169689Skan error = cv_wait_sig(&sc->sc_cv, &sc->sc_lock); 1202169689Skan if (error != 0) { 1203169689Skan sx_sunlock(&sc->sc_lock); 1204169689Skan return (error); 1205169689Skan } 1206169689Skan continue; 1207169689Skan } 1208169689Skan 1209169689Skan ISCSI_SESSION_LOCK(is); 1210169689Skan is->is_waiting_for_iscsid = false; 1211169689Skan is->is_login_phase = true; 1212169689Skan is->is_reason[0] = '\0'; 1213169689Skan ISCSI_SESSION_UNLOCK(is); 1214169689Skan 1215169689Skan request->idr_session_id = is->is_id; 1216169689Skan memcpy(&request->idr_conf, &is->is_conf, 1217169689Skan sizeof(request->idr_conf)); 1218169689Skan 1219169689Skan sx_sunlock(&sc->sc_lock); 1220169689Skan return (0); 1221169689Skan } 1222169689Skan} 1223169689Skan 1224169689Skanstatic int 1225169689Skaniscsi_ioctl_daemon_handoff(struct iscsi_softc *sc, 1226169689Skan struct iscsi_daemon_handoff *handoff) 1227169689Skan{ 1228169689Skan struct iscsi_session *is; 1229169689Skan int error; 1230169689Skan 1231169689Skan sx_slock(&sc->sc_lock); 1232169689Skan 1233169689Skan /* 1234169689Skan * Find the session to hand off socket to. 1235169689Skan */ 1236169689Skan TAILQ_FOREACH(is, &sc->sc_sessions, is_next) { 1237169689Skan if (is->is_id == handoff->idh_session_id) 1238169689Skan break; 1239169689Skan } 1240169689Skan if (is == NULL) { 1241169689Skan sx_sunlock(&sc->sc_lock); 1242169689Skan return (ESRCH); 1243169689Skan } 1244169689Skan ISCSI_SESSION_LOCK(is); 1245169689Skan if (is->is_conf.isc_discovery || is->is_terminating) { 1246169689Skan ISCSI_SESSION_UNLOCK(is); 1247169689Skan sx_sunlock(&sc->sc_lock); 1248169689Skan return (EINVAL); 1249169689Skan } 1250169689Skan if (is->is_connected) { 1251169689Skan /* 1252169689Skan * This might have happened because another iscsid(8) 1253169689Skan * instance handed off the connection in the meantime. 1254169689Skan * Just return. 1255169689Skan */ 1256169689Skan ISCSI_SESSION_WARN(is, "handoff on already connected " 1257169689Skan "session"); 1258169689Skan ISCSI_SESSION_UNLOCK(is); 1259169689Skan sx_sunlock(&sc->sc_lock); 1260169689Skan return (EBUSY); 1261169689Skan } 1262169689Skan 1263169689Skan strlcpy(is->is_target_alias, handoff->idh_target_alias, 1264169689Skan sizeof(is->is_target_alias)); 1265169689Skan memcpy(is->is_isid, handoff->idh_isid, sizeof(is->is_isid)); 1266169689Skan is->is_statsn = handoff->idh_statsn; 1267169689Skan is->is_initial_r2t = handoff->idh_initial_r2t; 1268169689Skan is->is_immediate_data = handoff->idh_immediate_data; 1269169689Skan is->is_max_data_segment_length = handoff->idh_max_data_segment_length; 1270169689Skan is->is_max_burst_length = handoff->idh_max_burst_length; 1271169689Skan is->is_first_burst_length = handoff->idh_first_burst_length; 1272169689Skan 1273169689Skan if (handoff->idh_header_digest == ISCSI_DIGEST_CRC32C) 1274169689Skan is->is_conn->ic_header_crc32c = true; 1275169689Skan else 1276169689Skan is->is_conn->ic_header_crc32c = false; 1277169689Skan if (handoff->idh_data_digest == ISCSI_DIGEST_CRC32C) 1278169689Skan is->is_conn->ic_data_crc32c = true; 1279169689Skan else 1280169689Skan is->is_conn->ic_data_crc32c = false; 1281169689Skan 1282169689Skan is->is_cmdsn = 0; 1283169689Skan is->is_expcmdsn = 0; 1284169689Skan is->is_maxcmdsn = 0; 1285169689Skan is->is_waiting_for_iscsid = false; 1286169689Skan is->is_login_phase = false; 1287169689Skan is->is_timeout = 0; 1288169689Skan is->is_connected = true; 1289169689Skan is->is_reason[0] = '\0'; 1290169689Skan 1291169689Skan ISCSI_SESSION_UNLOCK(is); 1292169689Skan 1293169689Skan#ifndef ICL_KERNEL_PROXY 1294169689Skan error = icl_conn_handoff(is->is_conn, handoff->idh_socket); 1295169689Skan if (error != 0) { 1296169689Skan sx_sunlock(&sc->sc_lock); 1297169689Skan iscsi_session_terminate(is); 1298169689Skan return (error); 1299169689Skan } 1300169689Skan#endif 1301169689Skan 1302169689Skan sx_sunlock(&sc->sc_lock); 1303169689Skan 1304169689Skan if (is->is_sim != NULL) { 1305169689Skan /* 1306169689Skan * When reconnecting, there already is SIM allocated for the session. 1307169689Skan */ 1308169689Skan KASSERT(is->is_simq_frozen, ("reconnect without frozen simq")); 1309169689Skan ISCSI_SESSION_LOCK(is); 1310169689Skan ISCSI_SESSION_DEBUG(is, "releasing"); 1311169689Skan xpt_release_simq(is->is_sim, 1); 1312169689Skan is->is_simq_frozen = false; 1313169689Skan ISCSI_SESSION_UNLOCK(is); 1314169689Skan 1315169689Skan } else { 1316169689Skan ISCSI_SESSION_LOCK(is); 1317169689Skan is->is_devq = cam_simq_alloc(maxtags); 1318169689Skan if (is->is_devq == NULL) { 1319169689Skan ISCSI_SESSION_WARN(is, "failed to allocate simq"); 1320169689Skan iscsi_session_terminate(is); 1321169689Skan return (ENOMEM); 1322169689Skan } 1323169689Skan 1324169689Skan is->is_sim = cam_sim_alloc(iscsi_action, iscsi_poll, "iscsi", 1325169689Skan is, is->is_id /* unit */, &is->is_lock, 1326169689Skan maxtags, maxtags, is->is_devq); 1327169689Skan if (is->is_sim == NULL) { 1328169689Skan ISCSI_SESSION_UNLOCK(is); 1329169689Skan ISCSI_SESSION_WARN(is, "failed to allocate SIM"); 1330169689Skan cam_simq_free(is->is_devq); 1331169689Skan iscsi_session_terminate(is); 1332169689Skan return (ENOMEM); 1333169689Skan } 1334169689Skan 1335169689Skan error = xpt_bus_register(is->is_sim, NULL, 0); 1336169689Skan if (error != 0) { 1337169689Skan ISCSI_SESSION_UNLOCK(is); 1338169689Skan ISCSI_SESSION_WARN(is, "failed to register bus"); 1339169689Skan iscsi_session_terminate(is); 1340169689Skan return (ENOMEM); 1341169689Skan } 1342169689Skan 1343169689Skan error = xpt_create_path(&is->is_path, /*periph*/NULL, 1344169689Skan cam_sim_path(is->is_sim), CAM_TARGET_WILDCARD, 1345169689Skan CAM_LUN_WILDCARD); 1346169689Skan if (error != CAM_REQ_CMP) { 1347169689Skan ISCSI_SESSION_UNLOCK(is); 1348169689Skan ISCSI_SESSION_WARN(is, "failed to create path"); 1349169689Skan iscsi_session_terminate(is); 1350169689Skan return (ENOMEM); 1351169689Skan } 1352169689Skan ISCSI_SESSION_UNLOCK(is); 1353169689Skan } 1354169689Skan 1355169689Skan return (0); 1356169689Skan} 1357169689Skan 1358169689Skanstatic int 1359169689Skaniscsi_ioctl_daemon_fail(struct iscsi_softc *sc, 1360169689Skan struct iscsi_daemon_fail *fail) 1361169689Skan{ 1362169689Skan struct iscsi_session *is; 1363169689Skan 1364169689Skan sx_slock(&sc->sc_lock); 1365169689Skan 1366169689Skan TAILQ_FOREACH(is, &sc->sc_sessions, is_next) { 1367169689Skan if (is->is_id == fail->idf_session_id) 1368169689Skan break; 1369169689Skan } 1370169689Skan if (is == NULL) { 1371169689Skan sx_sunlock(&sc->sc_lock); 1372169689Skan return (ESRCH); 1373169689Skan } 1374169689Skan ISCSI_SESSION_LOCK(is); 1375169689Skan ISCSI_SESSION_DEBUG(is, "iscsid(8) failed: %s", 1376169689Skan fail->idf_reason); 1377169689Skan strlcpy(is->is_reason, fail->idf_reason, sizeof(is->is_reason)); 1378169689Skan //is->is_waiting_for_iscsid = false; 1379169689Skan //is->is_login_phase = true; 1380169689Skan //iscsi_session_reconnect(is); 1381169689Skan ISCSI_SESSION_UNLOCK(is); 1382169689Skan sx_sunlock(&sc->sc_lock); 1383169689Skan 1384169689Skan return (0); 1385169689Skan} 1386169689Skan 1387169689Skan#ifdef ICL_KERNEL_PROXY 1388169689Skanstatic int 1389169689Skaniscsi_ioctl_daemon_connect(struct iscsi_softc *sc, 1390169689Skan struct iscsi_daemon_connect *idc) 1391169689Skan{ 1392169689Skan struct iscsi_session *is; 1393169689Skan struct sockaddr *from_sa, *to_sa; 1394169689Skan int error; 1395169689Skan 1396169689Skan sx_slock(&sc->sc_lock); 1397169689Skan TAILQ_FOREACH(is, &sc->sc_sessions, is_next) { 1398169689Skan if (is->is_id == idc->idc_session_id) 1399169689Skan break; 1400169689Skan } 1401169689Skan if (is == NULL) { 1402169689Skan sx_sunlock(&sc->sc_lock); 1403169689Skan return (ESRCH); 1404169689Skan } 1405169689Skan sx_sunlock(&sc->sc_lock); 1406169689Skan 1407169689Skan if (idc->idc_from_addrlen > 0) { 1408169689Skan error = getsockaddr(&from_sa, (void *)idc->idc_from_addr, idc->idc_from_addrlen); 1409169689Skan if (error != 0) 1410169689Skan return (error); 1411169689Skan } else { 1412169689Skan from_sa = NULL; 1413169689Skan } 1414169689Skan error = getsockaddr(&to_sa, (void *)idc->idc_to_addr, idc->idc_to_addrlen); 1415169689Skan if (error != 0) { 1416169689Skan free(from_sa, M_SONAME); 1417169689Skan return (error); 1418169689Skan } 1419169689Skan 1420169689Skan ISCSI_SESSION_LOCK(is); 1421169689Skan is->is_waiting_for_iscsid = false; 1422169689Skan is->is_login_phase = true; 1423169689Skan is->is_timeout = 0; 1424169689Skan ISCSI_SESSION_UNLOCK(is); 1425169689Skan 1426169689Skan error = icl_conn_connect(is->is_conn, idc->idc_iser, idc->idc_domain, 1427169689Skan idc->idc_socktype, idc->idc_protocol, from_sa, to_sa); 1428169689Skan free(from_sa, M_SONAME); 1429169689Skan free(to_sa, M_SONAME); 1430169689Skan 1431169689Skan /* 1432169689Skan * Digests are always disabled during login phase. 1433169689Skan */ 1434169689Skan is->is_conn->ic_header_crc32c = false; 1435169689Skan is->is_conn->ic_data_crc32c = false; 1436169689Skan 1437169689Skan return (error); 1438169689Skan} 1439169689Skan 1440169689Skanstatic int 1441169689Skaniscsi_ioctl_daemon_send(struct iscsi_softc *sc, 1442169689Skan struct iscsi_daemon_send *ids) 1443169689Skan{ 1444169689Skan struct iscsi_session *is; 1445169689Skan struct icl_pdu *ip; 1446169689Skan size_t datalen; 1447169689Skan void *data; 1448169689Skan int error; 1449169689Skan 1450169689Skan sx_slock(&sc->sc_lock); 1451169689Skan TAILQ_FOREACH(is, &sc->sc_sessions, is_next) { 1452169689Skan if (is->is_id == ids->ids_session_id) 1453169689Skan break; 1454169689Skan } 1455169689Skan if (is == NULL) { 1456169689Skan sx_sunlock(&sc->sc_lock); 1457169689Skan return (ESRCH); 1458169689Skan } 1459169689Skan sx_sunlock(&sc->sc_lock); 1460169689Skan 1461169689Skan if (is->is_login_phase == false) 1462169689Skan return (EBUSY); 1463169689Skan 1464169689Skan if (is->is_terminating || is->is_reconnecting) 1465169689Skan return (EIO); 1466169689Skan 1467169689Skan datalen = ids->ids_data_segment_len; 1468169689Skan if (datalen > ISCSI_MAX_DATA_SEGMENT_LENGTH) 1469169689Skan return (EINVAL); 1470169689Skan if (datalen > 0) { 1471169689Skan data = malloc(datalen, M_ISCSI, M_WAITOK); 1472169689Skan error = copyin(ids->ids_data_segment, data, datalen); 1473169689Skan if (error != 0) { 1474169689Skan free(data, M_ISCSI); 1475169689Skan return (error); 1476169689Skan } 1477169689Skan } 1478169689Skan 1479169689Skan ip = icl_pdu_new_bhs(is->is_conn, M_WAITOK); 1480169689Skan memcpy(ip->ip_bhs, ids->ids_bhs, sizeof(*ip->ip_bhs)); 1481169689Skan if (datalen > 0) { 1482169689Skan error = icl_pdu_append_data(ip, data, datalen, M_WAITOK); 1483169689Skan KASSERT(error == 0, ("icl_pdu_append_data(..., M_WAITOK) failed")); 1484169689Skan free(data, M_ISCSI); 1485169689Skan } 1486169689Skan icl_pdu_queue(ip); 1487169689Skan 1488169689Skan return (0); 1489169689Skan} 1490169689Skan 1491169689Skanstatic int 1492169689Skaniscsi_ioctl_daemon_receive(struct iscsi_softc *sc, 1493169689Skan struct iscsi_daemon_receive *idr) 1494169689Skan{ 1495169689Skan struct iscsi_session *is; 1496169689Skan struct icl_pdu *ip; 1497169689Skan void *data; 1498169689Skan 1499169689Skan sx_slock(&sc->sc_lock); 1500169689Skan TAILQ_FOREACH(is, &sc->sc_sessions, is_next) { 1501169689Skan if (is->is_id == idr->idr_session_id) 1502169689Skan break; 1503169689Skan } 1504169689Skan if (is == NULL) { 1505169689Skan sx_sunlock(&sc->sc_lock); 1506169689Skan return (ESRCH); 1507169689Skan } 1508169689Skan sx_sunlock(&sc->sc_lock); 1509169689Skan 1510169689Skan if (is->is_login_phase == false) 1511169689Skan return (EBUSY); 1512169689Skan 1513169689Skan ISCSI_SESSION_LOCK(is); 1514169689Skan while (is->is_login_pdu == NULL && 1515169689Skan is->is_terminating == false && 1516169689Skan is->is_reconnecting == false) 1517169689Skan cv_wait(&is->is_login_cv, &is->is_lock); 1518169689Skan if (is->is_terminating || is->is_reconnecting) { 1519169689Skan ISCSI_SESSION_UNLOCK(is); 1520169689Skan return (EIO); 1521169689Skan } 1522169689Skan ip = is->is_login_pdu; 1523169689Skan is->is_login_pdu = NULL; 1524169689Skan ISCSI_SESSION_UNLOCK(is); 1525169689Skan 1526169689Skan if (ip->ip_data_len > idr->idr_data_segment_len) { 1527169689Skan icl_pdu_free(ip); 1528169689Skan return (EMSGSIZE); 1529169689Skan } 1530169689Skan 1531169689Skan copyout(ip->ip_bhs, idr->idr_bhs, sizeof(*ip->ip_bhs)); 1532169689Skan if (ip->ip_data_len > 0) { 1533169689Skan data = malloc(ip->ip_data_len, M_ISCSI, M_WAITOK); 1534169689Skan icl_pdu_get_data(ip, 0, data, ip->ip_data_len); 1535169689Skan copyout(data, idr->idr_data_segment, ip->ip_data_len); 1536169689Skan free(data, M_ISCSI); 1537169689Skan } 1538169689Skan 1539169689Skan icl_pdu_free(ip); 1540169689Skan 1541169689Skan return (0); 1542169689Skan} 1543169689Skan 1544169689Skanstatic int 1545169689Skaniscsi_ioctl_daemon_close(struct iscsi_softc *sc, 1546169689Skan struct iscsi_daemon_close *idc) 1547169689Skan{ 1548169689Skan struct iscsi_session *is; 1549169689Skan 1550169689Skan sx_slock(&sc->sc_lock); 1551169689Skan TAILQ_FOREACH(is, &sc->sc_sessions, is_next) { 1552169689Skan if (is->is_id == idc->idc_session_id) 1553169689Skan break; 1554169689Skan } 1555169689Skan if (is == NULL) { 1556169689Skan sx_sunlock(&sc->sc_lock); 1557169689Skan return (ESRCH); 1558169689Skan } 1559169689Skan sx_sunlock(&sc->sc_lock); 1560169689Skan 1561169689Skan iscsi_session_reconnect(is); 1562169689Skan 1563169689Skan return (0); 1564169689Skan} 1565169689Skan#endif /* ICL_KERNEL_PROXY */ 1566169689Skan 1567169689Skanstatic void 1568169689Skaniscsi_sanitize_session_conf(struct iscsi_session_conf *isc) 1569169689Skan{ 1570169689Skan /* 1571169689Skan * Just make sure all the fields are null-terminated. 1572169689Skan * 1573169689Skan * XXX: This is not particularly secure. We should 1574169689Skan * create our own conf and then copy in relevant 1575169689Skan * fields. 1576169689Skan */ 1577169689Skan isc->isc_initiator[ISCSI_NAME_LEN - 1] = '\0'; 1578169689Skan isc->isc_initiator_addr[ISCSI_ADDR_LEN - 1] = '\0'; 1579169689Skan isc->isc_initiator_alias[ISCSI_ALIAS_LEN - 1] = '\0'; 1580169689Skan isc->isc_target[ISCSI_NAME_LEN - 1] = '\0'; 1581169689Skan isc->isc_target_addr[ISCSI_ADDR_LEN - 1] = '\0'; 1582169689Skan isc->isc_user[ISCSI_NAME_LEN - 1] = '\0'; 1583169689Skan isc->isc_secret[ISCSI_SECRET_LEN - 1] = '\0'; 1584169689Skan isc->isc_mutual_user[ISCSI_NAME_LEN - 1] = '\0'; 1585169689Skan isc->isc_mutual_secret[ISCSI_SECRET_LEN - 1] = '\0'; 1586169689Skan} 1587169689Skan 1588169689Skanstatic int 1589169689Skaniscsi_ioctl_session_add(struct iscsi_softc *sc, struct iscsi_session_add *isa) 1590169689Skan{ 1591169689Skan struct iscsi_session *is; 1592169689Skan const struct iscsi_session *is2; 1593169689Skan int error; 1594169689Skan 1595169689Skan iscsi_sanitize_session_conf(&isa->isa_conf); 1596169689Skan 1597169689Skan is = malloc(sizeof(*is), M_ISCSI, M_ZERO | M_WAITOK); 1598169689Skan memcpy(&is->is_conf, &isa->isa_conf, sizeof(is->is_conf)); 1599169689Skan 1600169689Skan if (is->is_conf.isc_initiator[0] == '\0' || 1601169689Skan is->is_conf.isc_target_addr[0] == '\0') { 1602169689Skan free(is, M_ISCSI); 1603169689Skan return (EINVAL); 1604169689Skan } 1605169689Skan 1606169689Skan if ((is->is_conf.isc_discovery != 0 && is->is_conf.isc_target[0] != 0) || 1607169689Skan (is->is_conf.isc_discovery == 0 && is->is_conf.isc_target[0] == 0)) { 1608169689Skan free(is, M_ISCSI); 1609169689Skan return (EINVAL); 1610169689Skan } 1611169689Skan 1612169689Skan sx_xlock(&sc->sc_lock); 1613169689Skan 1614169689Skan /* 1615169689Skan * Prevent duplicates. 1616169689Skan */ 1617169689Skan TAILQ_FOREACH(is2, &sc->sc_sessions, is_next) { 1618169689Skan if (!!is->is_conf.isc_discovery != 1619169689Skan !!is2->is_conf.isc_discovery) 1620169689Skan continue; 1621169689Skan 1622169689Skan if (strcmp(is->is_conf.isc_target_addr, 1623169689Skan is2->is_conf.isc_target_addr) != 0) 1624169689Skan continue; 1625169689Skan 1626169689Skan if (is->is_conf.isc_discovery == 0 && 1627169689Skan strcmp(is->is_conf.isc_target, 1628169689Skan is2->is_conf.isc_target) != 0) 1629169689Skan continue; 1630169689Skan 1631169689Skan sx_xunlock(&sc->sc_lock); 1632169689Skan free(is, M_ISCSI); 1633169689Skan return (EBUSY); 1634169689Skan } 1635169689Skan 1636169689Skan is->is_conn = icl_conn_new(&is->is_lock); 1637169689Skan is->is_conn->ic_receive = iscsi_receive_callback; 1638169689Skan is->is_conn->ic_error = iscsi_error_callback; 1639169689Skan is->is_conn->ic_prv0 = is; 1640169689Skan TAILQ_INIT(&is->is_outstanding); 1641169689Skan TAILQ_INIT(&is->is_postponed); 1642169689Skan mtx_init(&is->is_lock, "iscsi_lock", NULL, MTX_DEF); 1643169689Skan cv_init(&is->is_maintenance_cv, "iscsi_mt"); 1644169689Skan#ifdef ICL_KERNEL_PROXY 1645169689Skan cv_init(&is->is_login_cv, "iscsi_login"); 1646169689Skan#endif 1647169689Skan 1648169689Skan is->is_softc = sc; 1649169689Skan sc->sc_last_session_id++; 1650169689Skan is->is_id = sc->sc_last_session_id; 1651169689Skan callout_init(&is->is_callout, 1); 1652169689Skan callout_reset(&is->is_callout, 1 * hz, iscsi_callout, is); 1653169689Skan TAILQ_INSERT_TAIL(&sc->sc_sessions, is, is_next); 1654169689Skan 1655169689Skan error = kthread_add(iscsi_maintenance_thread, is, NULL, NULL, 0, 0, "iscsimt"); 1656169689Skan if (error != 0) { 1657169689Skan ISCSI_SESSION_WARN(is, "kthread_add(9) failed with error %d", error); 1658169689Skan return (error); 1659169689Skan } 1660169689Skan 1661169689Skan /* 1662169689Skan * Trigger immediate reconnection. 1663169689Skan */ 1664169689Skan is->is_waiting_for_iscsid = true; 1665169689Skan strlcpy(is->is_reason, "Waiting for iscsid(8)", sizeof(is->is_reason)); 1666169689Skan cv_signal(&sc->sc_cv); 1667169689Skan 1668169689Skan sx_xunlock(&sc->sc_lock); 1669169689Skan 1670169689Skan return (0); 1671169689Skan} 1672169689Skan 1673169689Skanstatic bool 1674169689Skaniscsi_session_conf_matches(unsigned int id1, const struct iscsi_session_conf *c1, 1675169689Skan unsigned int id2, const struct iscsi_session_conf *c2) 1676169689Skan{ 1677169689Skan if (id2 == 0 && c2->isc_target[0] == '\0' && 1678169689Skan c2->isc_target_addr[0] == '\0') 1679169689Skan return (true); 1680169689Skan if (id2 != 0 && id2 == id1) 1681169689Skan return (true); 1682169689Skan if (c2->isc_target[0] != '\0' && 1683169689Skan strcmp(c1->isc_target, c2->isc_target) == 0) 1684169689Skan return (true); 1685169689Skan if (c2->isc_target_addr[0] != '\0' && 1686169689Skan strcmp(c1->isc_target_addr, c2->isc_target_addr) == 0) 1687169689Skan return (true); 1688169689Skan return (false); 1689169689Skan} 1690169689Skan 1691169689Skanstatic int 1692169689Skaniscsi_ioctl_session_remove(struct iscsi_softc *sc, 1693169689Skan struct iscsi_session_remove *isr) 1694169689Skan{ 1695169689Skan struct iscsi_session *is, *tmp; 1696169689Skan bool found = false; 1697169689Skan 1698169689Skan iscsi_sanitize_session_conf(&isr->isr_conf); 1699169689Skan 1700169689Skan sx_xlock(&sc->sc_lock); 1701169689Skan TAILQ_FOREACH_SAFE(is, &sc->sc_sessions, is_next, tmp) { 1702169689Skan ISCSI_SESSION_LOCK(is); 1703169689Skan if (iscsi_session_conf_matches(is->is_id, &is->is_conf, 1704169689Skan isr->isr_session_id, &isr->isr_conf)) { 1705169689Skan found = true; 1706169689Skan iscsi_session_logout(is); 1707169689Skan iscsi_session_terminate(is); 1708169689Skan } 1709169689Skan ISCSI_SESSION_UNLOCK(is); 1710169689Skan } 1711169689Skan sx_xunlock(&sc->sc_lock); 1712169689Skan 1713169689Skan if (!found) 1714169689Skan return (ESRCH); 1715169689Skan 1716169689Skan return (0); 1717169689Skan} 1718169689Skan 1719169689Skanstatic int 1720169689Skaniscsi_ioctl_session_list(struct iscsi_softc *sc, struct iscsi_session_list *isl) 1721169689Skan{ 1722169689Skan int error; 1723169689Skan unsigned int i = 0; 1724169689Skan struct iscsi_session *is; 1725169689Skan struct iscsi_session_state iss; 1726169689Skan 1727169689Skan sx_slock(&sc->sc_lock); 1728169689Skan TAILQ_FOREACH(is, &sc->sc_sessions, is_next) { 1729169689Skan if (i >= isl->isl_nentries) { 1730169689Skan sx_sunlock(&sc->sc_lock); 1731169689Skan return (EMSGSIZE); 1732169689Skan } 1733169689Skan memset(&iss, 0, sizeof(iss)); 1734169689Skan memcpy(&iss.iss_conf, &is->is_conf, sizeof(iss.iss_conf)); 1735169689Skan iss.iss_id = is->is_id; 1736169689Skan strlcpy(iss.iss_target_alias, is->is_target_alias, sizeof(iss.iss_target_alias)); 1737169689Skan strlcpy(iss.iss_reason, is->is_reason, sizeof(iss.iss_reason)); 1738169689Skan 1739169689Skan if (is->is_conn->ic_header_crc32c) 1740169689Skan iss.iss_header_digest = ISCSI_DIGEST_CRC32C; 1741169689Skan else 1742169689Skan iss.iss_header_digest = ISCSI_DIGEST_NONE; 1743169689Skan 1744169689Skan if (is->is_conn->ic_data_crc32c) 1745169689Skan iss.iss_data_digest = ISCSI_DIGEST_CRC32C; 1746169689Skan else 1747169689Skan iss.iss_data_digest = ISCSI_DIGEST_NONE; 1748169689Skan 1749169689Skan iss.iss_max_data_segment_length = is->is_max_data_segment_length; 1750169689Skan iss.iss_immediate_data = is->is_immediate_data; 1751169689Skan iss.iss_connected = is->is_connected; 1752169689Skan 1753169689Skan error = copyout(&iss, isl->isl_pstates + i, sizeof(iss)); 1754169689Skan if (error != 0) { 1755169689Skan sx_sunlock(&sc->sc_lock); 1756169689Skan return (error); 1757169689Skan } 1758169689Skan i++; 1759169689Skan } 1760169689Skan sx_sunlock(&sc->sc_lock); 1761169689Skan 1762169689Skan isl->isl_nentries = i; 1763169689Skan 1764169689Skan return (0); 1765169689Skan} 1766169689Skan 1767169689Skanstatic int 1768169689Skaniscsi_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int mode, 1769169689Skan struct thread *td) 1770169689Skan{ 1771169689Skan struct iscsi_softc *sc; 1772169689Skan 1773169689Skan sc = dev->si_drv1; 1774169689Skan 1775169689Skan switch (cmd) { 1776169689Skan case ISCSIDWAIT: 1777169689Skan return (iscsi_ioctl_daemon_wait(sc, 1778169689Skan (struct iscsi_daemon_request *)arg)); 1779169689Skan case ISCSIDHANDOFF: 1780169689Skan return (iscsi_ioctl_daemon_handoff(sc, 1781169689Skan (struct iscsi_daemon_handoff *)arg)); 1782169689Skan case ISCSIDFAIL: 1783169689Skan return (iscsi_ioctl_daemon_fail(sc, 1784169689Skan (struct iscsi_daemon_fail *)arg)); 1785169689Skan#ifdef ICL_KERNEL_PROXY 1786169689Skan case ISCSIDCONNECT: 1787169689Skan return (iscsi_ioctl_daemon_connect(sc, 1788169689Skan (struct iscsi_daemon_connect *)arg)); 1789169689Skan case ISCSIDSEND: 1790169689Skan return (iscsi_ioctl_daemon_send(sc, 1791169689Skan (struct iscsi_daemon_send *)arg)); 1792169689Skan case ISCSIDRECEIVE: 1793169689Skan return (iscsi_ioctl_daemon_receive(sc, 1794169689Skan (struct iscsi_daemon_receive *)arg)); 1795169689Skan case ISCSIDCLOSE: 1796169689Skan return (iscsi_ioctl_daemon_close(sc, 1797169689Skan (struct iscsi_daemon_close *)arg)); 1798169689Skan#endif /* ICL_KERNEL_PROXY */ 1799169689Skan case ISCSISADD: 1800169689Skan return (iscsi_ioctl_session_add(sc, 1801169689Skan (struct iscsi_session_add *)arg)); 1802169689Skan case ISCSISREMOVE: 1803169689Skan return (iscsi_ioctl_session_remove(sc, 1804169689Skan (struct iscsi_session_remove *)arg)); 1805169689Skan case ISCSISLIST: 1806169689Skan return (iscsi_ioctl_session_list(sc, 1807169689Skan (struct iscsi_session_list *)arg)); 1808169689Skan default: 1809169689Skan return (EINVAL); 1810169689Skan } 1811169689Skan} 1812169689Skan 1813169689Skanstatic uint64_t 1814169689Skaniscsi_encode_lun(uint32_t lun) 1815169689Skan{ 1816169689Skan uint8_t encoded[8]; 1817169689Skan uint64_t result; 1818169689Skan 1819169689Skan memset(encoded, 0, sizeof(encoded)); 1820169689Skan 1821169689Skan if (lun < 256) { 1822169689Skan /* 1823169689Skan * Peripheral device addressing. 1824169689Skan */ 1825169689Skan encoded[1] = lun; 1826169689Skan } else if (lun < 16384) { 1827169689Skan /* 1828169689Skan * Flat space addressing. 1829169689Skan */ 1830169689Skan encoded[0] = 0x40; 1831169689Skan encoded[0] |= (lun >> 8) & 0x3f; 1832169689Skan encoded[1] = lun & 0xff; 1833169689Skan } else { 1834169689Skan /* 1835169689Skan * Extended flat space addressing. 1836169689Skan */ 1837169689Skan encoded[0] = 0xd2; 1838169689Skan encoded[1] = lun >> 16; 1839169689Skan encoded[2] = lun >> 8; 1840169689Skan encoded[3] = lun; 1841169689Skan } 1842169689Skan 1843169689Skan memcpy(&result, encoded, sizeof(result)); 1844169689Skan return (result); 1845169689Skan} 1846169689Skan 1847169689Skanstatic struct iscsi_outstanding * 1848169689Skaniscsi_outstanding_find(struct iscsi_session *is, uint32_t initiator_task_tag) 1849169689Skan{ 1850169689Skan struct iscsi_outstanding *io; 1851169689Skan 1852169689Skan ISCSI_SESSION_LOCK_ASSERT(is); 1853169689Skan 1854169689Skan TAILQ_FOREACH(io, &is->is_outstanding, io_next) { 1855169689Skan if (io->io_initiator_task_tag == initiator_task_tag) 1856169689Skan return (io); 1857169689Skan } 1858169689Skan return (NULL); 1859169689Skan} 1860169689Skan 1861169689Skanstatic int 1862169689Skaniscsi_outstanding_add(struct iscsi_session *is, 1863169689Skan uint32_t initiator_task_tag, union ccb *ccb) 1864169689Skan{ 1865169689Skan struct iscsi_outstanding *io; 1866169689Skan 1867169689Skan ISCSI_SESSION_LOCK_ASSERT(is); 1868169689Skan 1869169689Skan KASSERT(iscsi_outstanding_find(is, initiator_task_tag) == NULL, 1870169689Skan ("initiator_task_tag 0x%x already added", initiator_task_tag)); 1871169689Skan 1872169689Skan io = uma_zalloc(iscsi_outstanding_zone, M_NOWAIT | M_ZERO); 1873169689Skan if (io == NULL) { 1874169689Skan ISCSI_SESSION_WARN(is, "failed to allocate %zd bytes", sizeof(*io)); 1875169689Skan return (ENOMEM); 1876169689Skan } 1877169689Skan io->io_initiator_task_tag = initiator_task_tag; 1878169689Skan io->io_ccb = ccb; 1879169689Skan TAILQ_INSERT_TAIL(&is->is_outstanding, io, io_next); 1880169689Skan return (0); 1881169689Skan} 1882169689Skan 1883169689Skanstatic void 1884169689Skaniscsi_outstanding_remove(struct iscsi_session *is, struct iscsi_outstanding *io) 1885169689Skan{ 1886169689Skan 1887169689Skan ISCSI_SESSION_LOCK_ASSERT(is); 1888169689Skan 1889169689Skan TAILQ_REMOVE(&is->is_outstanding, io, io_next); 1890169689Skan uma_zfree(iscsi_outstanding_zone, io); 1891169689Skan} 1892169689Skan 1893169689Skanstatic void 1894169689Skaniscsi_action_scsiio(struct iscsi_session *is, union ccb *ccb) 1895169689Skan{ 1896169689Skan struct icl_pdu *request; 1897169689Skan struct iscsi_bhs_scsi_command *bhssc; 1898169689Skan struct ccb_scsiio *csio; 1899169689Skan size_t len; 1900169689Skan int error; 1901169689Skan 1902169689Skan ISCSI_SESSION_LOCK_ASSERT(is); 1903169689Skan 1904169689Skan#if 0 1905169689Skan KASSERT(is->is_login_phase == false, ("%s called during Login Phase", __func__)); 1906169689Skan#else 1907169689Skan if (is->is_login_phase) { 1908169689Skan ISCSI_SESSION_DEBUG(is, "called during login phase"); 1909169689Skan if ((ccb->ccb_h.status & CAM_DEV_QFRZN) == 0) { 1910169689Skan xpt_freeze_devq(ccb->ccb_h.path, 1); 1911169689Skan ISCSI_SESSION_DEBUG(is, "freezing devq"); 1912169689Skan } 1913169689Skan ccb->ccb_h.status = CAM_REQ_ABORTED | CAM_DEV_QFRZN; 1914169689Skan xpt_done(ccb); 1915169689Skan return; 1916169689Skan } 1917169689Skan#endif 1918169689Skan 1919169689Skan request = icl_pdu_new_bhs(is->is_conn, M_NOWAIT); 1920169689Skan if (request == NULL) { 1921169689Skan if ((ccb->ccb_h.status & CAM_DEV_QFRZN) == 0) { 1922169689Skan xpt_freeze_devq(ccb->ccb_h.path, 1); 1923169689Skan ISCSI_SESSION_DEBUG(is, "freezing devq"); 1924169689Skan } 1925169689Skan ccb->ccb_h.status = CAM_RESRC_UNAVAIL | CAM_DEV_QFRZN; 1926169689Skan xpt_done(ccb); 1927169689Skan return; 1928169689Skan } 1929169689Skan 1930169689Skan csio = &ccb->csio; 1931169689Skan bhssc = (struct iscsi_bhs_scsi_command *)request->ip_bhs; 1932169689Skan bhssc->bhssc_opcode = ISCSI_BHS_OPCODE_SCSI_COMMAND; 1933169689Skan bhssc->bhssc_flags |= BHSSC_FLAGS_F; 1934169689Skan switch (csio->ccb_h.flags & CAM_DIR_MASK) { 1935169689Skan case CAM_DIR_IN: 1936169689Skan bhssc->bhssc_flags |= BHSSC_FLAGS_R; 1937169689Skan break; 1938169689Skan case CAM_DIR_OUT: 1939169689Skan bhssc->bhssc_flags |= BHSSC_FLAGS_W; 1940169689Skan break; 1941169689Skan } 1942169689Skan 1943169689Skan switch (csio->tag_action) { 1944169689Skan case MSG_HEAD_OF_Q_TAG: 1945169689Skan bhssc->bhssc_flags |= BHSSC_FLAGS_ATTR_HOQ; 1946169689Skan break; 1947169689Skan break; 1948169689Skan case MSG_ORDERED_Q_TAG: 1949169689Skan bhssc->bhssc_flags |= BHSSC_FLAGS_ATTR_ORDERED; 1950169689Skan break; 1951169689Skan case MSG_ACA_TASK: 1952169689Skan bhssc->bhssc_flags |= BHSSC_FLAGS_ATTR_ACA; 1953169689Skan break; 1954169689Skan case CAM_TAG_ACTION_NONE: 1955169689Skan case MSG_SIMPLE_Q_TAG: 1956169689Skan default: 1957169689Skan bhssc->bhssc_flags |= BHSSC_FLAGS_ATTR_SIMPLE; 1958169689Skan break; 1959169689Skan } 1960169689Skan 1961169689Skan bhssc->bhssc_lun = iscsi_encode_lun(csio->ccb_h.target_lun); 1962169689Skan bhssc->bhssc_initiator_task_tag = is->is_initiator_task_tag; 1963169689Skan is->is_initiator_task_tag++; 1964169689Skan bhssc->bhssc_expected_data_transfer_length = htonl(csio->dxfer_len); 1965169689Skan KASSERT(csio->cdb_len <= sizeof(bhssc->bhssc_cdb), 1966169689Skan ("unsupported CDB size %zd", (size_t)csio->cdb_len)); 1967169689Skan 1968169689Skan if (csio->ccb_h.flags & CAM_CDB_POINTER) 1969169689Skan memcpy(&bhssc->bhssc_cdb, csio->cdb_io.cdb_ptr, csio->cdb_len); 1970169689Skan else 1971169689Skan memcpy(&bhssc->bhssc_cdb, csio->cdb_io.cdb_bytes, csio->cdb_len); 1972169689Skan 1973169689Skan error = iscsi_outstanding_add(is, bhssc->bhssc_initiator_task_tag, ccb); 1974169689Skan if (error != 0) { 1975169689Skan icl_pdu_free(request); 1976169689Skan if ((ccb->ccb_h.status & CAM_DEV_QFRZN) == 0) { 1977169689Skan xpt_freeze_devq(ccb->ccb_h.path, 1); 1978169689Skan ISCSI_SESSION_DEBUG(is, "freezing devq"); 1979169689Skan } 1980169689Skan ccb->ccb_h.status = CAM_RESRC_UNAVAIL | CAM_DEV_QFRZN; 1981169689Skan xpt_done(ccb); 1982169689Skan return; 1983169689Skan } 1984169689Skan 1985169689Skan if (is->is_immediate_data && 1986169689Skan (csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT) { 1987169689Skan len = csio->dxfer_len; 1988169689Skan //ISCSI_SESSION_DEBUG(is, "adding %zd of immediate data", len); 1989169689Skan if (len > is->is_first_burst_length) { 1990169689Skan ISCSI_SESSION_DEBUG(is, "len %zd -> %zd", len, is->is_first_burst_length); 1991169689Skan len = is->is_first_burst_length; 1992169689Skan } 1993169689Skan 1994169689Skan error = icl_pdu_append_data(request, csio->data_ptr, len, M_NOWAIT); 1995169689Skan if (error != 0) { 1996169689Skan icl_pdu_free(request); 1997169689Skan if ((ccb->ccb_h.status & CAM_DEV_QFRZN) == 0) { 1998169689Skan xpt_freeze_devq(ccb->ccb_h.path, 1); 1999169689Skan ISCSI_SESSION_DEBUG(is, "freezing devq"); 2000169689Skan } 2001169689Skan ccb->ccb_h.status = CAM_RESRC_UNAVAIL | CAM_DEV_QFRZN; 2002169689Skan xpt_done(ccb); 2003169689Skan return; 2004169689Skan } 2005169689Skan } 2006169689Skan iscsi_pdu_queue_locked(request); 2007169689Skan} 2008169689Skan 2009169689Skanstatic void 2010169689Skaniscsi_action(struct cam_sim *sim, union ccb *ccb) 2011169689Skan{ 2012169689Skan struct iscsi_session *is; 2013169689Skan 2014169689Skan is = cam_sim_softc(sim); 2015169689Skan 2016169689Skan ISCSI_SESSION_LOCK_ASSERT(is); 2017169689Skan 2018169689Skan if (is->is_terminating) { 2019169689Skan ISCSI_SESSION_DEBUG(is, "called during termination"); 2020169689Skan ccb->ccb_h.status = CAM_DEV_NOT_THERE; 2021169689Skan xpt_done(ccb); 2022169689Skan return; 2023169689Skan } 2024169689Skan 2025169689Skan switch (ccb->ccb_h.func_code) { 2026169689Skan case XPT_PATH_INQ: 2027169689Skan { 2028169689Skan struct ccb_pathinq *cpi = &ccb->cpi; 2029169689Skan 2030169689Skan cpi->version_num = 1; 2031169689Skan cpi->hba_inquiry = PI_TAG_ABLE; 2032169689Skan cpi->target_sprt = 0; 2033169689Skan //cpi->hba_misc = PIM_NOBUSRESET; 2034169689Skan cpi->hba_misc = 0; 2035169689Skan cpi->hba_eng_cnt = 0; 2036169689Skan cpi->max_target = 0; 2037169689Skan cpi->max_lun = 255; 2038169689Skan //cpi->initiator_id = 0; /* XXX */ 2039169689Skan cpi->initiator_id = 64; /* XXX */ 2040169689Skan strlcpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); 2041169689Skan strlcpy(cpi->hba_vid, "iSCSI", HBA_IDLEN); 2042169689Skan strlcpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); 2043169689Skan cpi->unit_number = cam_sim_unit(sim); 2044169689Skan cpi->bus_id = cam_sim_bus(sim); 2045169689Skan cpi->base_transfer_speed = 150000; /* XXX */ 2046169689Skan cpi->transport = XPORT_ISCSI; 2047169689Skan cpi->transport_version = 0; 2048169689Skan cpi->protocol = PROTO_SCSI; 2049169689Skan cpi->protocol_version = SCSI_REV_SPC3; 2050169689Skan cpi->maxio = MAXPHYS; 2051169689Skan cpi->ccb_h.status = CAM_REQ_CMP; 2052169689Skan break; 2053169689Skan } 2054169689Skan case XPT_CALC_GEOMETRY: 2055169689Skan cam_calc_geometry(&ccb->ccg, /*extended*/1); 2056169689Skan ccb->ccb_h.status = CAM_REQ_CMP; 2057169689Skan break; 2058169689Skan#if 0 2059169689Skan /* 2060169689Skan * XXX: What's the point? 2061169689Skan */ 2062169689Skan case XPT_RESET_BUS: 2063169689Skan case XPT_ABORT: 2064169689Skan case XPT_TERM_IO: 2065169689Skan ISCSI_SESSION_DEBUG(is, "faking success for reset, abort, or term_io"); 2066169689Skan ccb->ccb_h.status = CAM_REQ_CMP; 2067169689Skan break; 2068169689Skan#endif 2069169689Skan case XPT_SCSI_IO: 2070169689Skan iscsi_action_scsiio(is, ccb); 2071169689Skan return; 2072169689Skan default: 2073169689Skan#if 0 2074169689Skan ISCSI_SESSION_DEBUG(is, "got unsupported code 0x%x", ccb->ccb_h.func_code); 2075169689Skan#endif 2076169689Skan ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; 2077169689Skan break; 2078169689Skan } 2079169689Skan xpt_done(ccb); 2080169689Skan} 2081169689Skan 2082169689Skanstatic void 2083169689Skaniscsi_poll(struct cam_sim *sim) 2084169689Skan{ 2085169689Skan 2086169689Skan KASSERT(0, ("%s: you're not supposed to be here", __func__)); 2087169689Skan} 2088169689Skan 2089169689Skanstatic void 2090169689Skaniscsi_shutdown(struct iscsi_softc *sc) 2091169689Skan{ 2092169689Skan struct iscsi_session *is; 2093169689Skan 2094169689Skan ISCSI_DEBUG("removing all sessions due to shutdown"); 2095169689Skan 2096169689Skan sx_slock(&sc->sc_lock); 2097169689Skan TAILQ_FOREACH(is, &sc->sc_sessions, is_next) 2098169689Skan iscsi_session_terminate(is); 2099169689Skan sx_sunlock(&sc->sc_lock); 2100169689Skan} 2101169689Skan 2102169689Skanstatic int 2103169689Skaniscsi_load(void) 2104169689Skan{ 2105169689Skan int error; 2106169689Skan 2107169689Skan sc = malloc(sizeof(*sc), M_ISCSI, M_ZERO | M_WAITOK); 2108169689Skan sx_init(&sc->sc_lock, "iscsi"); 2109169689Skan TAILQ_INIT(&sc->sc_sessions); 2110169689Skan cv_init(&sc->sc_cv, "iscsi_cv"); 2111169689Skan 2112169689Skan iscsi_outstanding_zone = uma_zcreate("iscsi_outstanding", 2113169689Skan sizeof(struct iscsi_outstanding), NULL, NULL, NULL, NULL, 2114169689Skan UMA_ALIGN_PTR, 0); 2115169689Skan 2116169689Skan error = make_dev_p(MAKEDEV_CHECKNAME, &sc->sc_cdev, &iscsi_cdevsw, 2117169689Skan NULL, UID_ROOT, GID_WHEEL, 0600, "iscsi"); 2118169689Skan if (error != 0) { 2119169689Skan ISCSI_WARN("failed to create device node, error %d", error); 2120169689Skan return (error); 2121169689Skan } 2122169689Skan sc->sc_cdev->si_drv1 = sc; 2123169689Skan 2124169689Skan /* 2125169689Skan * Note that this needs to get run before dashutdown(). Otherwise, 2126169689Skan * when rebooting with iSCSI session with outstanding requests, 2127169689Skan * but disconnected, dashutdown() will hang on cam_periph_runccb(). 2128169689Skan */ 2129169689Skan sc->sc_shutdown_eh = EVENTHANDLER_REGISTER(shutdown_post_sync, 2130169689Skan iscsi_shutdown, sc, SHUTDOWN_PRI_FIRST); 2131169689Skan 2132169689Skan return (0); 2133169689Skan} 2134169689Skan 2135169689Skanstatic int 2136169689Skaniscsi_unload(void) 2137169689Skan{ 2138169689Skan struct iscsi_session *is, *tmp; 2139169689Skan 2140169689Skan if (sc->sc_cdev != NULL) { 2141169689Skan ISCSI_DEBUG("removing device node"); 2142169689Skan destroy_dev(sc->sc_cdev); 2143169689Skan ISCSI_DEBUG("device node removed"); 2144169689Skan } 2145169689Skan 2146169689Skan if (sc->sc_shutdown_eh != NULL) 2147169689Skan EVENTHANDLER_DEREGISTER(shutdown_post_sync, sc->sc_shutdown_eh); 2148169689Skan 2149169689Skan sx_slock(&sc->sc_lock); 2150169689Skan TAILQ_FOREACH_SAFE(is, &sc->sc_sessions, is_next, tmp) 2151169689Skan iscsi_session_terminate(is); 2152169689Skan while(!TAILQ_EMPTY(&sc->sc_sessions)) { 2153169689Skan ISCSI_DEBUG("waiting for sessions to terminate"); 2154169689Skan cv_wait(&sc->sc_cv, &sc->sc_lock); 2155169689Skan } 2156169689Skan ISCSI_DEBUG("all sessions terminated"); 2157169689Skan sx_sunlock(&sc->sc_lock); 2158169689Skan 2159169689Skan uma_zdestroy(iscsi_outstanding_zone); 2160169689Skan sx_destroy(&sc->sc_lock); 2161169689Skan cv_destroy(&sc->sc_cv); 2162169689Skan free(sc, M_ISCSI); 2163169689Skan return (0); 2164169689Skan} 2165171825Skan 2166171825Skanstatic int 2167169689Skaniscsi_quiesce(void) 2168171825Skan{ 2169171825Skan sx_slock(&sc->sc_lock); 2170171825Skan if (!TAILQ_EMPTY(&sc->sc_sessions)) { 2171171825Skan sx_sunlock(&sc->sc_lock); 2172171825Skan return (EBUSY); 2173169689Skan } 2174169689Skan sx_sunlock(&sc->sc_lock); 2175169689Skan return (0); 2176169689Skan} 2177169689Skan 2178169689Skanstatic int 2179169689Skaniscsi_modevent(module_t mod, int what, void *arg) 2180169689Skan{ 2181169689Skan int error; 2182169689Skan 2183169689Skan switch (what) { 2184169689Skan case MOD_LOAD: 2185169689Skan error = iscsi_load(); 2186169689Skan break; 2187169689Skan case MOD_UNLOAD: 2188169689Skan error = iscsi_unload(); 2189169689Skan break; 2190169689Skan case MOD_QUIESCE: 2191169689Skan error = iscsi_quiesce(); 2192169689Skan break; 2193169689Skan default: 2194169689Skan error = EINVAL; 2195169689Skan break; 2196169689Skan } 2197169689Skan return (error); 2198169689Skan} 2199169689Skan 2200169689Skanmoduledata_t iscsi_data = { 2201169689Skan "iscsi", 2202169689Skan iscsi_modevent, 2203169689Skan 0 2204169689Skan}; 2205169689Skan 2206169689SkanDECLARE_MODULE(iscsi, iscsi_data, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 2207169689SkanMODULE_DEPEND(iscsi, cam, 1, 1, 1); 2208169689SkanMODULE_DEPEND(iscsi, icl, 1, 1, 1); 2209169689Skan