X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=drivers%2Fscsi%2Flibiscsi.c;h=b43bf1d60dac8a5499922f8a66da15b3c26626d2;hb=08722bc4a066705e3f5fb4a5a87ce717fe9f896e;hp=b7a2b9ad3a979da075d1d24f2d69115ae9040a3c;hpb=b3a7ea8d50f6028964b468d13a095dfb2508b2fb;p=safe%2Fjmp%2Flinux-2.6 diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index b7a2b9a..b43bf1d 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -136,6 +137,70 @@ static int iscsi_add_hdr(struct iscsi_cmd_task *ctask, unsigned len) return 0; } +/* + * make an extended cdb AHS + */ +static int iscsi_prep_ecdb_ahs(struct iscsi_cmd_task *ctask) +{ + struct scsi_cmnd *cmd = ctask->sc; + unsigned rlen, pad_len; + unsigned short ahslength; + struct iscsi_ecdb_ahdr *ecdb_ahdr; + int rc; + + ecdb_ahdr = iscsi_next_hdr(ctask); + rlen = cmd->cmd_len - ISCSI_CDB_SIZE; + + BUG_ON(rlen > sizeof(ecdb_ahdr->ecdb)); + ahslength = rlen + sizeof(ecdb_ahdr->reserved); + + pad_len = iscsi_padding(rlen); + + rc = iscsi_add_hdr(ctask, sizeof(ecdb_ahdr->ahslength) + + sizeof(ecdb_ahdr->ahstype) + ahslength + pad_len); + if (rc) + return rc; + + if (pad_len) + memset(&ecdb_ahdr->ecdb[rlen], 0, pad_len); + + ecdb_ahdr->ahslength = cpu_to_be16(ahslength); + ecdb_ahdr->ahstype = ISCSI_AHSTYPE_CDB; + ecdb_ahdr->reserved = 0; + memcpy(ecdb_ahdr->ecdb, cmd->cmnd + ISCSI_CDB_SIZE, rlen); + + debug_scsi("iscsi_prep_ecdb_ahs: varlen_cdb_len %d " + "rlen %d pad_len %d ahs_length %d iscsi_headers_size %u\n", + cmd->cmd_len, rlen, pad_len, ahslength, ctask->hdr_len); + + return 0; +} + +static int iscsi_prep_bidi_ahs(struct iscsi_cmd_task *ctask) +{ + struct scsi_cmnd *sc = ctask->sc; + struct iscsi_rlength_ahdr *rlen_ahdr; + int rc; + + rlen_ahdr = iscsi_next_hdr(ctask); + rc = iscsi_add_hdr(ctask, sizeof(*rlen_ahdr)); + if (rc) + return rc; + + rlen_ahdr->ahslength = + cpu_to_be16(sizeof(rlen_ahdr->read_length) + + sizeof(rlen_ahdr->reserved)); + rlen_ahdr->ahstype = ISCSI_AHSTYPE_RLENGTH; + rlen_ahdr->reserved = 0; + rlen_ahdr->read_length = cpu_to_be32(scsi_in(sc)->length); + + debug_scsi("bidi-in rlen_ahdr->read_length(%d) " + "rlen_ahdr->ahslength(%d)\n", + be32_to_cpu(rlen_ahdr->read_length), + be16_to_cpu(rlen_ahdr->ahslength)); + return 0; +} + /** * iscsi_prep_scsi_cmd_pdu - prep iscsi scsi cmd pdu * @ctask: iscsi cmd task @@ -149,29 +214,41 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask) struct iscsi_session *session = conn->session; struct iscsi_cmd *hdr = ctask->hdr; struct scsi_cmnd *sc = ctask->sc; - unsigned hdrlength; + unsigned hdrlength, cmd_len; int rc; ctask->hdr_len = 0; rc = iscsi_add_hdr(ctask, sizeof(*hdr)); if (rc) return rc; - hdr->opcode = ISCSI_OP_SCSI_CMD; - hdr->flags = ISCSI_ATTR_SIMPLE; - int_to_scsilun(sc->device->lun, (struct scsi_lun *)hdr->lun); - hdr->itt = build_itt(ctask->itt, conn->id, session->age); - hdr->data_length = cpu_to_be32(scsi_bufflen(sc)); - hdr->cmdsn = cpu_to_be32(session->cmdsn); - session->cmdsn++; - hdr->exp_statsn = cpu_to_be32(conn->exp_statsn); - memcpy(hdr->cdb, sc->cmnd, sc->cmd_len); - if (sc->cmd_len < MAX_COMMAND_SIZE) - memset(&hdr->cdb[sc->cmd_len], 0, - MAX_COMMAND_SIZE - sc->cmd_len); - - ctask->data_count = 0; + hdr->opcode = ISCSI_OP_SCSI_CMD; + hdr->flags = ISCSI_ATTR_SIMPLE; + int_to_scsilun(sc->device->lun, (struct scsi_lun *)hdr->lun); + hdr->itt = build_itt(ctask->itt, session->age); + hdr->cmdsn = cpu_to_be32(session->cmdsn); + session->cmdsn++; + hdr->exp_statsn = cpu_to_be32(conn->exp_statsn); + cmd_len = sc->cmd_len; + if (cmd_len < ISCSI_CDB_SIZE) + memset(&hdr->cdb[cmd_len], 0, ISCSI_CDB_SIZE - cmd_len); + else if (cmd_len > ISCSI_CDB_SIZE) { + rc = iscsi_prep_ecdb_ahs(ctask); + if (rc) + return rc; + cmd_len = ISCSI_CDB_SIZE; + } + memcpy(hdr->cdb, sc->cmnd, cmd_len); + ctask->imm_count = 0; + if (scsi_bidi_cmnd(sc)) { + hdr->flags |= ISCSI_FLAG_CMD_READ; + rc = iscsi_prep_bidi_ahs(ctask); + if (rc) + return rc; + } if (sc->sc_data_direction == DMA_TO_DEVICE) { + unsigned out_len = scsi_out(sc)->length; + hdr->data_length = cpu_to_be32(out_len); hdr->flags |= ISCSI_FLAG_CMD_WRITE; /* * Write counters: @@ -192,28 +269,29 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask) ctask->unsol_datasn = 0; if (session->imm_data_en) { - if (scsi_bufflen(sc) >= session->first_burst) + if (out_len >= session->first_burst) ctask->imm_count = min(session->first_burst, conn->max_xmit_dlength); else - ctask->imm_count = min(scsi_bufflen(sc), + ctask->imm_count = min(out_len, conn->max_xmit_dlength); - hton24(ctask->hdr->dlength, ctask->imm_count); + hton24(hdr->dlength, ctask->imm_count); } else - zero_data(ctask->hdr->dlength); + zero_data(hdr->dlength); if (!session->initial_r2t_en) { - ctask->unsol_count = min((session->first_burst), - (scsi_bufflen(sc))) - ctask->imm_count; + ctask->unsol_count = min(session->first_burst, out_len) + - ctask->imm_count; ctask->unsol_offset = ctask->imm_count; } if (!ctask->unsol_count) /* No unsolicit Data-Out's */ - ctask->hdr->flags |= ISCSI_FLAG_CMD_FINAL; + hdr->flags |= ISCSI_FLAG_CMD_FINAL; } 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; @@ -228,13 +306,17 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask) WARN_ON(hdrlength >= 256); hdr->hlength = hdrlength & 0xFF; - conn->scsicmd_pdus_cnt++; + if (conn->session->tt->init_cmd_task(conn->ctask)) + return EIO; - debug_scsi("iscsi prep [%s cid %d sc %p cdb 0x%x itt 0x%x len %d " - "cmdsn %d win %d]\n", - sc->sc_data_direction == DMA_TO_DEVICE ? "write" : "read", - conn->id, sc, sc->cmnd[0], ctask->itt, scsi_bufflen(sc), - session->cmdsn, session->max_cmdsn - session->exp_cmdsn + 1); + conn->scsicmd_pdus_cnt++; + debug_scsi("iscsi prep [%s cid %d sc %p cdb 0x%x itt 0x%x " + "len %d bidi_len %d cmdsn %d win %d]\n", + scsi_bidi_cmnd(sc) ? "bidirectional" : + sc->sc_data_direction == DMA_TO_DEVICE ? "write" : "read", + conn->id, sc, sc->cmnd[0], ctask->itt, + scsi_bufflen(sc), scsi_bidi_cmnd(sc) ? scsi_in(sc)->length : 0, + session->cmdsn, session->max_cmdsn - session->exp_cmdsn + 1); return 0; } @@ -248,13 +330,16 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask) */ static void iscsi_complete_command(struct iscsi_cmd_task *ctask) { - struct iscsi_session *session = ctask->conn->session; + struct iscsi_conn *conn = ctask->conn; + struct iscsi_session *session = conn->session; struct scsi_cmnd *sc = ctask->sc; ctask->state = ISCSI_TASK_COMPLETED; ctask->sc = NULL; /* SCSI eh reuses commands to verify us */ sc->SCp.ptr = NULL; + if (conn->ctask == ctask) + conn->ctask = NULL; list_del_init(&ctask->running); __kfifo_put(session->cmdpool.queue, (void*)&ctask, sizeof(void*)); sc->scsi_done(sc); @@ -293,7 +378,12 @@ static void fail_command(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask, conn->session->tt->cleanup_cmd_task(conn, ctask); sc->result = err; - scsi_set_resid(sc, scsi_bufflen(sc)); + if (!scsi_bidi_cmnd(sc)) + scsi_set_resid(sc, scsi_bufflen(sc)); + else { + scsi_out(sc)->resid = scsi_out(sc)->length; + scsi_in(sc)->resid = scsi_in(sc)->length; + } if (conn->ctask == ctask) conn->ctask = NULL; /* release ref from queuecommand */ @@ -313,11 +403,70 @@ void iscsi_free_mgmt_task(struct iscsi_conn *conn, list_del_init(&mtask->running); if (conn->login_mtask == mtask) return; + + if (conn->ping_mtask == mtask) + conn->ping_mtask = NULL; __kfifo_put(conn->session->mgmtpool.queue, (void*)&mtask, sizeof(void*)); } EXPORT_SYMBOL_GPL(iscsi_free_mgmt_task); +static struct iscsi_mgmt_task * +__iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, + char *data, uint32_t data_size) +{ + struct iscsi_session *session = conn->session; + struct iscsi_mgmt_task *mtask; + + 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)) + /* + * Login and Text are sent serially, in + * request-followed-by-response sequence. + * Same mtask can be used. Same ITT must be used. + * Note that login_mtask is preallocated at conn_create(). + */ + mtask = conn->login_mtask; + else { + BUG_ON(conn->c_stage == ISCSI_CONN_INITIAL_STAGE); + BUG_ON(conn->c_stage == ISCSI_CONN_STOPPED); + + if (!__kfifo_get(session->mgmtpool.queue, + (void*)&mtask, sizeof(void*))) + return NULL; + } + + if (data_size) { + memcpy(mtask->data, data, data_size); + mtask->data_count = data_size; + } else + mtask->data_count = 0; + + memcpy(mtask->hdr, hdr, sizeof(struct iscsi_hdr)); + INIT_LIST_HEAD(&mtask->running); + list_add_tail(&mtask->running, &conn->mgmtqueue); + return mtask; +} + +int iscsi_conn_send_pdu(struct iscsi_cls_conn *cls_conn, struct iscsi_hdr *hdr, + char *data, uint32_t data_size) +{ + struct iscsi_conn *conn = cls_conn->dd_data; + struct iscsi_session *session = conn->session; + int err = 0; + + spin_lock_bh(&session->lock); + if (!__iscsi_conn_send_pdu(conn, hdr, data, data_size)) + err = -EPERM; + spin_unlock_bh(&session->lock); + scsi_queue_work(session->host, &conn->xmitwork); + return err; +} +EXPORT_SYMBOL_GPL(iscsi_conn_send_pdu); + /** * iscsi_cmd_rsp - SCSI Command Response processing * @conn: iscsi connection @@ -352,8 +501,9 @@ static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr, if (datalen < 2) { invalid_datalen: - printk(KERN_ERR "iscsi: Got CHECK_CONDITION but " - "invalid data buffer size of %d\n", datalen); + iscsi_conn_printk(KERN_ERR, conn, + "Got CHECK_CONDITION but invalid data " + "buffer size of %d\n", datalen); sc->result = DID_BAD_TARGET << 16; goto out; } @@ -368,6 +518,18 @@ invalid_datalen: min_t(uint16_t, senselen, SCSI_SENSE_BUFFERSIZE)); } + if (rhdr->flags & (ISCSI_FLAG_CMD_BIDI_UNDERFLOW | + ISCSI_FLAG_CMD_BIDI_OVERFLOW)) { + int res_count = be32_to_cpu(rhdr->bi_residual_count); + + if (scsi_bidi_cmnd(sc) && res_count > 0 && + (rhdr->flags & ISCSI_FLAG_CMD_BIDI_OVERFLOW || + res_count <= scsi_in(sc)->length)) + scsi_in(sc)->resid = res_count; + else + sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status; + } + if (rhdr->flags & (ISCSI_FLAG_CMD_UNDERFLOW | ISCSI_FLAG_CMD_OVERFLOW)) { int res_count = be32_to_cpu(rhdr->residual_count); @@ -375,13 +537,11 @@ invalid_datalen: if (res_count > 0 && (rhdr->flags & ISCSI_FLAG_CMD_OVERFLOW || res_count <= scsi_bufflen(sc))) + /* write side for bidi or uni-io set_resid */ scsi_set_resid(sc, res_count); else sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status; - } else if (rhdr->flags & (ISCSI_FLAG_CMD_BIDI_UNDERFLOW | - ISCSI_FLAG_CMD_BIDI_OVERFLOW)) - sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status; - + } out: debug_scsi("done [sc %lx res %d itt 0x%x]\n", (long)sc, sc->result, ctask->itt); @@ -409,6 +569,39 @@ static void iscsi_tmf_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr) wake_up(&conn->ehwait); } +static void iscsi_send_nopout(struct iscsi_conn *conn, struct iscsi_nopin *rhdr) +{ + struct iscsi_nopout hdr; + struct iscsi_mgmt_task *mtask; + + if (!rhdr && conn->ping_mtask) + return; + + memset(&hdr, 0, sizeof(struct iscsi_nopout)); + hdr.opcode = ISCSI_OP_NOOP_OUT | ISCSI_OP_IMMEDIATE; + hdr.flags = ISCSI_FLAG_CMD_FINAL; + + if (rhdr) { + memcpy(hdr.lun, rhdr->lun, 8); + hdr.ttt = rhdr->ttt; + hdr.itt = RESERVED_ITT; + } else + hdr.ttt = RESERVED_ITT; + + mtask = __iscsi_conn_send_pdu(conn, (struct iscsi_hdr *)&hdr, NULL, 0); + if (!mtask) { + iscsi_conn_printk(KERN_ERR, conn, "Could not send nopout\n"); + return; + } + + /* only track our nops */ + if (!rhdr) { + conn->ping_mtask = mtask; + conn->last_ping = jiffies; + } + scsi_queue_work(conn->session->host, &conn->xmitwork); +} + static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr, char *data, int datalen) { @@ -425,9 +618,10 @@ static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr, if (ntoh24(reject->dlength) >= sizeof(struct iscsi_hdr)) { memcpy(&rejected_pdu, data, sizeof(struct iscsi_hdr)); itt = get_itt(rejected_pdu.itt); - printk(KERN_ERR "itt 0x%x had pdu (op 0x%x) rejected " - "due to DataDigest error.\n", itt, - rejected_pdu.opcode); + iscsi_conn_printk(KERN_ERR, conn, + "itt 0x%x had pdu (op 0x%x) rejected " + "due to DataDigest error.\n", itt, + rejected_pdu.opcode); } } return 0; @@ -444,8 +638,8 @@ static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr, * queuecommand or send generic. session lock must be held and verify * itt must have been called. */ -int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, - char *data, int datalen) +static int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, + char *data, int datalen) { struct iscsi_session *session = conn->session; int opcode = hdr->opcode & ISCSI_OPCODE_MASK, rc = 0; @@ -453,6 +647,7 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, struct iscsi_mgmt_task *mtask; uint32_t itt; + conn->last_recv = jiffies; if (hdr->itt != RESERVED_ITT) itt = get_itt(hdr->itt); else @@ -520,14 +715,24 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, iscsi_free_mgmt_task(conn, mtask); break; case ISCSI_OP_NOOP_IN: - if (hdr->ttt != cpu_to_be32(ISCSI_RESERVED_TAG) || datalen) { + if (hdr->ttt != cpu_to_be32(ISCSI_RESERVED_TAG) || + datalen) { rc = ISCSI_ERR_PROTO; break; } conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1; - if (iscsi_recv_pdu(conn->cls_conn, hdr, data, datalen)) - rc = ISCSI_ERR_CONN_FAILED; + if (conn->ping_mtask != mtask) { + /* + * If this is not in response to one of our + * nops then it must be from userspace. + */ + if (iscsi_recv_pdu(conn->cls_conn, hdr, data, + datalen)) + rc = ISCSI_ERR_CONN_FAILED; + } else + mod_timer(&conn->transport_timer, + jiffies + conn->recv_timeout); iscsi_free_mgmt_task(conn, mtask); break; default: @@ -547,8 +752,7 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, if (hdr->ttt == cpu_to_be32(ISCSI_RESERVED_TAG)) break; - if (iscsi_recv_pdu(conn->cls_conn, hdr, NULL, 0)) - rc = ISCSI_ERR_CONN_FAILED; + iscsi_send_nopout(conn, (struct iscsi_nopin*)hdr); break; case ISCSI_OP_REJECT: rc = iscsi_handle_reject(conn, hdr, data, datalen); @@ -567,7 +771,6 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, return rc; } -EXPORT_SYMBOL_GPL(__iscsi_complete_pdu); int iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, char *data, int datalen) @@ -592,18 +795,13 @@ int iscsi_verify_itt(struct iscsi_conn *conn, struct iscsi_hdr *hdr, if (hdr->itt != RESERVED_ITT) { if (((__force u32)hdr->itt & ISCSI_AGE_MASK) != (session->age << ISCSI_AGE_SHIFT)) { - printk(KERN_ERR "iscsi: received itt %x expected " - "session age (%x)\n", (__force u32)hdr->itt, - session->age & ISCSI_AGE_MASK); + iscsi_conn_printk(KERN_ERR, conn, + "received itt %x expected session " + "age (%x)\n", (__force u32)hdr->itt, + session->age & ISCSI_AGE_MASK); return ISCSI_ERR_BAD_ITT; } - if (((__force u32)hdr->itt & ISCSI_CID_MASK) != - (conn->id << ISCSI_CID_SHIFT)) { - printk(KERN_ERR "iscsi: received itt %x, expected " - "CID (%x)\n", (__force u32)hdr->itt, conn->id); - return ISCSI_ERR_BAD_ITT; - } itt = get_itt(hdr->itt); } else itt = ~0U; @@ -612,16 +810,17 @@ int iscsi_verify_itt(struct iscsi_conn *conn, struct iscsi_hdr *hdr, ctask = session->cmds[itt]; if (!ctask->sc) { - printk(KERN_INFO "iscsi: dropping ctask with " - "itt 0x%x\n", ctask->itt); + iscsi_conn_printk(KERN_INFO, conn, "dropping ctask " + "with itt 0x%x\n", ctask->itt); /* force drop */ return ISCSI_ERR_NO_SCSI_CMD; } if (ctask->sc->SCp.phase != session->age) { - printk(KERN_ERR "iscsi: ctask's session age %d, " - "expected %d\n", ctask->sc->SCp.phase, - session->age); + iscsi_conn_printk(KERN_ERR, conn, + "iscsi: ctask's session age %d, " + "expected %d\n", ctask->sc->SCp.phase, + session->age); return ISCSI_ERR_SESSION_FAILED; } } @@ -666,7 +865,7 @@ static void iscsi_prep_mtask(struct iscsi_conn *conn, */ nop->cmdsn = cpu_to_be32(session->cmdsn); if (hdr->itt != RESERVED_ITT) { - hdr->itt = build_itt(mtask->itt, conn->id, session->age); + hdr->itt = build_itt(mtask->itt, session->age); /* * TODO: We always use immediate, so we never hit this. * If we start to send tmfs or nops as non-immediate then @@ -817,14 +1016,14 @@ check_mgmt: conn->ctask = list_entry(conn->xmitqueue.next, struct iscsi_cmd_task, running); if (conn->session->state == ISCSI_STATE_LOGGING_OUT) { - fail_command(conn, conn->ctask, DID_NO_CONNECT << 16); + fail_command(conn, conn->ctask, DID_IMM_RETRY << 16); continue; } if (iscsi_prep_scsi_cmd_pdu(conn->ctask)) { fail_command(conn, conn->ctask, DID_ABORT << 16); continue; } - conn->session->tt->init_cmd_task(conn->ctask); + conn->ctask->state = ISCSI_TASK_RUNNING; list_move_tail(conn->xmitqueue.next, &conn->run_list); rc = iscsi_xmit_ctask(conn); @@ -892,6 +1091,7 @@ enum { FAILURE_SESSION_IN_RECOVERY, FAILURE_SESSION_RECOVERY_TIMEOUT, FAILURE_SESSION_LOGGING_OUT, + FAILURE_SESSION_NOT_READY, }; int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *)) @@ -907,10 +1107,17 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *)) sc->SCp.ptr = NULL; host = sc->device->host; - session = iscsi_hostdata(host->hostdata); + spin_unlock(host->host_lock); + session = iscsi_hostdata(host->hostdata); spin_lock(&session->lock); + reason = iscsi_session_chkready(session_to_cls(session)); + if (reason) { + sc->result = reason; + goto fault; + } + /* * ISCSI_STATE_FAILED is a temp. state. The recovery * code will decide what is best to do with command queued @@ -924,23 +1131,26 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *)) * be entering our queuecommand while a block is starting * up because the block code is not locked) */ - if (session->state == ISCSI_STATE_IN_RECOVERY) { - reason = FAILURE_SESSION_IN_RECOVERY; - goto reject; - } - switch (session->state) { + case ISCSI_STATE_IN_RECOVERY: + reason = FAILURE_SESSION_IN_RECOVERY; + sc->result = DID_IMM_RETRY << 16; + break; + case ISCSI_STATE_LOGGING_OUT: + reason = FAILURE_SESSION_LOGGING_OUT; + sc->result = DID_IMM_RETRY << 16; + break; case ISCSI_STATE_RECOVERY_FAILED: reason = FAILURE_SESSION_RECOVERY_TIMEOUT; + sc->result = DID_NO_CONNECT << 16; break; case ISCSI_STATE_TERMINATE: reason = FAILURE_SESSION_TERMINATE; - break; - case ISCSI_STATE_LOGGING_OUT: - reason = FAILURE_SESSION_LOGGING_OUT; + sc->result = DID_NO_CONNECT << 16; break; default: reason = FAILURE_SESSION_FREED; + sc->result = DID_NO_CONNECT << 16; } goto fault; } @@ -948,6 +1158,7 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *)) conn = session->leadconn; if (!conn) { reason = FAILURE_SESSION_FREED; + sc->result = DID_NO_CONNECT << 16; goto fault; } @@ -976,20 +1187,26 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *)) spin_unlock(&session->lock); scsi_queue_work(host, &conn->xmitwork); + spin_lock(host->host_lock); return 0; reject: spin_unlock(&session->lock); debug_scsi("cmd 0x%x rejected (%d)\n", sc->cmnd[0], reason); + spin_lock(host->host_lock); return SCSI_MLQUEUE_HOST_BUSY; fault: spin_unlock(&session->lock); - printk(KERN_ERR "iscsi: cmd 0x%x is not queued (%d)\n", - sc->cmnd[0], reason); - sc->result = (DID_NO_CONNECT << 16); - scsi_set_resid(sc, scsi_bufflen(sc)); + debug_scsi("iscsi: cmd 0x%x is not queued (%d)\n", sc->cmnd[0], reason); + if (!scsi_bidi_cmnd(sc)) + scsi_set_resid(sc, scsi_bufflen(sc)); + else { + scsi_out(sc)->resid = scsi_out(sc)->length; + scsi_in(sc)->resid = scsi_in(sc)->length; + } sc->scsi_done(sc); + spin_lock(host->host_lock); return 0; } EXPORT_SYMBOL_GPL(iscsi_queuecommand); @@ -1003,62 +1220,6 @@ int iscsi_change_queue_depth(struct scsi_device *sdev, int depth) } EXPORT_SYMBOL_GPL(iscsi_change_queue_depth); -static struct iscsi_mgmt_task * -__iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, - char *data, uint32_t data_size) -{ - struct iscsi_session *session = conn->session; - struct iscsi_mgmt_task *mtask; - - 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)) - /* - * Login and Text are sent serially, in - * request-followed-by-response sequence. - * Same mtask can be used. Same ITT must be used. - * Note that login_mtask is preallocated at conn_create(). - */ - mtask = conn->login_mtask; - else { - BUG_ON(conn->c_stage == ISCSI_CONN_INITIAL_STAGE); - BUG_ON(conn->c_stage == ISCSI_CONN_STOPPED); - - if (!__kfifo_get(session->mgmtpool.queue, - (void*)&mtask, sizeof(void*))) - return NULL; - } - - if (data_size) { - memcpy(mtask->data, data, data_size); - mtask->data_count = data_size; - } else - mtask->data_count = 0; - - memcpy(mtask->hdr, hdr, sizeof(struct iscsi_hdr)); - INIT_LIST_HEAD(&mtask->running); - list_add_tail(&mtask->running, &conn->mgmtqueue); - return mtask; -} - -int iscsi_conn_send_pdu(struct iscsi_cls_conn *cls_conn, struct iscsi_hdr *hdr, - char *data, uint32_t data_size) -{ - struct iscsi_conn *conn = cls_conn->dd_data; - struct iscsi_session *session = conn->session; - int err = 0; - - spin_lock_bh(&session->lock); - if (!__iscsi_conn_send_pdu(conn, hdr, data, data_size)) - err = -EPERM; - spin_unlock_bh(&session->lock); - scsi_queue_work(session->host, &conn->xmitwork); - return err; -} -EXPORT_SYMBOL_GPL(iscsi_conn_send_pdu); - void iscsi_session_recovery_timedout(struct iscsi_cls_session *cls_session) { struct iscsi_session *session = class_to_transport_session(cls_session); @@ -1079,17 +1240,19 @@ int iscsi_eh_host_reset(struct scsi_cmnd *sc) struct iscsi_session *session = iscsi_hostdata(host->hostdata); struct iscsi_conn *conn = session->leadconn; + mutex_lock(&session->eh_mutex); spin_lock_bh(&session->lock); if (session->state == ISCSI_STATE_TERMINATE) { failed: debug_scsi("failing host reset: session terminated " "[CID %d age %d]\n", conn->id, 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 @@ -1104,13 +1267,15 @@ 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) - printk(KERN_INFO "iscsi: host reset succeeded\n"); + iscsi_session_printk(KERN_INFO, session, + "host reset succeeded\n"); else goto failed; spin_unlock_bh(&session->lock); - + mutex_unlock(&session->eh_mutex); return SUCCESS; } EXPORT_SYMBOL_GPL(iscsi_eh_host_reset); @@ -1131,7 +1296,8 @@ static void iscsi_tmf_timedout(unsigned long data) } static int iscsi_exec_task_mgmt_fn(struct iscsi_conn *conn, - struct iscsi_tm *hdr, int age) + struct iscsi_tm *hdr, int age, + int timeout) { struct iscsi_session *session = conn->session; struct iscsi_mgmt_task *mtask; @@ -1146,7 +1312,7 @@ static int iscsi_exec_task_mgmt_fn(struct iscsi_conn *conn, return -EPERM; } conn->tmfcmd_pdus_cnt++; - conn->tmf_timer.expires = 30 * HZ + jiffies; + conn->tmf_timer.expires = timeout * HZ + jiffies; conn->tmf_timer.function = iscsi_tmf_timedout; conn->tmf_timer.data = (unsigned long)conn; add_timer(&conn->tmf_timer); @@ -1184,7 +1350,8 @@ static int iscsi_exec_task_mgmt_fn(struct iscsi_conn *conn, * Fail commands. session lock held and recv side suspended and xmit * thread flushed */ -static void fail_all_commands(struct iscsi_conn *conn, unsigned lun) +static void fail_all_commands(struct iscsi_conn *conn, unsigned lun, + int error) { struct iscsi_cmd_task *ctask, *tmp; @@ -1196,7 +1363,7 @@ static void fail_all_commands(struct iscsi_conn *conn, unsigned lun) if (lun == ctask->sc->device->lun || lun == -1) { debug_scsi("failing pending sc %p itt 0x%x\n", ctask->sc, ctask->itt); - fail_command(conn, ctask, DID_BUS_BUSY << 16); + fail_command(conn, ctask, error << 16); } } @@ -1204,7 +1371,7 @@ static void fail_all_commands(struct iscsi_conn *conn, unsigned lun) if (lun == ctask->sc->device->lun || lun == -1) { debug_scsi("failing requeued sc %p itt 0x%x\n", ctask->sc, ctask->itt); - fail_command(conn, ctask, DID_BUS_BUSY << 16); + fail_command(conn, ctask, error << 16); } } @@ -1230,6 +1397,102 @@ static void iscsi_start_tx(struct iscsi_conn *conn) scsi_queue_work(conn->session->host, &conn->xmitwork); } +static enum scsi_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *scmd) +{ + struct iscsi_cls_session *cls_session; + struct iscsi_session *session; + struct iscsi_conn *conn; + enum scsi_eh_timer_return rc = EH_NOT_HANDLED; + + cls_session = starget_to_session(scsi_target(scmd->device)); + session = class_to_transport_session(cls_session); + + debug_scsi("scsi cmd %p timedout\n", scmd); + + spin_lock(&session->lock); + if (session->state != ISCSI_STATE_LOGGED_IN) { + /* + * We are probably in the middle of iscsi recovery so let + * that complete and handle the error. + */ + rc = EH_RESET_TIMER; + goto done; + } + + conn = session->leadconn; + if (!conn) { + /* In the middle of shuting down */ + rc = EH_RESET_TIMER; + goto done; + } + + if (!conn->recv_timeout && !conn->ping_timeout) + goto done; + /* + * if the ping timedout then we are in the middle of cleaning up + * and can let the iscsi eh handle it + */ + if (time_before_eq(conn->last_recv + (conn->recv_timeout * HZ) + + (conn->ping_timeout * HZ), jiffies)) + rc = EH_RESET_TIMER; + /* + * if we are about to check the transport then give the command + * more time + */ + if (time_before_eq(conn->last_recv + (conn->recv_timeout * HZ), + jiffies)) + rc = EH_RESET_TIMER; + /* if in the middle of checking the transport then give us more time */ + if (conn->ping_mtask) + rc = EH_RESET_TIMER; +done: + spin_unlock(&session->lock); + debug_scsi("return %s\n", rc == EH_RESET_TIMER ? "timer reset" : "nh"); + return rc; +} + +static void iscsi_check_transport_timeouts(unsigned long data) +{ + struct iscsi_conn *conn = (struct iscsi_conn *)data; + struct iscsi_session *session = conn->session; + unsigned long recv_timeout, next_timeout = 0, last_recv; + + spin_lock(&session->lock); + if (session->state != ISCSI_STATE_LOGGED_IN) + goto done; + + recv_timeout = conn->recv_timeout; + if (!recv_timeout) + goto done; + + recv_timeout *= HZ; + last_recv = conn->last_recv; + if (conn->ping_mtask && + time_before_eq(conn->last_ping + (conn->ping_timeout * HZ), + jiffies)) { + iscsi_conn_printk(KERN_ERR, conn, "ping timeout of %d secs " + "expired, last rx %lu, last ping %lu, " + "now %lu\n", conn->ping_timeout, last_recv, + conn->last_ping, jiffies); + spin_unlock(&session->lock); + iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); + return; + } + + if (time_before_eq(last_recv + recv_timeout, jiffies)) { + /* send a ping to try to provoke some traffic */ + debug_scsi("Sending nopout as ping on conn %p\n", conn); + iscsi_send_nopout(conn, NULL); + next_timeout = conn->last_ping + (conn->ping_timeout * HZ); + } else + next_timeout = last_recv + recv_timeout; + + debug_scsi("Setting next tmo %lu\n", next_timeout); + mod_timer(&conn->transport_timer, next_timeout); +done: + spin_unlock(&session->lock); +} + static void iscsi_prep_abort_task_pdu(struct iscsi_cmd_task *ctask, struct iscsi_tm *hdr) { @@ -1301,7 +1564,7 @@ int iscsi_eh_abort(struct scsi_cmnd *sc) hdr = &conn->tmhdr; iscsi_prep_abort_task_pdu(ctask, hdr); - if (iscsi_exec_task_mgmt_fn(conn, hdr, age)) { + if (iscsi_exec_task_mgmt_fn(conn, hdr, age, session->abort_timeout)) { rc = FAILED; goto failed; } @@ -1362,7 +1625,7 @@ static void iscsi_prep_lun_reset_pdu(struct scsi_cmnd *sc, struct iscsi_tm *hdr) 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); - hdr->rtt = ISCSI_RESERVED_TAG; + hdr->rtt = RESERVED_ITT; } int iscsi_eh_device_reset(struct scsi_cmnd *sc) @@ -1393,7 +1656,8 @@ int iscsi_eh_device_reset(struct scsi_cmnd *sc) hdr = &conn->tmhdr; iscsi_prep_lun_reset_pdu(sc, hdr); - if (iscsi_exec_task_mgmt_fn(conn, hdr, session->age)) { + if (iscsi_exec_task_mgmt_fn(conn, hdr, session->age, + session->lu_reset_timeout)) { rc = FAILED; goto unlock; } @@ -1417,7 +1681,7 @@ int iscsi_eh_device_reset(struct scsi_cmnd *sc) /* need to grab the recv lock then session lock */ write_lock_bh(conn->recv_lock); spin_lock(&session->lock); - fail_all_commands(conn, sc->device->lun); + fail_all_commands(conn, sc->device->lun, DID_ERROR); conn->tmf_state = TMF_INITIAL; spin_unlock(&session->lock); write_unlock_bh(conn->recv_lock); @@ -1545,8 +1809,8 @@ iscsi_session_setup(struct iscsi_transport *iscsit, qdepth = ISCSI_DEF_CMD_PER_LUN; } - if (cmds_max < 2 || (cmds_max & (cmds_max - 1)) || - cmds_max >= ISCSI_MGMT_ITT_OFFSET) { + if (!is_power_of_2(cmds_max) || cmds_max >= ISCSI_MGMT_ITT_OFFSET || + cmds_max < 2) { if (cmds_max != 0) printk(KERN_ERR "iscsi: invalid can_queue of %d. " "can_queue must be a power of 2 and between " @@ -1569,12 +1833,16 @@ iscsi_session_setup(struct iscsi_transport *iscsit, shost->max_cmd_len = iscsit->max_cmd_len; shost->transportt = scsit; shost->transportt->create_work_queue = 1; + shost->transportt->eh_timed_out = iscsi_eh_cmd_timed_out; *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->fast_abort = 1; + session->lu_reset_timeout = 15; + session->abort_timeout = 10; session->mgmtpool_max = ISCSI_MGMT_CMDS_MAX; session->cmds_max = cmds_max; session->queued_cmdsn = session->cmdsn = initial_cmdsn; @@ -1659,7 +1927,7 @@ void iscsi_session_teardown(struct iscsi_cls_session *cls_session) struct iscsi_session *session = iscsi_hostdata(shost->hostdata); struct module *owner = cls_session->transport->owner; - iscsi_unblock_session(cls_session); + iscsi_remove_session(cls_session); scsi_remove_host(shost); iscsi_pool_free(&session->mgmtpool); @@ -1674,7 +1942,7 @@ void iscsi_session_teardown(struct iscsi_cls_session *cls_session) kfree(session->hwaddress); kfree(session->initiatorname); - iscsi_destroy_session(cls_session); + iscsi_free_session(cls_session); scsi_host_put(shost); module_put(owner); } @@ -1705,6 +1973,11 @@ iscsi_conn_setup(struct iscsi_cls_session *cls_session, uint32_t conn_idx) conn->id = conn_idx; conn->exp_statsn = 0; conn->tmf_state = TMF_INITIAL; + + init_timer(&conn->transport_timer); + conn->transport_timer.data = (unsigned long)conn; + conn->transport_timer.function = iscsi_check_transport_timeouts; + INIT_LIST_HEAD(&conn->run_list); INIT_LIST_HEAD(&conn->mgmt_run_list); INIT_LIST_HEAD(&conn->mgmtqueue); @@ -1754,6 +2027,8 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn) struct iscsi_session *session = conn->session; unsigned long flags; + del_timer_sync(&conn->transport_timer); + spin_lock_bh(&session->lock); conn->c_stage = ISCSI_CONN_CLEANUP_WAIT; if (session->leadconn == conn) { @@ -1777,9 +2052,10 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn) } spin_unlock_irqrestore(session->host->host_lock, flags); msleep_interruptible(500); - printk(KERN_INFO "iscsi: scsi conn_destroy(): host_busy %d " - "host_failed %d\n", session->host->host_busy, - session->host->host_failed); + iscsi_conn_printk(KERN_INFO, conn, "iscsi conn_destroy(): " + "host_busy %d host_failed %d\n", + session->host->host_busy, + session->host->host_failed); /* * force eh_abort() to unblock */ @@ -1808,23 +2084,42 @@ int iscsi_conn_start(struct iscsi_cls_conn *cls_conn) struct iscsi_session *session = conn->session; if (!session) { - printk(KERN_ERR "iscsi: can't start unbound connection\n"); + iscsi_conn_printk(KERN_ERR, conn, + "can't start unbound connection\n"); return -EPERM; } if ((session->imm_data_en || !session->initial_r2t_en) && session->first_burst > session->max_burst) { - printk("iscsi: invalid burst lengths: " - "first_burst %d max_burst %d\n", - session->first_burst, session->max_burst); + iscsi_conn_printk(KERN_INFO, conn, "invalid burst lengths: " + "first_burst %d max_burst %d\n", + session->first_burst, session->max_burst); return -EINVAL; } + if (conn->ping_timeout && !conn->recv_timeout) { + iscsi_conn_printk(KERN_ERR, conn, "invalid recv timeout of " + "zero. Using 5 seconds\n."); + conn->recv_timeout = 5; + } + + if (conn->recv_timeout && !conn->ping_timeout) { + iscsi_conn_printk(KERN_ERR, conn, "invalid ping timeout of " + "zero. Using 5 seconds.\n"); + conn->ping_timeout = 5; + } + spin_lock_bh(&session->lock); conn->c_stage = ISCSI_CONN_STARTED; session->state = ISCSI_STATE_LOGGED_IN; session->queued_cmdsn = session->cmdsn; + conn->last_recv = jiffies; + conn->last_ping = jiffies; + if (conn->recv_timeout && conn->ping_timeout) + mod_timer(&conn->transport_timer, + jiffies + (conn->recv_timeout * HZ)); + switch(conn->stop_stage) { case STOP_CONN_RECOVER: /* @@ -1834,11 +2129,9 @@ int iscsi_conn_start(struct iscsi_cls_conn *cls_conn) conn->stop_stage = 0; conn->tmf_state = TMF_INITIAL; session->age++; - spin_unlock_bh(&session->lock); - - iscsi_unblock_session(session_to_cls(session)); - wake_up(&conn->ehwait); - return 0; + if (session->age == 16) + session->age = 0; + break; case STOP_CONN_TERM: conn->stop_stage = 0; break; @@ -1847,6 +2140,8 @@ int iscsi_conn_start(struct iscsi_cls_conn *cls_conn) } spin_unlock_bh(&session->lock); + iscsi_unblock_session(session_to_cls(session)); + wake_up(&conn->ehwait); return 0; } EXPORT_SYMBOL_GPL(iscsi_conn_start); @@ -1876,6 +2171,8 @@ static void iscsi_start_session_recovery(struct iscsi_session *session, { int old_stop_stage; + del_timer_sync(&conn->transport_timer); + mutex_lock(&session->eh_mutex); spin_lock_bh(&session->lock); if (conn->stop_stage == STOP_CONN_TERM) { @@ -1936,7 +2233,8 @@ static void iscsi_start_session_recovery(struct iscsi_session *session, * flush queues. */ spin_lock_bh(&session->lock); - fail_all_commands(conn, -1); + fail_all_commands(conn, -1, + STOP_CONN_RECOVER ? DID_BUS_BUSY : DID_ERROR); flush_control_queues(session, conn); spin_unlock_bh(&session->lock); mutex_unlock(&session->eh_mutex); @@ -1953,7 +2251,8 @@ void iscsi_conn_stop(struct iscsi_cls_conn *cls_conn, int flag) iscsi_start_session_recovery(session, conn, flag); break; default: - printk(KERN_ERR "iscsi: invalid stop flag %d\n", flag); + iscsi_conn_printk(KERN_ERR, conn, + "invalid stop flag %d\n", flag); } } EXPORT_SYMBOL_GPL(iscsi_conn_stop); @@ -1990,6 +2289,18 @@ int iscsi_set_param(struct iscsi_cls_conn *cls_conn, case ISCSI_PARAM_FAST_ABORT: sscanf(buf, "%d", &session->fast_abort); break; + case ISCSI_PARAM_ABORT_TMO: + sscanf(buf, "%d", &session->abort_timeout); + break; + case ISCSI_PARAM_LU_RESET_TMO: + sscanf(buf, "%d", &session->lu_reset_timeout); + break; + case ISCSI_PARAM_PING_TMO: + sscanf(buf, "%d", &conn->ping_timeout); + break; + case ISCSI_PARAM_RECV_TMO: + sscanf(buf, "%d", &conn->recv_timeout); + break; case ISCSI_PARAM_MAX_RECV_DLENGTH: sscanf(buf, "%d", &conn->max_recv_dlength); break; @@ -2107,6 +2418,12 @@ int iscsi_session_get_param(struct iscsi_cls_session *cls_session, case ISCSI_PARAM_FAST_ABORT: len = sprintf(buf, "%d\n", session->fast_abort); break; + case ISCSI_PARAM_ABORT_TMO: + len = sprintf(buf, "%d\n", session->abort_timeout); + break; + case ISCSI_PARAM_LU_RESET_TMO: + len = sprintf(buf, "%d\n", session->lu_reset_timeout); + break; case ISCSI_PARAM_INITIAL_R2T_EN: len = sprintf(buf, "%d\n", session->initial_r2t_en); break; @@ -2164,6 +2481,12 @@ int iscsi_conn_get_param(struct iscsi_cls_conn *cls_conn, int len; switch(param) { + case ISCSI_PARAM_PING_TMO: + len = sprintf(buf, "%u\n", conn->ping_timeout); + break; + case ISCSI_PARAM_RECV_TMO: + len = sprintf(buf, "%u\n", conn->recv_timeout); + break; case ISCSI_PARAM_MAX_RECV_DLENGTH: len = sprintf(buf, "%u\n", conn->max_recv_dlength); break;