📄 libiscsi.c
字号:
} spin_unlock_bh(&session->lock); mutex_unlock(&session->eh_mutex); scsi_queue_work(session->host, &conn->xmitwork); /* * block eh thread until: * * 1) abort response * 2) abort timeout * 3) session is terminated or restarted or userspace has * given up on recovery */ wait_event_interruptible(conn->ehwait, sc->SCp.phase != session->age || session->state != ISCSI_STATE_LOGGED_IN || conn->tmabort_state != TMABORT_INITIAL); if (signal_pending(current)) flush_signals(current); del_timer_sync(&conn->tmabort_timer); mutex_lock(&session->eh_mutex); spin_lock_bh(&session->lock); return 0;}/* * session lock must be held */static struct iscsi_mgmt_task *iscsi_remove_mgmt_task(struct kfifo *fifo, uint32_t itt){ int i, nr_tasks = __kfifo_len(fifo) / sizeof(void*); struct iscsi_mgmt_task *task; debug_scsi("searching %d tasks\n", nr_tasks); for (i = 0; i < nr_tasks; i++) { __kfifo_get(fifo, (void*)&task, sizeof(void*)); debug_scsi("check task %u\n", task->itt); if (task->itt == itt) { debug_scsi("matched task\n"); return task; } __kfifo_put(fifo, (void*)&task, sizeof(void*)); } return NULL;}static int iscsi_ctask_mtask_cleanup(struct iscsi_cmd_task *ctask){ struct iscsi_conn *conn = ctask->conn; struct iscsi_session *session = conn->session; if (!ctask->mtask) return -EINVAL; if (!iscsi_remove_mgmt_task(conn->mgmtqueue, ctask->mtask->itt)) list_del(&ctask->mtask->running); __kfifo_put(session->mgmtpool.queue, (void*)&ctask->mtask, sizeof(void*)); ctask->mtask = NULL; return 0;}/* * session lock must be held */static void fail_command(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask, int err){ struct scsi_cmnd *sc; sc = ctask->sc; if (!sc) return; if (ctask->state == ISCSI_TASK_PENDING) /* * cmd never made it to the xmit thread, so we should not count * the cmd in the sequencing */ conn->session->queued_cmdsn--; else conn->session->tt->cleanup_cmd_task(conn, ctask); iscsi_ctask_mtask_cleanup(ctask); sc->result = err; scsi_set_resid(sc, scsi_bufflen(sc)); if (conn->ctask == ctask) conn->ctask = NULL; /* release ref from queuecommand */ __iscsi_put_ctask(ctask);}static void iscsi_suspend_tx(struct iscsi_conn *conn){ set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx); scsi_flush_work(conn->session->host);}static void iscsi_start_tx(struct iscsi_conn *conn){ clear_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx); scsi_queue_work(conn->session->host, &conn->xmitwork);}int iscsi_eh_abort(struct scsi_cmnd *sc){ struct Scsi_Host *host = sc->device->host; struct iscsi_session *session = iscsi_hostdata(host->hostdata); struct iscsi_cmd_task *ctask; struct iscsi_conn *conn; int rc; mutex_lock(&session->eh_mutex); spin_lock_bh(&session->lock); /* * if session was ISCSI_STATE_IN_RECOVERY then we may not have * got the command. */ if (!sc->SCp.ptr) { debug_scsi("sc never reached iscsi layer or it completed.\n"); spin_unlock_bh(&session->lock); mutex_unlock(&session->eh_mutex); return SUCCESS; } ctask = (struct iscsi_cmd_task *)sc->SCp.ptr; conn = ctask->conn; conn->eh_abort_cnt++; debug_scsi("aborting [sc %p itt 0x%x]\n", sc, ctask->itt); /* * If we are not logged in or we have started a new session * then let the host reset code handle this */ if (session->state != ISCSI_STATE_LOGGED_IN || sc->SCp.phase != session->age) goto failed; /* ctask completed before time out */ if (!ctask->sc) { debug_scsi("sc completed while abort in progress\n"); goto success; } /* what should we do here ? */ if (conn->ctask == ctask) { printk(KERN_INFO "iscsi: sc %p itt 0x%x partially sent. " "Failing abort\n", sc, ctask->itt); goto failed; } if (ctask->state == ISCSI_TASK_PENDING) { fail_command(conn, ctask, DID_ABORT << 16); goto success; } conn->tmabort_state = TMABORT_INITIAL; rc = iscsi_exec_abort_task(sc, ctask); if (rc || sc->SCp.phase != session->age || session->state != ISCSI_STATE_LOGGED_IN) goto failed; iscsi_ctask_mtask_cleanup(ctask); switch (conn->tmabort_state) { case TMABORT_SUCCESS: spin_unlock_bh(&session->lock); iscsi_suspend_tx(conn); /* * clean up task if aborted. grab the recv lock as a writer */ write_lock_bh(conn->recv_lock); spin_lock(&session->lock); fail_command(conn, ctask, DID_ABORT << 16); spin_unlock(&session->lock); write_unlock_bh(conn->recv_lock); iscsi_start_tx(conn); goto success_unlocked; case TMABORT_NOT_FOUND: if (!ctask->sc) { /* ctask completed before tmf abort response */ debug_scsi("sc completed while abort in progress\n"); goto success; } /* fall through */ default: /* timedout or failed */ spin_unlock_bh(&session->lock); iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); goto failed_unlocked; }success: spin_unlock_bh(&session->lock);success_unlocked: debug_scsi("abort success [sc %lx itt 0x%x]\n", (long)sc, ctask->itt); mutex_unlock(&session->eh_mutex); return SUCCESS;failed: spin_unlock_bh(&session->lock);failed_unlocked: debug_scsi("abort failed [sc %lx itt 0x%x]\n", (long)sc, ctask->itt); mutex_unlock(&session->eh_mutex); return FAILED;}EXPORT_SYMBOL_GPL(iscsi_eh_abort);intiscsi_pool_init(struct iscsi_queue *q, int max, void ***items, int item_size){ int i; *items = kmalloc(max * sizeof(void*), GFP_KERNEL); if (*items == NULL) return -ENOMEM; q->max = max; q->pool = kmalloc(max * sizeof(void*), GFP_KERNEL); if (q->pool == NULL) { kfree(*items); return -ENOMEM; } q->queue = kfifo_init((void*)q->pool, max * sizeof(void*), GFP_KERNEL, NULL); if (q->queue == ERR_PTR(-ENOMEM)) { kfree(q->pool); kfree(*items); return -ENOMEM; } for (i = 0; i < max; i++) { q->pool[i] = kmalloc(item_size, GFP_KERNEL); if (q->pool[i] == NULL) { int j; for (j = 0; j < i; j++) kfree(q->pool[j]); kfifo_free(q->queue); kfree(q->pool); kfree(*items); return -ENOMEM; } memset(q->pool[i], 0, item_size); (*items)[i] = q->pool[i]; __kfifo_put(q->queue, (void*)&q->pool[i], sizeof(void*)); } return 0;}EXPORT_SYMBOL_GPL(iscsi_pool_init);void iscsi_pool_free(struct iscsi_queue *q, void **items){ int i; for (i = 0; i < q->max; i++) kfree(items[i]); kfree(q->pool); kfree(items);}EXPORT_SYMBOL_GPL(iscsi_pool_free);/* * iSCSI Session's hostdata organization: * * *------------------* <== hostdata_session(host->hostdata) * | ptr to class sess| * |------------------| <== iscsi_hostdata(host->hostdata) * | iscsi_session | * *------------------* */#define hostdata_privsize(_sz) (sizeof(unsigned long) + _sz + \ _sz % sizeof(unsigned long))#define hostdata_session(_hostdata) (iscsi_ptr(*(unsigned long *)_hostdata))/** * iscsi_session_setup - create iscsi cls session and host and session * @scsit: scsi transport template * @iscsit: iscsi transport template * @cmds_max: scsi host can queue * @qdepth: scsi host cmds per lun * @cmd_task_size: LLD ctask private data size * @mgmt_task_size: LLD mtask private data size * @initial_cmdsn: initial CmdSN * @hostno: host no allocated * * This can be used by software iscsi_transports that allocate * a session per scsi host. **/struct iscsi_cls_session *iscsi_session_setup(struct iscsi_transport *iscsit, struct scsi_transport_template *scsit, uint16_t cmds_max, uint16_t qdepth, int cmd_task_size, int mgmt_task_size, uint32_t initial_cmdsn, uint32_t *hostno){ struct Scsi_Host *shost; struct iscsi_session *session; struct iscsi_cls_session *cls_session; int cmd_i; if (qdepth > ISCSI_MAX_CMD_PER_LUN || qdepth < 1) { if (qdepth != 0) printk(KERN_ERR "iscsi: invalid queue depth of %d. " "Queue depth must be between 1 and %d.\n", qdepth, ISCSI_MAX_CMD_PER_LUN); qdepth = ISCSI_DEF_CMD_PER_LUN; } if (cmds_max < 2 || (cmds_max & (cmds_max - 1)) || cmds_max >= ISCSI_MGMT_ITT_OFFSET) { if (cmds_max != 0) printk(KERN_ERR "iscsi: invalid can_queue of %d. " "can_queue must be a power of 2 and between " "2 and %d - setting to %d.\n", cmds_max, ISCSI_MGMT_ITT_OFFSET, ISCSI_DEF_XMIT_CMDS_MAX); cmds_max = ISCSI_DEF_XMIT_CMDS_MAX; } shost = scsi_host_alloc(iscsit->host_template, hostdata_privsize(sizeof(*session))); if (!shost) return NULL; /* the iscsi layer takes one task for reserve */ shost->can_queue = cmds_max - 1; shost->cmd_per_lun = qdepth; shost->max_id = 1; shost->max_channel = 0; shost->max_lun = iscsit->max_lun; shost->max_cmd_len = iscsit->max_cmd_len; shost->transportt = scsit; shost->transportt->create_work_queue = 1; *hostno = shost->host_no; session = iscsi_hostdata(shost->hostdata); memset(session, 0, sizeof(struct iscsi_session)); session->host = shost; session->state = ISCSI_STATE_FREE; session->mgmtpool_max = ISCSI_MGMT_CMDS_MAX; session->cmds_max = cmds_max; session->queued_cmdsn = session->cmdsn = initial_cmdsn; session->exp_cmdsn = initial_cmdsn + 1; session->max_cmdsn = initial_cmdsn + 1; session->max_r2t = 1; session->tt = iscsit; mutex_init(&session->eh_mutex); /* initialize SCSI PDU commands pool */ if (iscsi_pool_init(&session->cmdpool, session->cmds_max, (void***)&session->cmds, cmd_task_size + sizeof(struct iscsi_cmd_task))) goto cmdpool_alloc_fail; /* pre-format cmds pool with ITT */ for (cmd_i = 0; cmd_i < session->cmds_max; cmd_i++) { struct iscsi_cmd_task *ctask = session->cmds[cmd_i]; if (cmd_task_size) ctask->dd_data = &ctask[1]; ctask->itt = cmd_i; INIT_LIST_HEAD(&ctask->running); } spin_lock_init(&session->lock); /* initialize immediate command pool */ if (iscsi_pool_init(&session->mgmtpool, session->mgmtpool_max, (void***)&session->mgmt_cmds, mgmt_task_size + sizeof(struct iscsi_mgmt_task))) goto mgmtpool_alloc_fail; /* pre-format immediate cmds pool with ITT */ for (cmd_i = 0; cmd_i < session->mgmtpool_max; cmd_i++) { struct iscsi_mgmt_task *mtask = session->mgmt_cmds[cmd_i]; if (mgmt_task_size) mtask->dd_data = &mtask[1]; mtask->itt = ISCSI_MGMT_ITT_OFFSET + cmd_i; INIT_LIST_HEAD(&mtask->running); } if (scsi_add_host(shost, NULL)) goto add_host_fail; if (!try_module_get(iscsit->owner)) goto cls_session_fail; cls_session = iscsi_create_session(shost, iscsit, 0); if (!cls_session) goto module_put; *(unsigned long*)shost->hostdata = (unsigned long)cls_session; return cls_session;module_put: module_put(iscsit->owner);cls_session_fail: scsi_remove_host(shost);add_host_fail: iscsi_pool_free(&session->mgmtpool, (void**)session->mgmt_cmds);mgmtpool_alloc_fail: iscsi_pool_free(&session->cmdpool, (void**)session->cmds);cmdpool_alloc_fail: scsi_host_put(shost); return NULL;}EXPORT_SYMBOL_GPL(iscsi_session_setup);/** * iscsi_session_teardown - destroy session, host, and cls_session * shost: scsi host * * This can be used by software iscsi_transports that allocate * a session per scsi host. **/void iscsi_session_teardown(struct iscsi_cls_session *cls_session){ struct Scsi_Host *shost = iscsi_session_to_shost(cls_session); struct iscsi_session *session = iscsi_hostdata(shost->hostdata); struct module *owner = cls_session->transport->owner; iscsi_unblock_session(cls_session); scsi_remove_host(shost); iscsi_pool_free(&session->mgmtpool, (void**)session->mgmt_cmds); iscsi_pool_free(&session->cmdpool, (void**)session->cmds); kfree(session->password); kfree(session->password_in); kfree(session->username); kfree(session->username_in); kfree(session->targetname); kfree(session->netdev); kfree(session->hwaddress); kfree(session->initiatorname); iscsi_destroy_session(cls_session); scsi_host_put(shost); module_put(owner);}EXPORT_SYMBOL_GPL(iscsi_session_teardown);/** * iscsi_conn_setup - create iscsi_cls_conn and iscsi_conn * @cls_session: iscsi_cls_session * @conn_idx: cid **/struct iscsi_cls_conn *iscsi_conn_setup(struct iscsi_cls_session *cls_session, uint32_t conn_idx){ struct iscsi_session *session = class_to_transport_session(cls_session); struct iscsi_conn *conn; struct iscsi_cls_conn *cls_conn; char *data; cls_conn = iscsi_create_conn(cls_session, conn_idx); if (!cls_conn) return NULL; conn = cls_conn->dd_data; memset(conn, 0, sizeof(*conn)); conn->session = session; conn->cls_conn = cls_conn; conn->c_stage = ISCSI_CONN_INITIAL_STAGE; conn->id = conn_idx; conn->exp_statsn = 0; conn->tmabort_state = TMABORT_INITIAL; INIT_LIST_HEAD(&conn->run_list); INIT_LIST_HEAD(&conn->mgmt_run_list); INIT_LIST_HEAD(&conn->xmitqueue); /* initialize general immediate & non-immediate PDU commands queue */ conn->mgmtqueue = kfifo_alloc(session->mgmtpool_max * sizeof(void*), GFP_KERNEL, NULL); if (conn->mgmtqueue == ERR_PTR(-ENOMEM)) goto mgmtqueue_alloc_fail; INIT_WORK(&conn->xmitwork, iscsi_xmitworker); /* allocate login_mtask used for the login/text sequences */ spin_lock_bh(&session->lock); if (!__kfifo_get(session->mgmtpool.queue, (void*)&conn->login_mtask, sizeof(void*))) { spin_unlock_bh(&session->lock); goto login_mtask_alloc_fail; } spin_unlock_bh(&session->lock); data = kmalloc(ISCSI_DEF_MAX_RECV_SEG_LEN, GFP_KERNEL); if (!data) goto login_mtask_data_alloc_fail; conn->login_mtask->data = conn->data = data; init_timer(&conn->tmabort_timer); init_waitqueue_head(&conn->ehwait); return cls_conn;login_mtask_data_alloc_fail: __kfifo_put(session->mgmtpool.queue, (void*)&conn->login_mtask, sizeof(void*));login_mtask_alloc_fail: kfifo_free(conn->mgmtqueue);mgmtqueue_alloc_fail: iscsi_destroy_conn(cls_conn); return NULL;}EXPORT_SYMBOL_GPL(iscsi_conn_setup);/** * iscsi_conn_teardown - teardown iscsi connection * cls_conn: iscsi class connection * * TODO: we may need to make this into a two step process * like scsi-mls remove + put host */void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn){ struct iscsi_conn *conn = cls_conn->dd_data; struct iscsi_session *session = conn->session; unsigned long flags; spin_lock_bh(&session->lock); set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx); conn->c_stage = ISCSI_CONN_CLEANUP_WAIT;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -