diff options
Diffstat (limited to 'drivers/scsi/libiscsi.c')
| -rw-r--r-- | drivers/scsi/libiscsi.c | 1203 |
1 files changed, 903 insertions, 300 deletions
diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index 716cc344c5d..3d1bc67bac9 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -25,6 +25,8 @@ #include <linux/kfifo.h> #include <linux/delay.h> #include <linux/log2.h> +#include <linux/slab.h> +#include <linux/module.h> #include <asm/unaligned.h> #include <net/tcp.h> #include <scsi/scsi_cmnd.h> @@ -83,22 +85,6 @@ MODULE_PARM_DESC(debug_libiscsi_eh, __func__, ##arg); \ } while (0); -/* Serial Number Arithmetic, 32 bits, less than, RFC1982 */ -#define SNA32_CHECK 2147483648UL - -static int iscsi_sna_lt(u32 n1, u32 n2) -{ - return n1 != n2 && ((n1 < n2 && (n2 - n1 < SNA32_CHECK)) || - (n1 > n2 && (n2 - n1 < SNA32_CHECK))); -} - -/* Serial Number Arithmetic, 32 bits, less than, RFC1982 */ -static int iscsi_sna_lte(u32 n1, u32 n2) -{ - return n1 == n2 || ((n1 < n2 && (n2 - n1 < SNA32_CHECK)) || - (n1 > n2 && (n2 - n1 < SNA32_CHECK))); -} - inline void iscsi_conn_queue_work(struct iscsi_conn *conn) { struct Scsi_Host *shost = conn->session->host; @@ -109,12 +95,9 @@ inline void iscsi_conn_queue_work(struct iscsi_conn *conn) } EXPORT_SYMBOL_GPL(iscsi_conn_queue_work); -void -iscsi_update_cmdsn(struct iscsi_session *session, struct iscsi_nopin *hdr) +static void __iscsi_update_cmdsn(struct iscsi_session *session, + uint32_t exp_cmdsn, uint32_t max_cmdsn) { - uint32_t max_cmdsn = be32_to_cpu(hdr->max_cmdsn); - uint32_t exp_cmdsn = be32_to_cpu(hdr->exp_cmdsn); - /* * standard specifies this check for when to update expected and * max sequence numbers @@ -127,16 +110,14 @@ iscsi_update_cmdsn(struct iscsi_session *session, struct iscsi_nopin *hdr) session->exp_cmdsn = exp_cmdsn; if (max_cmdsn != session->max_cmdsn && - !iscsi_sna_lt(max_cmdsn, session->max_cmdsn)) { + !iscsi_sna_lt(max_cmdsn, session->max_cmdsn)) session->max_cmdsn = max_cmdsn; - /* - * if the window closed with IO queued, then kick the - * xmit thread - */ - if (!list_empty(&session->leadconn->cmdqueue) || - !list_empty(&session->leadconn->mgmtqueue)) - iscsi_conn_queue_work(session->leadconn); - } +} + +void iscsi_update_cmdsn(struct iscsi_session *session, struct iscsi_nopin *hdr) +{ + __iscsi_update_cmdsn(session, be32_to_cpu(hdr->exp_cmdsn), + be32_to_cpu(hdr->max_cmdsn)); } EXPORT_SYMBOL_GPL(iscsi_update_cmdsn); @@ -165,7 +146,7 @@ void iscsi_prep_data_out_pdu(struct iscsi_task *task, struct iscsi_r2t_info *r2t hdr->datasn = cpu_to_be32(r2t->datasn); r2t->datasn++; hdr->opcode = ISCSI_OP_SCSI_DATA_OUT; - memcpy(hdr->lun, task->lun, sizeof(hdr->lun)); + hdr->lun = task->lun; hdr->itt = task->hdr_itt; hdr->exp_statsn = r2t->exp_statsn; hdr->offset = cpu_to_be32(r2t->data_offset + r2t->sent); @@ -263,6 +244,88 @@ static int iscsi_prep_bidi_ahs(struct iscsi_task *task) } /** + * iscsi_check_tmf_restrictions - check if a task is affected by TMF + * @task: iscsi task + * @opcode: opcode to check for + * + * During TMF a task has to be checked if it's affected. + * All unrelated I/O can be passed through, but I/O to the + * affected LUN should be restricted. + * If 'fast_abort' is set we won't be sending any I/O to the + * affected LUN. + * Otherwise the target is waiting for all TTTs to be completed, + * so we have to send all outstanding Data-Out PDUs to the target. + */ +static int iscsi_check_tmf_restrictions(struct iscsi_task *task, int opcode) +{ + struct iscsi_conn *conn = task->conn; + struct iscsi_tm *tmf = &conn->tmhdr; + unsigned int hdr_lun; + + if (conn->tmf_state == TMF_INITIAL) + return 0; + + if ((tmf->opcode & ISCSI_OPCODE_MASK) != ISCSI_OP_SCSI_TMFUNC) + return 0; + + switch (ISCSI_TM_FUNC_VALUE(tmf)) { + case ISCSI_TM_FUNC_LOGICAL_UNIT_RESET: + /* + * Allow PDUs for unrelated LUNs + */ + hdr_lun = scsilun_to_int(&tmf->lun); + if (hdr_lun != task->sc->device->lun) + return 0; + /* fall through */ + case ISCSI_TM_FUNC_TARGET_WARM_RESET: + /* + * Fail all SCSI cmd PDUs + */ + if (opcode != ISCSI_OP_SCSI_DATA_OUT) { + iscsi_conn_printk(KERN_INFO, conn, + "task [op %x/%x itt " + "0x%x/0x%x] " + "rejected.\n", + task->hdr->opcode, opcode, + task->itt, task->hdr_itt); + return -EACCES; + } + /* + * And also all data-out PDUs in response to R2T + * if fast_abort is set. + */ + if (conn->session->fast_abort) { + iscsi_conn_printk(KERN_INFO, conn, + "task [op %x/%x itt " + "0x%x/0x%x] fast abort.\n", + task->hdr->opcode, opcode, + task->itt, task->hdr_itt); + return -EACCES; + } + break; + case ISCSI_TM_FUNC_ABORT_TASK: + /* + * the caller has already checked if the task + * they want to abort was in the pending queue so if + * we are here the cmd pdu has gone out already, and + * we will only hit this for data-outs + */ + if (opcode == ISCSI_OP_SCSI_DATA_OUT && + task->hdr_itt == tmf->rtt) { + ISCSI_DBG_SESSION(conn->session, + "Preventing task %x/%x from sending " + "data-out due to abort task in " + "progress\n", task->itt, + task->hdr_itt); + return -EACCES; + } + break; + } + + return 0; +} + +/** * iscsi_prep_scsi_cmd_pdu - prep iscsi scsi cmd pdu * @task: iscsi task * @@ -274,17 +337,21 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task) struct iscsi_conn *conn = task->conn; struct iscsi_session *session = conn->session; struct scsi_cmnd *sc = task->sc; - struct iscsi_cmd *hdr; - unsigned hdrlength, cmd_len; + struct iscsi_scsi_req *hdr; + unsigned hdrlength, cmd_len, transfer_length; itt_t itt; int rc; + rc = iscsi_check_tmf_restrictions(task, ISCSI_OP_SCSI_CMD); + if (rc) + return rc; + if (conn->session->tt->alloc_pdu) { rc = conn->session->tt->alloc_pdu(task, ISCSI_OP_SCSI_CMD); if (rc) return rc; } - hdr = (struct iscsi_cmd *) task->hdr; + hdr = (struct iscsi_scsi_req *)task->hdr; itt = hdr->itt; memset(hdr, 0, sizeof(*hdr)); @@ -299,10 +366,8 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task) return rc; hdr->opcode = ISCSI_OP_SCSI_CMD; hdr->flags = ISCSI_ATTR_SIMPLE; - int_to_scsilun(sc->device->lun, (struct scsi_lun *)hdr->lun); - memcpy(task->lun, hdr->lun, sizeof(task->lun)); - hdr->cmdsn = task->cmdsn = cpu_to_be32(session->cmdsn); - session->cmdsn++; + int_to_scsilun(sc->device->lun, &hdr->lun); + task->lun = hdr->lun; hdr->exp_statsn = cpu_to_be32(conn->exp_statsn); cmd_len = sc->cmd_len; if (cmd_len < ISCSI_CDB_SIZE) @@ -322,11 +387,15 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task) if (rc) return rc; } + + if (scsi_get_prot_op(sc) != SCSI_PROT_NORMAL) + task->protected = true; + + transfer_length = scsi_transfer_length(sc); + hdr->data_length = cpu_to_be32(transfer_length); if (sc->sc_data_direction == DMA_TO_DEVICE) { - unsigned out_len = scsi_out(sc)->length; struct iscsi_r2t_info *r2t = &task->unsol_r2t; - hdr->data_length = cpu_to_be32(out_len); hdr->flags |= ISCSI_FLAG_CMD_WRITE; /* * Write counters: @@ -345,18 +414,19 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task) memset(r2t, 0, sizeof(*r2t)); if (session->imm_data_en) { - if (out_len >= session->first_burst) + if (transfer_length >= session->first_burst) task->imm_count = min(session->first_burst, conn->max_xmit_dlength); else - task->imm_count = min(out_len, - conn->max_xmit_dlength); + task->imm_count = min(transfer_length, + conn->max_xmit_dlength); hton24(hdr->dlength, task->imm_count); } else zero_data(hdr->dlength); if (!session->initial_r2t_en) { - r2t->data_length = min(session->first_burst, out_len) - + r2t->data_length = min(session->first_burst, + transfer_length) - task->imm_count; r2t->data_offset = task->imm_count; r2t->ttt = cpu_to_be32(ISCSI_RESERVED_TAG); @@ -369,7 +439,6 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task) } else { hdr->flags |= ISCSI_FLAG_CMD_FINAL; zero_data(hdr->dlength); - hdr->data_length = cpu_to_be32(scsi_in(sc)->length); if (sc->sc_data_direction == DMA_FROM_DEVICE) hdr->flags |= ISCSI_FLAG_CMD_READ; @@ -383,11 +452,13 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task) WARN_ON(hdrlength >= 256); hdr->hlength = hdrlength & 0xFF; + hdr->cmdsn = task->cmdsn = cpu_to_be32(session->cmdsn); if (session->tt->init_task && session->tt->init_task(task)) return -EIO; task->state = ISCSI_TASK_RUNNING; + session->cmdsn++; conn->scsicmd_pdus_cnt++; ISCSI_DBG_SESSION(session, "iscsi prep [%s cid %d sc %p cdb 0x%x " @@ -395,7 +466,7 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task) scsi_bidi_cmnd(sc) ? "bidirectional" : sc->sc_data_direction == DMA_TO_DEVICE ? "write" : "read", conn->id, sc, sc->cmnd[0], - task->itt, scsi_bufflen(sc), + task->itt, transfer_length, scsi_bidi_cmnd(sc) ? scsi_in(sc)->length : 0, session->cmdsn, session->max_cmdsn - session->exp_cmdsn + 1); @@ -406,7 +477,7 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task) * iscsi_free_task - free a task * @task: iscsi cmd task * - * Must be called with session lock. + * Must be called with session back_lock. * This function returns the scsi command to scsi-ml or cleans * up mgmt tasks then returns the task to the pool. */ @@ -415,6 +486,7 @@ static void iscsi_free_task(struct iscsi_task *task) struct iscsi_conn *conn = task->conn; struct iscsi_session *session = conn->session; struct scsi_cmnd *sc = task->sc; + int oldstate = task->state; ISCSI_DBG_SESSION(session, "freeing task itt 0x%x state %d sc %p\n", task->itt, task->state, task->sc); @@ -428,17 +500,16 @@ static void iscsi_free_task(struct iscsi_task *task) if (conn->login_task == task) return; - __kfifo_put(session->cmdpool.queue, (void*)&task, sizeof(void*)); + kfifo_in(&session->cmdpool.queue, (void*)&task, sizeof(void*)); if (sc) { - task->sc = NULL; /* SCSI eh reuses commands to verify us */ sc->SCp.ptr = NULL; /* - * queue command may call this to free the task, but - * not have setup the sc callback + * queue command may call this to free the task, so + * it will decide how to return sc to scsi-ml. */ - if (sc->scsi_done) + if (oldstate != ISCSI_TASK_REQUEUE_SCSIQ) sc->scsi_done(sc); } } @@ -449,19 +520,21 @@ void __iscsi_get_task(struct iscsi_task *task) } EXPORT_SYMBOL_GPL(__iscsi_get_task); -static void __iscsi_put_task(struct iscsi_task *task) +void __iscsi_put_task(struct iscsi_task *task) { if (atomic_dec_and_test(&task->refcount)) iscsi_free_task(task); } +EXPORT_SYMBOL_GPL(__iscsi_put_task); void iscsi_put_task(struct iscsi_task *task) { struct iscsi_session *session = task->conn->session; - spin_lock_bh(&session->lock); + /* regular RX path uses back_lock */ + spin_lock_bh(&session->back_lock); __iscsi_put_task(task); - spin_unlock_bh(&session->lock); + spin_unlock_bh(&session->back_lock); } EXPORT_SYMBOL_GPL(iscsi_put_task); @@ -470,7 +543,7 @@ EXPORT_SYMBOL_GPL(iscsi_put_task); * @task: iscsi cmd task * @state: state to complete task with * - * Must be called with session lock. + * Must be called with session back_lock. */ static void iscsi_complete_task(struct iscsi_task *task, int state) { @@ -481,7 +554,8 @@ static void iscsi_complete_task(struct iscsi_task *task, int state) task->itt, task->state, task->sc); if (task->state == ISCSI_TASK_COMPLETED || task->state == ISCSI_TASK_ABRT_TMF || - task->state == ISCSI_TASK_ABRT_SESS_RECOV) + task->state == ISCSI_TASK_ABRT_SESS_RECOV || + task->state == ISCSI_TASK_REQUEUE_SCSIQ) return; WARN_ON_ONCE(task->state == ISCSI_TASK_FREE); task->state = state; @@ -499,8 +573,33 @@ static void iscsi_complete_task(struct iscsi_task *task, int state) __iscsi_put_task(task); } +/** + * iscsi_complete_scsi_task - finish scsi task normally + * @task: iscsi task for scsi cmd + * @exp_cmdsn: expected cmd sn in cpu format + * @max_cmdsn: max cmd sn in cpu format + * + * This is used when drivers do not need or cannot perform + * lower level pdu processing. + * + * Called with session back_lock + */ +void iscsi_complete_scsi_task(struct iscsi_task *task, + uint32_t exp_cmdsn, uint32_t max_cmdsn) +{ + struct iscsi_conn *conn = task->conn; + + ISCSI_DBG_SESSION(conn->session, "[itt 0x%x]\n", task->itt); + + conn->last_recv = jiffies; + __iscsi_update_cmdsn(conn->session, exp_cmdsn, max_cmdsn); + iscsi_complete_task(task, ISCSI_TASK_COMPLETED); +} +EXPORT_SYMBOL_GPL(iscsi_complete_scsi_task); + + /* - * session lock must be held and if not called for a task that is + * session back_lock must be held and if not called for a task that is * still pending or from the xmit thread, then xmit thread must * be suspended. */ @@ -540,7 +639,10 @@ static void fail_scsi_task(struct iscsi_task *task, int err) scsi_in(sc)->resid = scsi_in(sc)->length; } + /* regular RX path uses back_lock */ + spin_lock_bh(&conn->session->back_lock); iscsi_complete_task(task, state); + spin_unlock_bh(&conn->session->back_lock); } static int iscsi_prep_mgmt_task(struct iscsi_conn *conn, @@ -549,12 +651,12 @@ static int iscsi_prep_mgmt_task(struct iscsi_conn *conn, struct iscsi_session *session = conn->session; struct iscsi_hdr *hdr = task->hdr; struct iscsi_nopout *nop = (struct iscsi_nopout *)hdr; + uint8_t opcode = hdr->opcode & ISCSI_OPCODE_MASK; if (conn->session->state == ISCSI_STATE_LOGGING_OUT) return -ENOTCONN; - if (hdr->opcode != (ISCSI_OP_LOGIN | ISCSI_OP_IMMEDIATE) && - hdr->opcode != (ISCSI_OP_TEXT | ISCSI_OP_IMMEDIATE)) + if (opcode != ISCSI_OP_LOGIN && opcode != ISCSI_OP_TEXT) nop->exp_statsn = cpu_to_be32(conn->exp_statsn); /* * pre-format CmdSN for outgoing PDU. @@ -562,9 +664,12 @@ static int iscsi_prep_mgmt_task(struct iscsi_conn *conn, nop->cmdsn = cpu_to_be32(session->cmdsn); if (hdr->itt != RESERVED_ITT) { /* - * TODO: We always use immediate, so we never hit this. + * TODO: We always use immediate for normal session pdus. * If we start to send tmfs or nops as non-immediate then * we should start checking the cmdsn numbers for mgmt tasks. + * + * During discovery sessions iscsid sends TEXT as non immediate, + * but we always only send one PDU at a time. */ if (conn->c_stage == ISCSI_CONN_STARTED && !(hdr->opcode & ISCSI_OP_IMMEDIATE)) { @@ -592,29 +697,35 @@ __iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, { struct iscsi_session *session = conn->session; struct iscsi_host *ihost = shost_priv(session->host); + uint8_t opcode = hdr->opcode & ISCSI_OPCODE_MASK; struct iscsi_task *task; itt_t itt; if (session->state == ISCSI_STATE_TERMINATE) return NULL; - if (hdr->opcode == (ISCSI_OP_LOGIN | ISCSI_OP_IMMEDIATE) || - hdr->opcode == (ISCSI_OP_TEXT | ISCSI_OP_IMMEDIATE)) + if (opcode == ISCSI_OP_LOGIN || opcode == ISCSI_OP_TEXT) { /* * Login and Text are sent serially, in * request-followed-by-response sequence. * Same task can be used. Same ITT must be used. * Note that login_task is preallocated at conn_create(). */ + if (conn->login_task->state != ISCSI_TASK_FREE) { + iscsi_conn_printk(KERN_ERR, conn, "Login/Text in " + "progress. Cannot start new task.\n"); + return NULL; + } + task = conn->login_task; - else { + } else { if (session->state != ISCSI_STATE_LOGGED_IN) return NULL; BUG_ON(conn->c_stage == ISCSI_CONN_INITIAL_STAGE); BUG_ON(conn->c_stage == ISCSI_CONN_STOPPED); - if (!__kfifo_get(session->cmdpool.queue, + if (!kfifo_out(&session->cmdpool.queue, (void*)&task, sizeof(void*))) return NULL; } @@ -669,7 +780,10 @@ __iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, return task; free_task: + /* regular RX path uses back_lock */ + spin_lock_bh(&session->back_lock); __iscsi_put_task(task); + spin_unlock_bh(&session->back_lock); return NULL; } @@ -680,10 +794,10 @@ int iscsi_conn_send_pdu(struct iscsi_cls_conn *cls_conn, struct iscsi_hdr *hdr, struct iscsi_session *session = conn->session; int err = 0; - spin_lock_bh(&session->lock); + spin_lock_bh(&session->frwd_lock); if (!__iscsi_conn_send_pdu(conn, hdr, data, data_size)) err = -EPERM; - spin_unlock_bh(&session->lock); + spin_unlock_bh(&session->frwd_lock); return err; } EXPORT_SYMBOL_GPL(iscsi_conn_send_pdu); @@ -703,7 +817,7 @@ static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr, struct iscsi_task *task, char *data, int datalen) { - struct iscsi_cmd_rsp *rhdr = (struct iscsi_cmd_rsp *)hdr; + struct iscsi_scsi_rsp *rhdr = (struct iscsi_scsi_rsp *)hdr; struct iscsi_session *session = conn->session; struct scsi_cmnd *sc = task->sc; @@ -712,6 +826,33 @@ static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr, sc->result = (DID_OK << 16) | rhdr->cmd_status; + if (task->protected) { + sector_t sector; + u8 ascq; + + /** + * Transports that didn't implement check_protection + * callback but still published T10-PI support to scsi-mid + * deserve this BUG_ON. + **/ + BUG_ON(!session->tt->check_protection); + + ascq = session->tt->check_protection(task, §or); + if (ascq) { + sc->result = DRIVER_SENSE << 24 | + SAM_STAT_CHECK_CONDITION; + scsi_build_sense_buffer(1, sc->sense_buffer, + ILLEGAL_REQUEST, 0x10, ascq); + sc->sense_buffer[7] = 0xc; /* Additional sense length */ + sc->sense_buffer[8] = 0; /* Information desc type */ + sc->sense_buffer[9] = 0xa; /* Additional desc length */ + sc->sense_buffer[10] = 0x80; /* Validity bit */ + + put_unaligned_be64(sector, &sc->sense_buffer[12]); + goto out; + } + } + if (rhdr->response != ISCSI_STATUS_CMD_COMPLETED) { sc->result = DID_ERROR << 16; goto out; @@ -841,7 +982,7 @@ static void iscsi_send_nopout(struct iscsi_conn *conn, struct iscsi_nopin *rhdr) hdr.flags = ISCSI_FLAG_CMD_FINAL; if (rhdr) { - memcpy(hdr.lun, rhdr->lun, 8); + hdr.lun = rhdr->lun; hdr.ttt = rhdr->ttt; hdr.itt = RESERVED_ITT; } else @@ -857,27 +998,107 @@ static void iscsi_send_nopout(struct iscsi_conn *conn, struct iscsi_nopin *rhdr) } } +static int iscsi_nop_out_rsp(struct iscsi_task *task, + struct iscsi_nopin *nop, char *data, int datalen) +{ + struct iscsi_conn *conn = task->conn; + int rc = 0; + + if (conn->ping_task != task) { + /* + * If this is not in response to one of our + * nops then it must be from userspace. + */ + if (iscsi_recv_pdu(conn->cls_conn, (struct iscsi_hdr *)nop, + data, datalen)) + rc = ISCSI_ERR_CONN_FAILED; + } else + mod_timer(&conn->transport_timer, jiffies + conn->recv_timeout); + iscsi_complete_task(task, ISCSI_TASK_COMPLETED); + return rc; +} + static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr, char *data, int datalen) { struct iscsi_reject *reject = (struct iscsi_reject *)hdr; struct iscsi_hdr rejected_pdu; + int opcode, rc = 0; conn->exp_statsn = be32_to_cpu(reject->statsn) + 1; - if (reject->reason == ISCSI_REASON_DATA_DIGEST_ERROR) { - if (ntoh24(reject->dlength) > datalen) - return ISCSI_ERR_PROTO; + if (ntoh24(reject->dlength) > datalen || + ntoh24(reject->dlength) < sizeof(struct iscsi_hdr)) { + iscsi_conn_printk(KERN_ERR, conn, "Cannot handle rejected " + "pdu. Invalid data length (pdu dlength " + "%u, datalen %d\n", ntoh24(reject->dlength), + datalen); + return ISCSI_ERR_PROTO; + } + memcpy(&rejected_pdu, data, sizeof(struct iscsi_hdr)); + opcode = rejected_pdu.opcode & ISCSI_OPCODE_MASK; - if (ntoh24(reject->dlength) >= sizeof(struct iscsi_hdr)) { - memcpy(&rejected_pdu, data, sizeof(struct iscsi_hdr)); - iscsi_conn_printk(KERN_ERR, conn, - "pdu (op 0x%x) rejected " - "due to DataDigest error.\n", - rejected_pdu.opcode); + switch (reject->reason) { + case ISCSI_REASON_DATA_DIGEST_ERROR: + iscsi_conn_printk(KERN_ERR, conn, + "pdu (op 0x%x itt 0x%x) rejected " + "due to DataDigest error.\n", + opcode, rejected_pdu.itt); + break; + case ISCSI_REASON_IMM_CMD_REJECT: + iscsi_conn_printk(KERN_ERR, conn, + "pdu (op 0x%x itt 0x%x) rejected. Too many " + "immediate commands.\n", + opcode, rejected_pdu.itt); + /* + * We only send one TMF at a time so if the target could not + * handle it, then it should get fixed (RFC mandates that + * a target can handle one immediate TMF per conn). + * + * For nops-outs, we could have sent more than one if + * the target is sending us lots of nop-ins + */ + if (opcode != ISCSI_OP_NOOP_OUT) + return 0; + + if (rejected_pdu.itt == cpu_to_be32(ISCSI_RESERVED_TAG)) { + /* + * nop-out in response to target's nop-out rejected. + * Just resend. + */ + /* In RX path we are under back lock */ + spin_unlock(&conn->session->back_lock); + spin_lock(&conn->session->frwd_lock); + iscsi_send_nopout(conn, + (struct iscsi_nopin*)&rejected_pdu); + spin_unlock(&conn->session->frwd_lock); + spin_lock(&conn->session->back_lock); + } else { + struct iscsi_task *task; + /* + * Our nop as ping got dropped. We know the target + * and transport are ok so just clean up + */ + task = iscsi_itt_to_task(conn, rejected_pdu.itt); + if (!task) { + iscsi_conn_printk(KERN_ERR, conn, + "Invalid pdu reject. Could " + "not lookup rejected task.\n"); + rc = ISCSI_ERR_BAD_ITT; + } else + rc = iscsi_nop_out_rsp(task, + (struct iscsi_nopin*)&rejected_pdu, + NULL, 0); } + break; + default: + iscsi_conn_printk(KERN_ERR, conn, + "pdu (op 0x%x itt 0x%x) rejected. Reason " + "code 0x%x\n", rejected_pdu.opcode, + rejected_pdu.itt, reject->reason); + break; } - return 0; + return rc; } /** @@ -888,7 +1109,7 @@ static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr, * This should be used for mgmt tasks like login and nops, or if * the LDD's itt space does not include the session age. * - * The session lock must be held. + * The session back_lock must be held. */ struct iscsi_task *iscsi_itt_to_task(struct iscsi_conn *conn, itt_t itt) { @@ -917,7 +1138,7 @@ EXPORT_SYMBOL_GPL(iscsi_itt_to_task); * @datalen: len of data buffer * * Completes pdu processing by freeing any resources allocated at - * queuecommand or send generic. session lock must be held and verify + * queuecommand or send generic. session back_lock must be held and verify * itt must have been called. */ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, @@ -954,7 +1175,12 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, if (hdr->ttt == cpu_to_be32(ISCSI_RESERVED_TAG)) break; + /* In RX path we are under back lock */ + spin_unlock(&session->back_lock); + spin_lock(&session->frwd_lock); iscsi_send_nopout(conn, (struct iscsi_nopin*)hdr); + spin_unlock(&session->frwd_lock); + spin_lock(&session->back_lock); break; case ISCSI_OP_REJECT: rc = iscsi_handle_reject(conn, hdr, data, datalen); @@ -1038,15 +1264,8 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, } conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1; - if (conn->ping_task != task) - /* - * If this is not in response to one of our - * nops then it must be from userspace. - */ - goto recv_pdu; - - mod_timer(&conn->transport_timer, jiffies + conn->recv_timeout); - iscsi_complete_task(task, ISCSI_TASK_COMPLETED); + rc = iscsi_nop_out_rsp(task, (struct iscsi_nopin*)hdr, + data, datalen); break; default: rc = ISCSI_ERR_BAD_OPCODE; @@ -1068,9 +1287,9 @@ int iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, { int rc; - spin_lock(&conn->session->lock); + spin_lock(&conn->session->back_lock); rc = __iscsi_complete_pdu(conn, hdr, data, datalen); - spin_unlock(&conn->session->lock); + spin_unlock(&conn->session->back_lock); return rc; } EXPORT_SYMBOL_GPL(iscsi_complete_pdu); @@ -1114,7 +1333,7 @@ EXPORT_SYMBOL_GPL(iscsi_verify_itt); * * This should be used for cmd tasks. * - * The session lock must be held. + * The session back_lock must be held. */ struct iscsi_task *iscsi_itt_to_ctask(struct iscsi_conn *conn, itt_t itt) { @@ -1143,17 +1362,16 @@ void iscsi_session_failure(struct iscsi_session *session, { struct iscsi_conn *conn; struct device *dev; - unsigned long flags; - spin_lock_irqsave(&session->lock, flags); + spin_lock_bh(&session->frwd_lock); conn = session->leadconn; if (session->state == ISCSI_STATE_TERMINATE || !conn) { - spin_unlock_irqrestore(&session->lock, flags); + spin_unlock_bh(&session->frwd_lock); return; } dev = get_device(&conn->cls_conn->dev); - spin_unlock_irqrestore(&session->lock, flags); + spin_unlock_bh(&session->frwd_lock); if (!dev) return; /* @@ -1172,17 +1390,16 @@ EXPORT_SYMBOL_GPL(iscsi_session_failure); void iscsi_conn_failure(struct iscsi_conn *conn, enum iscsi_err err) { struct iscsi_session *session = conn->session; - unsigned long flags; - spin_lock_irqsave(&session->lock, flags); + spin_lock_bh(&session->frwd_lock); if (session->state == ISCSI_STATE_FAILED) { - spin_unlock_irqrestore(&session->lock, flags); + spin_unlock_bh(&session->frwd_lock); return; } if (conn->stop_stage == 0) session->state = ISCSI_STATE_FAILED; - spin_unlock_irqrestore(&session->lock, flags); + spin_unlock_bh(&session->frwd_lock); set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx); set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx); @@ -1212,16 +1429,22 @@ static int iscsi_xmit_task(struct iscsi_conn *conn) struct iscsi_task *task = conn->task; int rc; + if (test_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx)) + return -ENODATA; + __iscsi_get_task(task); - spin_unlock_bh(&conn->session->lock); + spin_unlock_bh(&conn->session->frwd_lock); rc = conn->session->tt->xmit_task(task); - spin_lock_bh(&conn->session->lock); + spin_lock_bh(&conn->session->frwd_lock); if (!rc) { /* done with this task */ task->last_xfer = jiffies; conn->task = NULL; } + /* regular RX path uses back_lock */ + spin_lock(&conn->session->back_lock); __iscsi_put_task(task); + spin_unlock(&conn->session->back_lock); return rc; } @@ -1230,7 +1453,7 @@ static int iscsi_xmit_task(struct iscsi_conn *conn) * @task: task to requeue * * LLDs that need to run a task from the session workqueue should call - * this. The session lock must be held. This should only be called + * this. The session frwd_lock must be held. This should only be called * by software drivers. */ void iscsi_requeue_task(struct iscsi_task *task) @@ -1258,19 +1481,20 @@ EXPORT_SYMBOL_GPL(iscsi_requeue_task); **/ static int iscsi_data_xmit(struct iscsi_conn *conn) { + struct iscsi_task *task; int rc = 0; - spin_lock_bh(&conn->session->lock); - if (unlikely(conn->suspend_tx)) { + spin_lock_bh(&conn->session->frwd_lock); + if (test_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx)) { ISCSI_DBG_SESSION(conn->session, "Tx suspended!\n"); - spin_unlock_bh(&conn->session->lock); + spin_unlock_bh(&conn->session->frwd_lock); return -ENODATA; } if (conn->task) { rc = iscsi_xmit_task(conn); if (rc) - goto again; + goto done; } /* @@ -1284,22 +1508,22 @@ check_mgmt: struct iscsi_task, running); list_del_init(&conn->task->running); if (iscsi_prep_mgmt_task(conn, conn->task)) { + /* regular RX path uses back_lock */ + spin_lock_bh(&conn->session->back_lock); __iscsi_put_task(conn->task); + spin_unlock_bh(&conn->session->back_lock); conn->task = NULL; continue; } rc = iscsi_xmit_task(conn); if (rc) - goto again; + goto done; } /* process pending command queue */ while (!list_empty(&conn->cmdqueue)) { - if (conn->tmf_state == TMF_QUEUED) - break; - - conn->task = list_entry(conn->cmdqueue.next, - struct iscsi_task, running); + conn->task = list_entry(conn->cmdqueue.next, struct iscsi_task, + running); list_del_init(&conn->task->running); if (conn->session->state == ISCSI_STATE_LOGGING_OUT) { fail_scsi_task(conn->task, DID_IMM_RETRY); @@ -1307,18 +1531,18 @@ check_mgmt: } rc = iscsi_prep_scsi_cmd_pdu(conn->task); if (rc) { - if (rc == -ENOMEM) { + if (rc == -ENOMEM || rc == -EACCES) { list_add_tail(&conn->task->running, &conn->cmdqueue); conn->task = NULL; - goto again; + goto done; } else fail_scsi_task(conn->task, DID_ABORT); continue; } rc = iscsi_xmit_task(conn); if (rc) - goto again; + goto done; /* * we could continuously get new task requests so * we need to check the mgmt queue for nops that need to @@ -1329,32 +1553,31 @@ check_mgmt: } while (!list_empty(&conn->requeue)) { - if (conn->session->fast_abort && conn->tmf_state != TMF_INITIAL) - break; - /* * we always do fastlogout - conn stop code will clean up. */ if (conn->session->state == ISCSI_STATE_LOGGING_OUT) break; - conn->task = list_entry(conn->requeue.next, - struct iscsi_task, running); + task = list_entry(conn->requeue.next, struct iscsi_task, + running); + if (iscsi_check_tmf_restrictions(task, ISCSI_OP_SCSI_DATA_OUT)) + break; + + conn->task = task; list_del_init(&conn->task->running); conn->task->state = ISCSI_TASK_RUNNING; rc = iscsi_xmit_task(conn); if (rc) - goto again; + goto done; if (!list_empty(&conn->mgmtqueue)) goto check_mgmt; } - spin_unlock_bh(&conn->session->lock); + spin_unlock_bh(&conn->session->frwd_lock); return -ENODATA; -again: - if (unlikely(conn->suspend_tx)) - rc = -ENODATA; - spin_unlock_bh(&conn->session->lock); +done: + spin_unlock_bh(&conn->session->frwd_lock); return rc; } @@ -1376,7 +1599,7 @@ static inline struct iscsi_task *iscsi_alloc_task(struct iscsi_conn *conn, { struct iscsi_task *task; - if (!__kfifo_get(conn->session->cmdpool.queue, + if (!kfifo_out(&conn->session->cmdpool.queue, (void *) &task, sizeof(void *))) return NULL; @@ -1390,6 +1613,7 @@ static inline struct iscsi_task *iscsi_alloc_task(struct iscsi_conn *conn, task->have_checked_conn = false; task->last_timeout = jiffies; task->last_xfer = jiffies; + task->protected = false; INIT_LIST_HEAD(&task->running); return task; } @@ -1407,27 +1631,23 @@ enum { FAILURE_SESSION_NOT_READY, }; -int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *)) +int iscsi_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *sc) { struct iscsi_cls_session *cls_session; - struct Scsi_Host *host; struct iscsi_host *ihost; int reason = 0; struct iscsi_session *session; struct iscsi_conn *conn; struct iscsi_task *task = NULL; - sc->scsi_done = done; sc->result = 0; sc->SCp.ptr = NULL; - host = sc->device->host; ihost = shost_priv(host); - spin_unlock(host->host_lock); cls_session = starget_to_session(scsi_target(sc->device)); session = cls_session->dd_data; - spin_lock(&session->lock); + spin_lock_bh(&session->frwd_lock); reason = iscsi_session_chkready(cls_session); if (reason) { @@ -1474,6 +1694,12 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *)) goto fault; } + if (test_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx)) { + reason = FAILURE_SESSION_IN_RECOVERY; + sc->result = DID_REQUEUE; + goto fault; + } + if (iscsi_check_cmdsn_window_closed(conn)) { reason = FAILURE_WINDOW_CLOSED; goto reject; @@ -1488,7 +1714,7 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *)) if (!ihost->workq) { reason = iscsi_prep_scsi_cmd_pdu(task); if (reason) { - if (reason == -ENOMEM) { + if (reason == -ENOMEM || reason == -EACCES) { reason = FAILURE_OOM; goto prepd_reject; } else { @@ -1497,6 +1723,7 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *)) } } if (session->tt->xmit_task(task)) { + session->cmdsn--; reason = FAILURE_SESSION_NOT_READY; goto prepd_reject; } @@ -1506,25 +1733,21 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *)) } session->queued_cmdsn++; - spin_unlock(&session->lock); - spin_lock(host->host_lock); + spin_unlock_bh(&session->frwd_lock); return 0; prepd_reject: - sc->scsi_done = NULL; - iscsi_complete_task(task, ISCSI_TASK_COMPLETED); + iscsi_complete_task(task, ISCSI_TASK_REQUEUE_SCSIQ); reject: - spin_unlock(&session->lock); + spin_unlock_bh(&session->frwd_lock); ISCSI_DBG_SESSION(session, "cmd 0x%x rejected (%d)\n", sc->cmnd[0], reason); - spin_lock(host->host_lock); return SCSI_MLQUEUE_TARGET_BUSY; prepd_fault: - sc->scsi_done = NULL; - iscsi_complete_task(task, ISCSI_TASK_COMPLETED); + iscsi_complete_task(task, ISCSI_TASK_REQUEUE_SCSIQ); fault: - spin_unlock(&session->lock); + spin_unlock_bh(&session->frwd_lock); ISCSI_DBG_SESSION(session, "iscsi: cmd 0x%x is not queued (%d)\n", sc->cmnd[0], reason); if (!scsi_bidi_cmnd(sc)) @@ -1533,15 +1756,26 @@ fault: scsi_out(sc)->resid = scsi_out(sc)->length; scsi_in(sc)->resid = scsi_in(sc)->length; } - done(sc); - spin_lock(host->host_lock); + sc->scsi_done(sc); return 0; } EXPORT_SYMBOL_GPL(iscsi_queuecommand); -int iscsi_change_queue_depth(struct scsi_device *sdev, int depth) +int iscsi_change_queue_depth(struct scsi_device *sdev, int depth, int reason) { - scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth); + switch (reason) { + case SCSI_QDEPTH_DEFAULT: + scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth); + break; + case SCSI_QDEPTH_QFULL: + scsi_track_queue_full(sdev, depth); + break; + case SCSI_QDEPTH_RAMP_UP: + scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth); + break; + default: + return -EOPNOTSUPP; + } return sdev->queue_depth; } EXPORT_SYMBOL_GPL(iscsi_change_queue_depth); @@ -1556,85 +1790,19 @@ int iscsi_target_alloc(struct scsi_target *starget) } EXPORT_SYMBOL_GPL(iscsi_target_alloc); -void iscsi_session_recovery_timedout(struct iscsi_cls_session *cls_session) -{ - struct iscsi_session *session = cls_session->dd_data; - - spin_lock_bh(&session->lock); - if (session->state != ISCSI_STATE_LOGGED_IN) { - session->state = ISCSI_STATE_RECOVERY_FAILED; - if (session->leadconn) - wake_up(&session->leadconn->ehwait); - } - spin_unlock_bh(&session->lock); -} -EXPORT_SYMBOL_GPL(iscsi_session_recovery_timedout); - -int iscsi_eh_target_reset(struct scsi_cmnd *sc) -{ - struct iscsi_cls_session *cls_session; - struct iscsi_session *session; - struct iscsi_conn *conn; - - cls_session = starget_to_session(scsi_target(sc->device)); - session = cls_session->dd_data; - conn = session->leadconn; - - mutex_lock(&session->eh_mutex); - spin_lock_bh(&session->lock); - if (session->state == ISCSI_STATE_TERMINATE) { -failed: - ISCSI_DBG_EH(session, - "failing target reset: Could not log back into " - "target [age %d]\n", - session->age); - spin_unlock_bh(&session->lock); - mutex_unlock(&session->eh_mutex); - return FAILED; - } - - spin_unlock_bh(&session->lock); - mutex_unlock(&session->eh_mutex); - /* - * we drop the lock here but the leadconn cannot be destoyed while - * we are in the scsi eh - */ - iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); - - ISCSI_DBG_EH(session, "wait for relogin\n"); - wait_event_interruptible(conn->ehwait, - session->state == ISCSI_STATE_TERMINATE || - session->state == ISCSI_STATE_LOGGED_IN || - session->state == ISCSI_STATE_RECOVERY_FAILED); - if (signal_pending(current)) - flush_signals(current); - - mutex_lock(&session->eh_mutex); - spin_lock_bh(&session->lock); - if (session->state == ISCSI_STATE_LOGGED_IN) { - ISCSI_DBG_EH(session, - "target reset succeeded\n"); - } else - goto failed; - spin_unlock_bh(&session->lock); - mutex_unlock(&session->eh_mutex); - return SUCCESS; -} -EXPORT_SYMBOL_GPL(iscsi_eh_target_reset); - static void iscsi_tmf_timedout(unsigned long data) { struct iscsi_conn *conn = (struct iscsi_conn *)data; struct iscsi_session *session = conn->session; - spin_lock(&session->lock); + spin_lock(&session->frwd_lock); if (conn->tmf_state == TMF_QUEUED) { conn->tmf_state = TMF_TIMEDOUT; ISCSI_DBG_EH(session, "tmf timedout\n"); /* unblock eh_abort() */ wake_up(&conn->ehwait); } - spin_unlock(&session->lock); + spin_unlock(&session->frwd_lock); } static int iscsi_exec_task_mgmt_fn(struct iscsi_conn *conn, @@ -1647,10 +1815,10 @@ static int iscsi_exec_task_mgmt_fn(struct iscsi_conn *conn, task = __iscsi_conn_send_pdu(conn, (struct iscsi_hdr *)hdr, NULL, 0); if (!task) { - spin_unlock_bh(&session->lock); + spin_unlock_bh(&session->frwd_lock); + iscsi_conn_printk(KERN_ERR, conn, "Could not send TMF.\n"); iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); - spin_lock_bh(&session->lock); - ISCSI_DBG_EH(session, "tmf exec failure\n"); + spin_lock_bh(&session->frwd_lock); return -EPERM; } conn->tmfcmd_pdus_cnt++; @@ -1660,7 +1828,7 @@ static int iscsi_exec_task_mgmt_fn(struct iscsi_conn *conn, add_timer(&conn->tmf_timer); ISCSI_DBG_EH(session, "tmf set timeout\n"); - spin_unlock_bh(&session->lock); + spin_unlock_bh(&session->frwd_lock); mutex_unlock(&session->eh_mutex); /* @@ -1679,7 +1847,7 @@ static int iscsi_exec_task_mgmt_fn(struct iscsi_conn *conn, del_timer_sync(&conn->tmf_timer); mutex_lock(&session->eh_mutex); - spin_lock_bh(&session->lock); + spin_lock_bh(&session->frwd_lock); /* if the session drops it will clean up the task */ if (age != session->age || session->state != ISCSI_STATE_LOGGED_IN) @@ -1712,6 +1880,33 @@ static void fail_scsi_tasks(struct iscsi_conn *conn, unsigned lun, } } +/** + * iscsi_suspend_queue - suspend iscsi_queuecommand + * @conn: iscsi conn to stop queueing IO on + * + * This grabs the session frwd_lock to make sure no one is in + * xmit_task/queuecommand, and then sets suspend to prevent + * new commands from being queued. This only needs to be called + * by offload drivers that need to sync a path like ep disconnect + * with the iscsi_queuecommand/xmit_task. To start IO again libiscsi + * will call iscsi_start_tx and iscsi_unblock_session when in FFP. + */ +void iscsi_suspend_queue(struct iscsi_conn *conn) +{ + spin_lock_bh(&conn->session->frwd_lock); + set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx); + spin_unlock_bh(&conn->session->frwd_lock); +} +EXPORT_SYMBOL_GPL(iscsi_suspend_queue); + +/** + * iscsi_suspend_tx - suspend iscsi_data_xmit + * @conn: iscsi conn tp stop processing IO on. + * + * This function sets the suspend bit to prevent iscsi_data_xmit + * from sending new IO, and if work is queued on the xmit thread + * it will wait for it to be completed. + */ void iscsi_suspend_tx(struct iscsi_conn *conn) { struct Scsi_Host *shost = conn->session->host; @@ -1748,17 +1943,28 @@ static int iscsi_has_ping_timed_out(struct iscsi_conn *conn) static enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *sc) { enum blk_eh_timer_return rc = BLK_EH_NOT_HANDLED; - struct iscsi_task *task = NULL; + struct iscsi_task *task = NULL, *running_task; struct iscsi_cls_session *cls_session; struct iscsi_session *session; struct iscsi_conn *conn; + int i; cls_session = starget_to_session(scsi_target(sc->device)); session = cls_session->dd_data; ISCSI_DBG_EH(session, "scsi cmd %p timedout\n", sc); - spin_lock(&session->lock); + spin_lock(&session->frwd_lock); + task = (struct iscsi_task *)sc->SCp.ptr; + if (!task) { + /* + * Raced with completion. Blk layer has taken ownership + * so let timeout code complete it now. + */ + rc = BLK_EH_HANDLED; + goto done; + } + if (session->state != ISCSI_STATE_LOGGED_IN) { /* * We are probably in the middle of iscsi recovery so let @@ -1775,9 +1981,6 @@ static enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *sc) goto done; } - task = (struct iscsi_task *)sc->SCp.ptr; - if (!task) - goto done; /* * If we have sent (at least queued to the network layer) a pdu or * recvd one for the task since the last timeout ask for @@ -1785,10 +1988,10 @@ static enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *sc) * we can check if it is the task or connection when we send the * nop as a ping. */ - if (time_after_eq(task->last_xfer, task->last_timeout)) { + if (time_after(task->last_xfer, task->last_timeout)) { ISCSI_DBG_EH(session, "Command making progress. Asking " "scsi-ml for more time to complete. " - "Last data recv at %lu. Last timeout was at " + "Last data xfer at %lu. Last timeout was at " "%lu\n.", task->last_xfer, task->last_timeout); task->have_checked_conn = false; rc = BLK_EH_RESET_TIMER; @@ -1806,6 +2009,43 @@ static enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *sc) goto done; } + for (i = 0; i < conn->session->cmds_max; i++) { + running_task = conn->session->cmds[i]; + if (!running_task->sc || running_task == task || + running_task->state != ISCSI_TASK_RUNNING) + continue; + + /* + * Only check if cmds started before this one have made + * progress, or this could never fail + */ + if (time_after(running_task->sc->jiffies_at_alloc, + task->sc->jiffies_at_alloc)) + continue; + + if (time_after(running_task->last_xfer, task->last_timeout)) { + /* + * This task has not made progress, but a task + * started before us has transferred data since + * we started/last-checked. We could be queueing + * too many tasks or the LU is bad. + * + * If the device is bad the cmds ahead of us on + * other devs will complete, and this loop will + * eventually fail starting the scsi eh. + */ + ISCSI_DBG_EH(session, "Command has not made progress " + "but commands ahead of it have. " + "Asking scsi-ml for more time to " + "complete. Our last xfer vs running task " + "last xfer %lu/%lu. Last check %lu.\n", + task->last_xfer, running_task->last_xfer, + task->last_timeout); + rc = BLK_EH_RESET_TIMER; + goto done; + } + } + /* Assumes nop timeout is shorter than scsi cmd timeout */ if (task->have_checked_conn) goto done; @@ -1828,7 +2068,7 @@ static enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *sc) done: if (task) task->last_timeout = jiffies; - spin_unlock(&session->lock); + spin_unlock(&session->frwd_lock); ISCSI_DBG_EH(session, "return %s\n", rc == BLK_EH_RESET_TIMER ? "timer reset" : "nh"); return rc; @@ -1840,7 +2080,7 @@ static void iscsi_check_transport_timeouts(unsigned long data) struct iscsi_session *session = conn->session; unsigned long recv_timeout, next_timeout = 0, last_recv; - spin_lock(&session->lock); + spin_lock(&session->frwd_lock); if (session->state != ISCSI_STATE_LOGGED_IN) goto done; @@ -1857,7 +2097,7 @@ static void iscsi_check_transport_timeouts(unsigned long data) "last ping %lu, now %lu\n", conn->ping_timeout, conn->recv_timeout, last_recv, conn->last_ping, jiffies); - spin_unlock(&session->lock); + spin_unlock(&session->frwd_lock); iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); return; } @@ -1873,7 +2113,7 @@ static void iscsi_check_transport_timeouts(unsigned long data) ISCSI_DBG_CONN(conn, "Setting next tmo %lu\n", next_timeout); mod_timer(&conn->transport_timer, next_timeout); done: - spin_unlock(&session->lock); + spin_unlock(&session->frwd_lock); } static void iscsi_prep_abort_task_pdu(struct iscsi_task *task, @@ -1883,7 +2123,7 @@ static void iscsi_prep_abort_task_pdu(struct iscsi_task *task, hdr->opcode = ISCSI_OP_SCSI_TMFUNC | ISCSI_OP_IMMEDIATE; hdr->flags = ISCSI_TM_FUNC_ABORT_TASK & ISCSI_FLAG_TM_FUNC_MASK; hdr->flags |= ISCSI_FLAG_CMD_FINAL; - memcpy(hdr->lun, task->lun, sizeof(hdr->lun)); + hdr->lun = task->lun; hdr->rtt = task->hdr_itt; hdr->refcmdsn = task->cmdsn; } @@ -1903,7 +2143,7 @@ int iscsi_eh_abort(struct scsi_cmnd *sc) ISCSI_DBG_EH(session, "aborting sc %p\n", sc); mutex_lock(&session->eh_mutex); - spin_lock_bh(&session->lock); + spin_lock_bh(&session->frwd_lock); /* * if session was ISCSI_STATE_IN_RECOVERY then we may not have * got the command. @@ -1911,7 +2151,7 @@ int iscsi_eh_abort(struct scsi_cmnd *sc) if (!sc->SCp.ptr) { ISCSI_DBG_EH(session, "sc never reached iscsi layer or " "it completed.\n"); - spin_unlock_bh(&session->lock); + spin_unlock_bh(&session->frwd_lock); mutex_unlock(&session->eh_mutex); return SUCCESS; } @@ -1922,7 +2162,7 @@ int iscsi_eh_abort(struct scsi_cmnd *sc) */ if (!session->leadconn || session->state != ISCSI_STATE_LOGGED_IN || sc->SCp.phase != session->age) { - spin_unlock_bh(&session->lock); + spin_unlock_bh(&session->frwd_lock); mutex_unlock(&session->eh_mutex); ISCSI_DBG_EH(session, "failing abort due to dropped " "session.\n"); @@ -1963,7 +2203,7 @@ int iscsi_eh_abort(struct scsi_cmnd *sc) switch (conn->tmf_state) { case TMF_SUCCESS: - spin_unlock_bh(&session->lock); + spin_unlock_bh(&session->frwd_lock); /* * stop tx side incase the target had sent a abort rsp but * the initiator was still writing out data. @@ -1974,19 +2214,21 @@ int iscsi_eh_abort(struct scsi_cmnd *sc) * good and have never sent us a successful tmf response * then sent more data for the cmd. */ - spin_lock(&session->lock); + spin_lock_bh(&session->frwd_lock); fail_scsi_task(task, DID_ABORT); conn->tmf_state = TMF_INITIAL; - spin_unlock(&session->lock); + memset(hdr, 0, sizeof(*hdr)); + spin_unlock_bh(&session->frwd_lock); iscsi_start_tx(conn); goto success_unlocked; case TMF_TIMEDOUT: - spin_unlock_bh(&session->lock); - iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); + spin_unlock_bh(&session->frwd_lock); + iscsi_conn_failure(conn, ISCSI_ERR_SCSI_EH_SESSION_RST); goto failed_unlocked; case TMF_NOT_FOUND: if (!sc->SCp.ptr) { conn->tmf_state = TMF_INITIAL; + memset(hdr, 0, sizeof(*hdr)); /* task completed before tmf abort response */ ISCSI_DBG_EH(session, "sc completed while abort in " "progress\n"); @@ -1999,7 +2241,7 @@ int iscsi_eh_abort(struct scsi_cmnd *sc) } success: - spin_unlock_bh(&session->lock); + spin_unlock_bh(&session->frwd_lock); success_unlocked: ISCSI_DBG_EH(session, "abort success [sc %p itt 0x%x]\n", sc, task->itt); @@ -2007,7 +2249,7 @@ success_unlocked: return SUCCESS; failed: - spin_unlock_bh(&session->lock); + spin_unlock_bh(&session->frwd_lock); failed_unlocked: ISCSI_DBG_EH(session, "abort failed [sc %p itt 0x%x]\n", sc, task ? task->itt : 0); @@ -2022,7 +2264,7 @@ static void iscsi_prep_lun_reset_pdu(struct scsi_cmnd *sc, struct iscsi_tm *hdr) hdr->opcode = ISCSI_OP_SCSI_TMFUNC | ISCSI_OP_IMMEDIATE; hdr->flags = ISCSI_TM_FUNC_LOGICAL_UNIT_RESET & ISCSI_FLAG_TM_FUNC_MASK; hdr->flags |= ISCSI_FLAG_CMD_FINAL; - int_to_scsilun(sc->device->lun, (struct scsi_lun *)hdr->lun); + int_to_scsilun(sc->device->lun, &hdr->lun); hdr->rtt = RESERVED_ITT; } @@ -2040,7 +2282,7 @@ int iscsi_eh_device_reset(struct scsi_cmnd *sc) ISCSI_DBG_EH(session, "LU Reset [sc %p lun %u]\n", sc, sc->device->lun); mutex_lock(&session->eh_mutex); - spin_lock_bh(&session->lock); + spin_lock_bh(&session->frwd_lock); /* * Just check if we are not logged in. We cannot check for * the phase because the reset could come from a ioctl. @@ -2067,8 +2309,8 @@ int iscsi_eh_device_reset(struct scsi_cmnd *sc) case TMF_SUCCESS: break; case TMF_TIMEDOUT: - spin_unlock_bh(&session->lock); - iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); + spin_unlock_bh(&session->frwd_lock); + iscsi_conn_failure(conn, ISCSI_ERR_SCSI_EH_SESSION_RST); goto done; default: conn->tmf_state = TMF_INITIAL; @@ -2076,20 +2318,21 @@ int iscsi_eh_device_reset(struct scsi_cmnd *sc) } rc = SUCCESS; - spin_unlock_bh(&session->lock); + spin_unlock_bh(&session->frwd_lock); iscsi_suspend_tx(conn); - spin_lock_bh(&session->lock); + spin_lock_bh(&session->frwd_lock); + memset(hdr, 0, sizeof(*hdr)); fail_scsi_tasks(conn, sc->device->lun, DID_ERROR); conn->tmf_state = TMF_INITIAL; - spin_unlock_bh(&session->lock); + spin_unlock_bh(&session->frwd_lock); iscsi_start_tx(conn); goto done; unlock: - spin_unlock_bh(&session->lock); + spin_unlock_bh(&session->frwd_lock); done: ISCSI_DBG_EH(session, "dev reset result = %s\n", rc == SUCCESS ? "SUCCESS" : "FAILED"); @@ -2098,6 +2341,187 @@ done: } EXPORT_SYMBOL_GPL(iscsi_eh_device_reset); +void iscsi_session_recovery_timedout(struct iscsi_cls_session *cls_session) +{ + struct iscsi_session *session = cls_session->dd_data; + + spin_lock_bh(&session->frwd_lock); + if (session->state != ISCSI_STATE_LOGGED_IN) { + session->state = ISCSI_STATE_RECOVERY_FAILED; + if (session->leadconn) + wake_up(&session->leadconn->ehwait); + } + spin_unlock_bh(&session->frwd_lock); +} +EXPORT_SYMBOL_GPL(iscsi_session_recovery_timedout); + +/** + * iscsi_eh_session_reset - drop session and attempt relogin + * @sc: scsi command + * + * This function will wait for a relogin, session termination from + * userspace, or a recovery/replacement timeout. + */ +int iscsi_eh_session_reset(struct scsi_cmnd *sc) +{ + struct iscsi_cls_session *cls_session; + struct iscsi_session *session; + struct iscsi_conn *conn; + + cls_session = starget_to_session(scsi_target(sc->device)); + session = cls_session->dd_data; + conn = session->leadconn; + + mutex_lock(&session->eh_mutex); + spin_lock_bh(&session->frwd_lock); + if (session->state == ISCSI_STATE_TERMINATE) { +failed: + ISCSI_DBG_EH(session, + "failing session reset: Could not log back into " + "%s, %s [age %d]\n", session->targetname, + conn->persistent_address, session->age); + spin_unlock_bh(&session->frwd_lock); + mutex_unlock(&session->eh_mutex); + return FAILED; + } + + spin_unlock_bh(&session->frwd_lock); + mutex_unlock(&session->eh_mutex); + /* + * we drop the lock here but the leadconn cannot be destoyed while + * we are in the scsi eh + */ + iscsi_conn_failure(conn, ISCSI_ERR_SCSI_EH_SESSION_RST); + + ISCSI_DBG_EH(session, "wait for relogin\n"); + wait_event_interruptible(conn->ehwait, + session->state == ISCSI_STATE_TERMINATE || + session->state == ISCSI_STATE_LOGGED_IN || + session->state == ISCSI_STATE_RECOVERY_FAILED); + if (signal_pending(current)) + flush_signals(current); + + mutex_lock(&session->eh_mutex); + spin_lock_bh(&session->frwd_lock); + if (session->state == ISCSI_STATE_LOGGED_IN) { + ISCSI_DBG_EH(session, + "session reset succeeded for %s,%s\n", + session->targetname, conn->persistent_address); + } else + goto failed; + spin_unlock_bh(&session->frwd_lock); + mutex_unlock(&session->eh_mutex); + return SUCCESS; +} +EXPORT_SYMBOL_GPL(iscsi_eh_session_reset); + +static void iscsi_prep_tgt_reset_pdu(struct scsi_cmnd *sc, struct iscsi_tm *hdr) +{ + memset(hdr, 0, sizeof(*hdr)); + hdr->opcode = ISCSI_OP_SCSI_TMFUNC | ISCSI_OP_IMMEDIATE; + hdr->flags = ISCSI_TM_FUNC_TARGET_WARM_RESET & ISCSI_FLAG_TM_FUNC_MASK; + hdr->flags |= ISCSI_FLAG_CMD_FINAL; + hdr->rtt = RESERVED_ITT; +} + +/** + * iscsi_eh_target_reset - reset target + * @sc: scsi command + * + * This will attempt to send a warm target reset. + */ +int iscsi_eh_target_reset(struct scsi_cmnd *sc) +{ + struct iscsi_cls_session *cls_session; + struct iscsi_session *session; + struct iscsi_conn *conn; + struct iscsi_tm *hdr; + int rc = FAILED; + + cls_session = starget_to_session(scsi_target(sc->device)); + session = cls_session->dd_data; + + ISCSI_DBG_EH(session, "tgt Reset [sc %p tgt %s]\n", sc, + session->targetname); + + mutex_lock(&session->eh_mutex); + spin_lock_bh(&session->frwd_lock); + /* + * Just check if we are not logged in. We cannot check for + * the phase because the reset could come from a ioctl. + */ + if (!session->leadconn || session->state != ISCSI_STATE_LOGGED_IN) + goto unlock; + conn = session->leadconn; + + /* only have one tmf outstanding at a time */ + if (conn->tmf_state != TMF_INITIAL) + goto unlock; + conn->tmf_state = TMF_QUEUED; + + hdr = &conn->tmhdr; + iscsi_prep_tgt_reset_pdu(sc, hdr); + + if (iscsi_exec_task_mgmt_fn(conn, hdr, session->age, + session->tgt_reset_timeout)) { + rc = FAILED; + goto unlock; + } + + switch (conn->tmf_state) { + case TMF_SUCCESS: + break; + case TMF_TIMEDOUT: + spin_unlock_bh(&session->frwd_lock); + iscsi_conn_failure(conn, ISCSI_ERR_SCSI_EH_SESSION_RST); + goto done; + default: + conn->tmf_state = TMF_INITIAL; + goto unlock; + } + + rc = SUCCESS; + spin_unlock_bh(&session->frwd_lock); + + iscsi_suspend_tx(conn); + + spin_lock_bh(&session->frwd_lock); + memset(hdr, 0, sizeof(*hdr)); + fail_scsi_tasks(conn, -1, DID_ERROR); + conn->tmf_state = TMF_INITIAL; + spin_unlock_bh(&session->frwd_lock); + + iscsi_start_tx(conn); + goto done; + +unlock: + spin_unlock_bh(&session->frwd_lock); +done: + ISCSI_DBG_EH(session, "tgt %s reset result = %s\n", session->targetname, + rc == SUCCESS ? "SUCCESS" : "FAILED"); + mutex_unlock(&session->eh_mutex); + return rc; +} +EXPORT_SYMBOL_GPL(iscsi_eh_target_reset); + +/** + * iscsi_eh_recover_target - reset target and possibly the session + * @sc: scsi command + * + * This will attempt to send a warm target reset. If that fails, + * we will escalate to ERL0 session recovery. + */ +int iscsi_eh_recover_target(struct scsi_cmnd *sc) +{ + int rc; + + rc = iscsi_eh_target_reset(sc); + if (rc == FAILED) + rc = iscsi_eh_session_reset(sc); + return rc; +} +EXPORT_SYMBOL_GPL(iscsi_eh_recover_target); + /* * Pre-allocate a pool of @max items of @item_size. By default, the pool * should be accessed via kfifo_{get,put} on q->queue. @@ -2121,12 +2545,7 @@ iscsi_pool_init(struct iscsi_pool *q, int max, void ***items, int item_size) if (q->pool == NULL) return -ENOMEM; - q->queue = kfifo_init((void*)q->pool, max * sizeof(void*), - GFP_KERNEL, NULL); - if (IS_ERR(q->queue)) { - q->queue = NULL; - goto enomem; - } + kfifo_init(&q->queue, (void*)q->pool, max * sizeof(void*)); for (i = 0; i < max; i++) { q->pool[i] = kzalloc(item_size, GFP_KERNEL); @@ -2134,7 +2553,7 @@ iscsi_pool_init(struct iscsi_pool *q, int max, void ***items, int item_size) q->max = i; goto enomem; } - __kfifo_put(q->queue, (void*)&q->pool[i], sizeof(void*)); + kfifo_in(&q->queue, (void*)&q->pool[i], sizeof(void*)); } if (items) { @@ -2157,7 +2576,6 @@ void iscsi_pool_free(struct iscsi_pool *q) for (i = 0; i < q->max; i++) kfree(q->pool[i]); kfree(q->pool); - kfree(q->queue); } EXPORT_SYMBOL_GPL(iscsi_pool_free); @@ -2305,7 +2723,7 @@ static void iscsi_host_dec_session_cnt(struct Scsi_Host *shost) */ struct iscsi_cls_session * iscsi_session_setup(struct iscsi_transport *iscsit, struct Scsi_Host *shost, - uint16_t cmds_max, int cmd_task_size, + uint16_t cmds_max, int dd_size, int cmd_task_size, uint32_t initial_cmdsn, unsigned int id) { struct iscsi_host *ihost = shost_priv(shost); @@ -2355,7 +2773,8 @@ iscsi_session_setup(struct iscsi_transport *iscsit, struct Scsi_Host *shost, scsi_cmds = total_cmds - ISCSI_MGMT_CMDS_MAX; cls_session = iscsi_alloc_session(shost, iscsit, - sizeof(struct iscsi_session)); + sizeof(struct iscsi_session) + + dd_size); if (!cls_session) goto dec_session_count; session = cls_session->dd_data; @@ -2363,6 +2782,7 @@ iscsi_session_setup(struct iscsi_transport *iscsit, struct Scsi_Host *shost, session->host = shost; session->state = ISCSI_STATE_FREE; session->fast_abort = 1; + session->tgt_reset_timeout = 30; session->lu_reset_timeout = 15; session->abort_timeout = 10; session->scsi_cmds_max = scsi_cmds; @@ -2372,8 +2792,11 @@ iscsi_session_setup(struct iscsi_transport *iscsit, struct Scsi_Host *shost, session->max_cmdsn = initial_cmdsn + 1; session->max_r2t = 1; session->tt = iscsit; + session->dd_data = cls_session->dd_data + sizeof(*session); + mutex_init(&session->eh_mutex); - spin_lock_init(&session->lock); + spin_lock_init(&session->frwd_lock); + spin_lock_init(&session->back_lock); /* initialize SCSI PDU commands pool */ if (iscsi_pool_init(&session->cmdpool, session->cmds_max, @@ -2432,8 +2855,14 @@ void iscsi_session_teardown(struct iscsi_cls_session *cls_session) kfree(session->username); kfree(session->username_in); kfree(session->targetname); + kfree(session->targetalias); kfree(session->initiatorname); + kfree(session->boot_root); + kfree(session->boot_nic); + kfree(session->boot_target); kfree(session->ifacename); + kfree(session->portal_type); + kfree(session->discovery_parent_type); iscsi_destroy_session(cls_session); iscsi_host_dec_session_cnt(shost); @@ -2481,14 +2910,14 @@ iscsi_conn_setup(struct iscsi_cls_session *cls_session, int dd_size, INIT_WORK(&conn->xmitwork, iscsi_xmitworker); /* allocate login_task used for the login/text sequences */ - spin_lock_bh(&session->lock); - if (!__kfifo_get(session->cmdpool.queue, + spin_lock_bh(&session->frwd_lock); + if (!kfifo_out(&session->cmdpool.queue, (void*)&conn->login_task, sizeof(void*))) { - spin_unlock_bh(&session->lock); + spin_unlock_bh(&session->frwd_lock); goto login_task_alloc_fail; } - spin_unlock_bh(&session->lock); + spin_unlock_bh(&session->frwd_lock); data = (char *) __get_free_pages(GFP_KERNEL, get_order(ISCSI_DEF_MAX_RECV_SEG_LEN)); @@ -2502,7 +2931,7 @@ iscsi_conn_setup(struct iscsi_cls_session *cls_session, int dd_size, return cls_conn; login_task_data_alloc_fail: - __kfifo_put(session->cmdpool.queue, (void*)&conn->login_task, + kfifo_in(&session->cmdpool.queue, (void*)&conn->login_task, sizeof(void*)); login_task_alloc_fail: iscsi_destroy_conn(cls_conn); @@ -2525,7 +2954,7 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn) del_timer_sync(&conn->transport_timer); - spin_lock_bh(&session->lock); + spin_lock_bh(&session->frwd_lock); conn->c_stage = ISCSI_CONN_CLEANUP_WAIT; if (session->leadconn == conn) { /* @@ -2534,7 +2963,7 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn) session->state = ISCSI_STATE_TERMINATE; wake_up(&conn->ehwait); } - spin_unlock_bh(&session->lock); + spin_unlock_bh(&session->frwd_lock); /* * Block until all in-progress commands for this connection @@ -2561,15 +2990,19 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn) /* flush queued up work because we free the connection below */ iscsi_suspend_tx(conn); - spin_lock_bh(&session->lock); + spin_lock_bh(&session->frwd_lock); free_pages((unsigned long) conn->data, get_order(ISCSI_DEF_MAX_RECV_SEG_LEN)); kfree(conn->persistent_address); - __kfifo_put(session->cmdpool.queue, (void*)&conn->login_task, + kfree(conn->local_ipaddr); + /* regular RX path uses back_lock */ + spin_lock_bh(&session->back_lock); + kfifo_in(&session->cmdpool.queue, (void*)&conn->login_task, sizeof(void*)); + spin_unlock_bh(&session->back_lock); if (session->leadconn == conn) session->leadconn = NULL; - spin_unlock_bh(&session->lock); + spin_unlock_bh(&session->frwd_lock); iscsi_destroy_conn(cls_conn); } @@ -2606,7 +3039,7 @@ int iscsi_conn_start(struct iscsi_cls_conn *cls_conn) conn->ping_timeout = 5; } - spin_lock_bh(&session->lock); + spin_lock_bh(&session->frwd_lock); conn->c_stage = ISCSI_CONN_STARTED; session->state = ISCSI_STATE_LOGGED_IN; session->queued_cmdsn = session->cmdsn; @@ -2635,7 +3068,7 @@ int iscsi_conn_start(struct iscsi_cls_conn *cls_conn) default: break; } - spin_unlock_bh(&session->lock); + spin_unlock_bh(&session->frwd_lock); iscsi_unblock_session(session->cls_session); wake_up(&conn->ehwait); @@ -2674,9 +3107,9 @@ static void iscsi_start_session_recovery(struct iscsi_session *session, int old_stop_stage; mutex_lock(&session->eh_mutex); - spin_lock_bh(&session->lock); + spin_lock_bh(&session->frwd_lock); if (conn->stop_stage == STOP_CONN_TERM) { - spin_unlock_bh(&session->lock); + spin_unlock_bh(&session->frwd_lock); mutex_unlock(&session->eh_mutex); return; } @@ -2690,16 +3123,17 @@ static void iscsi_start_session_recovery(struct iscsi_session *session, session->state = ISCSI_STATE_TERMINATE; else if (conn->stop_stage != STOP_CONN_RECOVER) session->state = ISCSI_STATE_IN_RECOVERY; - spin_unlock_bh(&session->lock); + + old_stop_stage = conn->stop_stage; + conn->stop_stage = flag; + spin_unlock_bh(&session->frwd_lock); del_timer_sync(&conn->transport_timer); iscsi_suspend_tx(conn); - spin_lock_bh(&session->lock); - old_stop_stage = conn->stop_stage; - conn->stop_stage = flag; + spin_lock_bh(&session->frwd_lock); conn->c_stage = ISCSI_CONN_STOPPED; - spin_unlock_bh(&session->lock); + spin_unlock_bh(&session->frwd_lock); /* * for connection level recovery we should not calculate @@ -2720,10 +3154,11 @@ static void iscsi_start_session_recovery(struct iscsi_session *session, /* * flush queues. */ - spin_lock_bh(&session->lock); + spin_lock_bh(&session->frwd_lock); fail_scsi_tasks(conn, -1, DID_TRANSPORT_DISRUPTED); fail_mgmt_tasks(session, conn); - spin_unlock_bh(&session->lock); + memset(&conn->tmhdr, 0, sizeof(conn->tmhdr)); + spin_unlock_bh(&session->frwd_lock); mutex_unlock(&session->eh_mutex); } @@ -2750,10 +3185,10 @@ int iscsi_conn_bind(struct iscsi_cls_session *cls_session, struct iscsi_session *session = cls_session->dd_data; struct iscsi_conn *conn = cls_conn->dd_data; - spin_lock_bh(&session->lock); + spin_lock_bh(&session->frwd_lock); if (is_leading) session->leadconn = conn; - spin_unlock_bh(&session->lock); + spin_unlock_bh(&session->frwd_lock); /* * Unblock xmitworker(), Login Phase will pass through. @@ -2764,7 +3199,7 @@ int iscsi_conn_bind(struct iscsi_cls_session *cls_session, } EXPORT_SYMBOL_GPL(iscsi_conn_bind); -static int iscsi_switch_str_param(char **param, char *new_val_buf) +int iscsi_switch_str_param(char **param, char *new_val_buf) { char *new_val; @@ -2781,13 +3216,14 @@ static int iscsi_switch_str_param(char **param, char *new_val_buf) *param = new_val; return 0; } +EXPORT_SYMBOL_GPL(iscsi_switch_str_param); int iscsi_set_param(struct iscsi_cls_conn *cls_conn, enum iscsi_param param, char *buf, int buflen) { struct iscsi_conn *conn = cls_conn->dd_data; struct iscsi_session *session = conn->session; - uint32_t value; + int val; switch(param) { case ISCSI_PARAM_FAST_ABORT: @@ -2799,6 +3235,9 @@ int iscsi_set_param(struct iscsi_cls_conn *cls_conn, case ISCSI_PARAM_LU_RESET_TMO: sscanf(buf, "%d", &session->lu_reset_timeout); break; + case ISCSI_PARAM_TGT_RESET_TMO: + sscanf(buf, "%d", &session->tgt_reset_timeout); + break; case ISCSI_PARAM_PING_TMO: sscanf(buf, "%d", &conn->ping_timeout); break; @@ -2821,7 +3260,7 @@ int iscsi_set_param(struct iscsi_cls_conn *cls_conn, sscanf(buf, "%d", &session->initial_r2t_en); break; case ISCSI_PARAM_MAX_R2T: - sscanf(buf, "%d", &session->max_r2t); + sscanf(buf, "%hu", &session->max_r2t); break; case ISCSI_PARAM_IMM_DATA_EN: sscanf(buf, "%d", &session->imm_data_en); @@ -2841,14 +3280,6 @@ int iscsi_set_param(struct iscsi_cls_conn *cls_conn, case ISCSI_PARAM_ERL: sscanf(buf, "%d", &session->erl); break; - case ISCSI_PARAM_IFMARKER_EN: - sscanf(buf, "%d", &value); - BUG_ON(value); - break; - case ISCSI_PARAM_OFMARKER_EN: - sscanf(buf, "%d", &value); - BUG_ON(value); - break; case ISCSI_PARAM_EXP_STATSN: sscanf(buf, "%u", &conn->exp_statsn); break; @@ -2862,6 +3293,8 @@ int iscsi_set_param(struct iscsi_cls_conn *cls_conn, return iscsi_switch_str_param(&session->password_in, buf); case ISCSI_PARAM_TARGET_NAME: return iscsi_switch_str_param(&session->targetname, buf); + case ISCSI_PARAM_TARGET_ALIAS: + return iscsi_switch_str_param(&session->targetalias, buf); case ISCSI_PARAM_TPGT: sscanf(buf, "%d", &session->tpgt); break; @@ -2874,6 +3307,23 @@ int iscsi_set_param(struct iscsi_cls_conn *cls_conn, return iscsi_switch_str_param(&session->ifacename, buf); case ISCSI_PARAM_INITIATOR_NAME: return iscsi_switch_str_param(&session->initiatorname, buf); + case ISCSI_PARAM_BOOT_ROOT: + return iscsi_switch_str_param(&session->boot_root, buf); + case ISCSI_PARAM_BOOT_NIC: + return iscsi_switch_str_param(&session->boot_nic, buf); + case ISCSI_PARAM_BOOT_TARGET: + return iscsi_switch_str_param(&session->boot_target, buf); + case ISCSI_PARAM_PORTAL_TYPE: + return iscsi_switch_str_param(&session->portal_type, buf); + case ISCSI_PARAM_DISCOVERY_PARENT_TYPE: + return iscsi_switch_str_param(&session->discovery_parent_type, + buf); + case ISCSI_PARAM_DISCOVERY_SESS: + sscanf(buf, "%d", &val); + session->discovery_sess = !!val; + break; + case ISCSI_PARAM_LOCAL_IPADDR: + return iscsi_switch_str_param(&conn->local_ipaddr, buf); default: return -ENOSYS; } @@ -2898,6 +3348,9 @@ int iscsi_session_get_param(struct iscsi_cls_session *cls_session, case ISCSI_PARAM_LU_RESET_TMO: len = sprintf(buf, "%d\n", session->lu_reset_timeout); break; + case ISCSI_PARAM_TGT_RESET_TMO: + len = sprintf(buf, "%d\n", session->tgt_reset_timeout); + break; case ISCSI_PARAM_INITIAL_R2T_EN: len = sprintf(buf, "%d\n", session->initial_r2t_en); break; @@ -2919,12 +3372,18 @@ int iscsi_session_get_param(struct iscsi_cls_session *cls_session, case ISCSI_PARAM_DATASEQ_INORDER_EN: len = sprintf(buf, "%d\n", session->dataseq_inorder_en); break; + case ISCSI_PARAM_DEF_TASKMGMT_TMO: + len = sprintf(buf, "%d\n", session->def_taskmgmt_tmo); + break; case ISCSI_PARAM_ERL: len = sprintf(buf, "%d\n", session->erl); break; case ISCSI_PARAM_TARGET_NAME: len = sprintf(buf, "%s\n", session->targetname); break; + case ISCSI_PARAM_TARGET_ALIAS: + len = sprintf(buf, "%s\n", session->targetalias); + break; case ISCSI_PARAM_TPGT: len = sprintf(buf, "%d\n", session->tpgt); break; @@ -2946,6 +3405,61 @@ int iscsi_session_get_param(struct iscsi_cls_session *cls_session, case ISCSI_PARAM_INITIATOR_NAME: len = sprintf(buf, "%s\n", session->initiatorname); break; + case ISCSI_PARAM_BOOT_ROOT: + len = sprintf(buf, "%s\n", session->boot_root); + break; + case ISCSI_PARAM_BOOT_NIC: + len = sprintf(buf, "%s\n", session->boot_nic); + break; + case ISCSI_PARAM_BOOT_TARGET: + len = sprintf(buf, "%s\n", session->boot_target); + break; + case ISCSI_PARAM_AUTO_SND_TGT_DISABLE: + len = sprintf(buf, "%u\n", session->auto_snd_tgt_disable); + break; + case ISCSI_PARAM_DISCOVERY_SESS: + len = sprintf(buf, "%u\n", session->discovery_sess); + break; + case ISCSI_PARAM_PORTAL_TYPE: + len = sprintf(buf, "%s\n", session->portal_type); + break; + case ISCSI_PARAM_CHAP_AUTH_EN: + len = sprintf(buf, "%u\n", session->chap_auth_en); + break; + case ISCSI_PARAM_DISCOVERY_LOGOUT_EN: + len = sprintf(buf, "%u\n", session->discovery_logout_en); + break; + case ISCSI_PARAM_BIDI_CHAP_EN: + len = sprintf(buf, "%u\n", session->bidi_chap_en); + break; + case ISCSI_PARAM_DISCOVERY_AUTH_OPTIONAL: + len = sprintf(buf, "%u\n", session->discovery_auth_optional); + break; + case ISCSI_PARAM_DEF_TIME2WAIT: + len = sprintf(buf, "%d\n", session->time2wait); + break; + case ISCSI_PARAM_DEF_TIME2RETAIN: + len = sprintf(buf, "%d\n", session->time2retain); + break; + case ISCSI_PARAM_TSID: + len = sprintf(buf, "%u\n", session->tsid); + break; + case ISCSI_PARAM_ISID: + len = sprintf(buf, "%02x%02x%02x%02x%02x%02x\n", + session->isid[0], session->isid[1], + session->isid[2], session->isid[3], + session->isid[4], session->isid[5]); + break; + case ISCSI_PARAM_DISCOVERY_PARENT_IDX: + len = sprintf(buf, "%u\n", session->discovery_parent_idx); + break; + case ISCSI_PARAM_DISCOVERY_PARENT_TYPE: + if (session->discovery_parent_type) + len = sprintf(buf, "%s\n", + session->discovery_parent_type); + else + len = sprintf(buf, "\n"); + break; default: return -ENOSYS; } @@ -2954,6 +3468,47 @@ int iscsi_session_get_param(struct iscsi_cls_session *cls_session, } EXPORT_SYMBOL_GPL(iscsi_session_get_param); +int iscsi_conn_get_addr_param(struct sockaddr_storage *addr, + enum iscsi_param param, char *buf) +{ + struct sockaddr_in6 *sin6 = NULL; + struct sockaddr_in *sin = NULL; + int len; + + switch (addr->ss_family) { + case AF_INET: + sin = (struct sockaddr_in *)addr; + break; + case AF_INET6: + sin6 = (struct sockaddr_in6 *)addr; + break; + default: + return -EINVAL; + } + + switch (param) { + case ISCSI_PARAM_CONN_ADDRESS: + case ISCSI_HOST_PARAM_IPADDRESS: + if (sin) + len = sprintf(buf, "%pI4\n", &sin->sin_addr.s_addr); + else + len = sprintf(buf, "%pI6\n", &sin6->sin6_addr); + break; + case ISCSI_PARAM_CONN_PORT: + if (sin) + len = sprintf(buf, "%hu\n", be16_to_cpu(sin->sin_port)); + else + len = sprintf(buf, "%hu\n", + be16_to_cpu(sin6->sin6_port)); + break; + default: + return -EINVAL; + } + + return len; +} +EXPORT_SYMBOL_GPL(iscsi_conn_get_addr_param); + int iscsi_conn_get_param(struct iscsi_cls_conn *cls_conn, enum iscsi_param param, char *buf) { @@ -2994,6 +3549,57 @@ int iscsi_conn_get_param(struct iscsi_cls_conn *cls_conn, case ISCSI_PARAM_PERSISTENT_ADDRESS: len = sprintf(buf, "%s\n", conn->persistent_address); break; + case ISCSI_PARAM_STATSN: + len = sprintf(buf, "%u\n", conn->statsn); + break; + case ISCSI_PARAM_MAX_SEGMENT_SIZE: + len = sprintf(buf, "%u\n", conn->max_segment_size); + break; + case ISCSI_PARAM_KEEPALIVE_TMO: + len = sprintf(buf, "%u\n", conn->keepalive_tmo); + break; + case ISCSI_PARAM_LOCAL_PORT: + len = sprintf(buf, "%u\n", conn->local_port); + break; + case ISCSI_PARAM_TCP_TIMESTAMP_STAT: + len = sprintf(buf, "%u\n", conn->tcp_timestamp_stat); + break; + case ISCSI_PARAM_TCP_NAGLE_DISABLE: + len = sprintf(buf, "%u\n", conn->tcp_nagle_disable); + break; + case ISCSI_PARAM_TCP_WSF_DISABLE: + len = sprintf(buf, "%u\n", conn->tcp_wsf_disable); + break; + case ISCSI_PARAM_TCP_TIMER_SCALE: + len = sprintf(buf, "%u\n", conn->tcp_timer_scale); + break; + case ISCSI_PARAM_TCP_TIMESTAMP_EN: + len = sprintf(buf, "%u\n", conn->tcp_timestamp_en); + break; + case ISCSI_PARAM_IP_FRAGMENT_DISABLE: + len = sprintf(buf, "%u\n", conn->fragment_disable); + break; + case ISCSI_PARAM_IPV4_TOS: + len = sprintf(buf, "%u\n", conn->ipv4_tos); + break; + case ISCSI_PARAM_IPV6_TC: + len = sprintf(buf, "%u\n", conn->ipv6_traffic_class); + break; + case ISCSI_PARAM_IPV6_FLOW_LABEL: + len = sprintf(buf, "%u\n", conn->ipv6_flow_label); + break; + case ISCSI_PARAM_IS_FW_ASSIGNED_IPV6: + len = sprintf(buf, "%u\n", conn->is_fw_assigned_ipv6); + break; + case ISCSI_PARAM_TCP_XMIT_WSF: + len = sprintf(buf, "%u\n", conn->tcp_xmit_wsf); + break; + case ISCSI_PARAM_TCP_RECV_WSF: + len = sprintf(buf, "%u\n", conn->tcp_recv_wsf); + break; + case ISCSI_PARAM_LOCAL_IPADDR: + len = sprintf(buf, "%s\n", conn->local_ipaddr); + break; default: return -ENOSYS; } @@ -3018,9 +3624,6 @@ int iscsi_host_get_param(struct Scsi_Host *shost, enum iscsi_host_param param, case ISCSI_HOST_PARAM_INITIATOR_NAME: len = sprintf(buf, "%s\n", ihost->initiatorname); break; - case ISCSI_HOST_PARAM_IPADDRESS: - len = sprintf(buf, "%s\n", ihost->local_address); - break; default: return -ENOSYS; } |
