diff --git a/iscsi-scst/kernel/config.c b/iscsi-scst/kernel/config.c index 5bdd98fa6..149f230a8 100644 --- a/iscsi-scst/kernel/config.c +++ b/iscsi-scst/kernel/config.c @@ -490,7 +490,7 @@ static void iscsi_dump_char(int ch) if (ch < 0) { while ((i % 16) != 0) { - printk(LOG_FLAG " "); + printk(" "); text[i] = ' '; i++; if ((i % 16) == 0) @@ -503,7 +503,7 @@ static void iscsi_dump_char(int ch) } text[i] = (ch < 0x20 || (ch >= 0x80 && ch <= 0xa0)) ? ' ' : ch; - printk(LOG_FLAG " %02x", ch); + printk(" %02x", ch); i++; if ((i % 16) == 0) { printk(" | %.16s |\n", text); @@ -519,18 +519,18 @@ void iscsi_dump_pdu(struct iscsi_pdu *pdu) int i; buf = (void *)&pdu->bhs; - printk(LOG_FLAG "BHS: (%p,%zd)\n", buf, sizeof(pdu->bhs)); + printk("BHS: (%p,%zd)\n", buf, sizeof(pdu->bhs)); for (i = 0; i < sizeof(pdu->bhs); i++) iscsi_dump_char(*buf++); iscsi_dump_char(-1); buf = (void *)pdu->ahs; - printk(LOG_FLAG "AHS: (%p,%d)\n", buf, pdu->ahssize); + printk("AHS: (%p,%d)\n", buf, pdu->ahssize); for (i = 0; i < pdu->ahssize; i++) iscsi_dump_char(*buf++); iscsi_dump_char(-1); - printk(LOG_FLAG "Data: (%d)\n", pdu->datasize); + printk("Data: (%d)\n", pdu->datasize); } } #endif /* CONFIG_SCST_DEBUG */ diff --git a/iscsi-scst/kernel/digest.c b/iscsi-scst/kernel/digest.c index a5dc7f1f0..5c6d00a95 100644 --- a/iscsi-scst/kernel/digest.c +++ b/iscsi-scst/kernel/digest.c @@ -57,10 +57,11 @@ int digest_init(struct iscsi_conn *conn) return 0; } -static u32 evaluate_crc32_from_sg(struct scatterlist *sg, int total, - int pad_bytes) +static u32 evaluate_crc32_from_sg(struct scatterlist *sg, int nbytes, + uint32_t padding) { u32 crc = ~0; + int pad_bytes = ((nbytes + 3) & -4) - nbytes; #ifdef CONFIG_SCST_ISCSI_DEBUG_DIGEST_FAILURES if (((scst_random() % 100000) == 752)) { @@ -70,24 +71,15 @@ static u32 evaluate_crc32_from_sg(struct scatterlist *sg, int total, #endif #if defined(CONFIG_LIBCRC32C_MODULE) || defined(CONFIG_LIBCRC32C) - while (total > 0) { - int d = min(min(total, (int)(sg->length)), - (int)(PAGE_SIZE - sg->offset)); - + while (nbytes > 0) { + int d = min(nbytes, (int)(sg->length)); crc = crc32c(crc, sg_virt(sg), d); - total -= d; + nbytes -= d; sg++; } - if (pad_bytes) { - u32 padding = 0; - /* - * Digest includes also padding for aligned pdu length, - * hopefully it is always filled with 0s in pdu (according to - * crypto/crc32c.c - */ + if (pad_bytes) crc = crc32c(crc, (u8 *)&padding, pad_bytes); - } #endif return ~cpu_to_le32(crc); @@ -97,23 +89,25 @@ static u32 digest_header(struct iscsi_pdu *pdu) { struct scatterlist sg[2]; unsigned int nbytes = sizeof(struct iscsi_hdr); + int asize = (pdu->ahssize + 3) & -4; sg_init_table(sg, 2); sg_set_buf(&sg[0], &pdu->bhs, nbytes); if (pdu->ahssize) { - sg_set_buf(&sg[1], pdu->ahs, pdu->ahssize); - nbytes += pdu->ahssize; + sg_set_buf(&sg[1], pdu->ahs, asize); + nbytes += asize; } + EXTRACHECKS_BUG_ON((nbytes & 3) != 0); return evaluate_crc32_from_sg(sg, nbytes, 0); } -static u32 digest_data(struct iscsi_cmnd *cmd, u32 osize, u32 offset) +static u32 digest_data(struct iscsi_cmnd *cmd, u32 size, u32 offset, + uint32_t padding) { struct scatterlist *sg = cmd->sg; int idx, count; struct scatterlist saved_sg; - u32 size = (osize + 3) & ~3; u32 crc; offset += sg[0].offset; @@ -130,7 +124,7 @@ static u32 digest_data(struct iscsi_cmnd *cmd, u32 osize, u32 offset) sg[idx].offset = offset; sg[idx].length -= offset - saved_sg.offset; - crc = evaluate_crc32_from_sg(sg + idx, osize, size - osize); + crc = evaluate_crc32_from_sg(sg + idx, size, padding); sg[idx] = saved_sg; return crc; @@ -178,7 +172,8 @@ int digest_rx_data(struct iscsi_cmnd *cmnd) offset = 0; } - crc = digest_data(req, cmnd->pdu.datasize, offset); + crc = digest_data(req, cmnd->pdu.datasize, offset, + cmnd->conn->rpadding); if (unlikely(crc != cmnd->ddigest)) { PRINT_ERROR("%s", "RX data digest failed"); @@ -213,11 +208,7 @@ void digest_tx_data(struct iscsi_cmnd *cmnd) offset = 0; } - /* - * cmnd is used here regardless of its sg comes from parent or was - * allocated for this cmnd only, see cmnd_send_pdu() - */ - cmnd->ddigest = digest_data(cmnd, cmnd->pdu.datasize, offset); + cmnd->ddigest = digest_data(cmnd, cmnd->pdu.datasize, offset, 0); TRACE_DBG("TX data digest for cmd %p: %x (offset %d, opcode %x)", cmnd, cmnd->ddigest, offset, cmnd_opcode(cmnd)); } diff --git a/iscsi-scst/kernel/iscsi.c b/iscsi-scst/kernel/iscsi.c index 3e1fffd07..de7992c56 100644 --- a/iscsi-scst/kernel/iscsi.c +++ b/iscsi-scst/kernel/iscsi.c @@ -54,6 +54,8 @@ DECLARE_WAIT_QUEUE_HEAD(iscsi_wr_waitQ); static struct page *dummy_page; static struct scatterlist dummy_sg; +static uint8_t sense_unexpected_unsolicited_data[SCST_STANDARD_SENSE_LEN]; + struct iscsi_thread_t { struct task_struct *thr; struct list_head threads_list_entry; @@ -242,13 +244,14 @@ void cmnd_done(struct iscsi_cmnd *cmnd) /* Order between above and below code is important! */ - if (cmnd->scst_cmd) { + if ((cmnd->scst_cmd != NULL) || (cmnd->scst_aen != NULL)) { switch (cmnd->scst_state) { case ISCSI_CMD_STATE_PROCESSED: TRACE_DBG("cmd %p PROCESSED", cmnd); scst_tgt_cmd_done(cmnd->scst_cmd, SCST_CONTEXT_DIRECT); break; + case ISCSI_CMD_STATE_AFTER_PREPROC: { struct scst_cmd *scst_cmd = cmnd->scst_cmd; @@ -260,6 +263,12 @@ void cmnd_done(struct iscsi_cmnd *cmnd) SCST_CONTEXT_THREAD); break; } + + case ISCSI_CMD_STATE_AEN: + TRACE_DBG("cmd %p AEN PROCESSED", cmnd); + scst_aen_done(cmnd->scst_aen); + break; + default: PRINT_CRIT_ERROR("Unexpected cmnd scst state " "%d", cmnd->scst_state); @@ -283,7 +292,7 @@ void cmnd_done(struct iscsi_cmnd *cmnd) if (cmnd->own_sg) { TRACE_DBG("%s", "own_sg"); - if (cmnd->sg != &dummy_sg) + if ((cmnd->sg != &dummy_sg) && (cmnd->sg != cmnd->rsp_sg)) scst_free(cmnd->sg, cmnd->sg_cnt); #ifdef CONFIG_SCST_DEBUG cmnd->own_sg = 0; @@ -577,22 +586,6 @@ static void iscsi_cmnd_init_write(struct iscsi_cmnd *rsp, int flags) return; } -static void iscsi_set_datasize(struct iscsi_cmnd *cmnd, u32 offset, u32 size) -{ - cmnd->pdu.datasize = size; - - if (size & 3) { - u32 last_off = offset + size; - int idx = last_off >> PAGE_SHIFT; - u8 *p = (u8 *)page_address(sg_page(&cmnd->sg[idx])) + - (last_off & ~PAGE_MASK); - int i = 4 - (size & 3); - while (i--) - *p++ = 0; - } - return; -} - static void send_data_rsp(struct iscsi_cmnd *req, u8 status, int send_status) { struct iscsi_cmnd *rsp; @@ -625,7 +618,7 @@ static void send_data_rsp(struct iscsi_cmnd *req, u8 status, int send_status) if (size <= pdusize) { TRACE_DBG("offset %d, size %d", offset, size); - iscsi_set_datasize(rsp, offset, size); + rsp->pdu.datasize = size; if (send_status) { TRACE_DBG("status %x", status); rsp_hdr->flags = @@ -649,7 +642,7 @@ static void send_data_rsp(struct iscsi_cmnd *req, u8 status, int send_status) TRACE_DBG("pdusize %d, offset %d, size %d", pdusize, offset, size); - iscsi_set_datasize(rsp, offset, pdusize); + rsp->pdu.datasize = pdusize; size -= pdusize; offset += pdusize; @@ -666,7 +659,6 @@ static struct iscsi_cmnd *create_status_rsp(struct iscsi_cmnd *req, int status, { struct iscsi_cmnd *rsp; struct iscsi_scsi_rsp_hdr *rsp_hdr; - struct iscsi_sense_data *sense; struct scatterlist *sg; rsp = iscsi_cmnd_create_rsp_cmnd(req); @@ -681,27 +673,19 @@ static struct iscsi_cmnd *create_status_rsp(struct iscsi_cmnd *req, int status, if (SCST_SENSE_VALID(sense_buf)) { TRACE_DBG("%s", "SENSE VALID"); - /* ToDo: __GFP_NOFAIL ?? */ - sg = rsp->sg = scst_alloc(PAGE_SIZE, GFP_KERNEL|__GFP_NOFAIL, - &rsp->sg_cnt); - if (sg == NULL) { - ;/* ToDo */; - } - rsp->own_sg = 1; - sense = (struct iscsi_sense_data *)page_address(sg_page(&sg[0])); - sense->length = cpu_to_be16(sense_len); - memcpy(sense->data, sense_buf, sense_len); - rsp->pdu.datasize = sizeof(struct iscsi_sense_data) + sense_len; - rsp->bufflen = (rsp->pdu.datasize + 3) & -4; - if (rsp->bufflen - rsp->pdu.datasize) { - unsigned int i = rsp->pdu.datasize; - u8 *p = (u8 *)sense + i; - while (i < rsp->bufflen) { - *p++ = 0; - i++; - } - } + sg = rsp->sg = rsp->rsp_sg; + rsp->sg_cnt = 2; + rsp->own_sg = 1; + + sg_init_table(sg, 2); + sg_set_buf(&sg[0], &rsp->sense_hdr, sizeof(rsp->sense_hdr)); + sg_set_buf(&sg[1], sense_buf, sense_len); + + rsp->sense_hdr.length = cpu_to_be16(sense_len); + + rsp->pdu.datasize = sizeof(rsp->sense_hdr) + sense_len; + rsp->bufflen = rsp->pdu.datasize; } else { rsp->pdu.datasize = 0; rsp->bufflen = 0; @@ -710,26 +694,11 @@ static struct iscsi_cmnd *create_status_rsp(struct iscsi_cmnd *req, int status, return rsp; } -static struct iscsi_cmnd *create_sense_rsp(struct iscsi_cmnd *req, - u8 sense_key, u8 asc, u8 ascq) -{ - u8 sense[14]; - memset(sense, 0, sizeof(sense)); - sense[0] = 0xf0; - sense[2] = sense_key; - sense[7] = 6; /* Additional sense length */ - sense[12] = asc; - sense[13] = ascq; - return create_status_rsp(req, SAM_STAT_CHECK_CONDITION, sense, - sizeof(sense)); -} - static void iscsi_cmnd_reject(struct iscsi_cmnd *req, int reason) { struct iscsi_cmnd *rsp; struct iscsi_reject_hdr *rsp_hdr; struct scatterlist *sg; - char *addr; TRACE_MGMT_DBG("Reject: req %p, reason %x", req, reason); @@ -744,16 +713,10 @@ static void iscsi_cmnd_reject(struct iscsi_cmnd *req, int reason) rsp_hdr->ffffffff = ISCSI_RESERVED_TAG; rsp_hdr->reason = reason; - /* ToDo: __GFP_NOFAIL ?? */ - sg = rsp->sg = scst_alloc(PAGE_SIZE, GFP_KERNEL|__GFP_NOFAIL, - &rsp->sg_cnt); - if (sg == NULL) { - ;/* ToDo */; - } + sg = rsp->sg = rsp->rsp_sg; + rsp->sg_cnt = 1; rsp->own_sg = 1; - addr = page_address(sg_page(&sg[0])); - clear_page(addr); - memcpy(addr, &req->pdu.bhs, sizeof(struct iscsi_hdr)); + sg_init_one(sg, &req->pdu.bhs, sizeof(struct iscsi_hdr)); rsp->bufflen = rsp->pdu.datasize = sizeof(struct iscsi_hdr); iscsi_cmnd_init_write(rsp, ISCSI_INIT_WRITE_REMOVE_HASH | @@ -971,7 +934,6 @@ static void cmnd_prepare_get_rejected_cmd_data(struct iscsi_cmnd *cmnd) addr = (char __force __user *)(page_address(sg_page(&sg[0]))); sBUG_ON(addr == NULL); - size = (size + 3) & -4; conn->read_size = size; for (i = 0; size > PAGE_SIZE; i++, size -= cmnd->bufflen) { /* We already checked pdu.datasize in check_segment_length() */ @@ -1059,7 +1021,7 @@ static int cmnd_prepare_recv_pdu(struct iscsi_conn *conn, offset &= ~PAGE_MASK; conn->read_msg.msg_iov = conn->read_iov; - conn->read_size = size = (size + 3) & -4; + conn->read_size = size; i = 0; while (1) { @@ -1077,7 +1039,6 @@ static int cmnd_prepare_recv_pdu(struct iscsi_conn *conn, TRACE_DBG("idx=%d, offset=%u, size=%d, iov_len=%zd, addr=%p", idx, offset, size, conn->read_iov[i].iov_len, addr); size -= conn->read_iov[i].iov_len; - offset = 0; if (unlikely(++i >= ISCSI_CONN_IOV_MAX)) { PRINT_ERROR("Initiator %s violated negotiated " "parameters by sending too much data (size " @@ -1088,6 +1049,7 @@ static int cmnd_prepare_recv_pdu(struct iscsi_conn *conn, break; } idx++; + offset = sg[idx].offset; } TRACE_DBG("msg_iov=%p, msg_iovlen=%zd", conn->read_msg.msg_iov, conn->read_msg.msg_iovlen); @@ -1241,7 +1203,6 @@ static int noop_out_start(struct iscsi_cmnd *cmnd) size = cmnd->pdu.datasize; if (size) { - size = (size + 3) & -4; conn->read_msg.msg_iov = conn->read_iov; if (cmnd->pdu.bhs.itt != cpu_to_be32(ISCSI_RESERVED_TAG)) { struct scatterlist *sg; @@ -1410,7 +1371,9 @@ static int scsi_cmnd_start(struct iscsi_cmnd *req) req->pdu.datasize)) { PRINT_ERROR("Unexpected unsolicited data (ITT %x " "CDB %x", cmnd_itt(req), req_hdr->scb[0]); - create_sense_rsp(req, ABORTED_COMMAND, 0xc, 0xc); + create_status_rsp(req, SAM_STAT_CHECK_CONDITION, + sense_unexpected_unsolicited_data, + sizeof(sense_unexpected_unsolicited_data)); cmnd_reject_scsi_cmd(req); goto out; } @@ -1461,7 +1424,9 @@ static int scsi_cmnd_start(struct iscsi_cmnd *req) if (unlikely(dir != SCST_DATA_WRITE)) { PRINT_ERROR("pdu.datasize(%d) >0, but dir(%x) isn't " "WRITE", req->pdu.datasize, dir); - create_sense_rsp(req, ABORTED_COMMAND, 0xc, 0xc); + create_status_rsp(req, SAM_STAT_CHECK_CONDITION, + sense_unexpected_unsolicited_data, + sizeof(sense_unexpected_unsolicited_data)); cmnd_reject_scsi_cmd(req); } else res = cmnd_prepare_recv_pdu(conn, req, 0, @@ -2092,35 +2057,6 @@ out_rejected: goto out; } -static void __cmnd_send_pdu(struct iscsi_conn *conn, struct iscsi_cmnd *cmnd, - u32 offset, u32 size) -{ - TRACE_DBG("%p %u,%u,%u", cmnd, offset, size, cmnd->bufflen); - - iscsi_extracheck_is_wr_thread(conn); - - sBUG_ON(offset > cmnd->bufflen); - sBUG_ON(offset + size > cmnd->bufflen); - - conn->write_offset = offset; - conn->write_size += size; - return; -} - -static void cmnd_send_pdu(struct iscsi_conn *conn, struct iscsi_cmnd *cmnd) -{ - u32 size; - - if (!cmnd->pdu.datasize) - return; - - size = (cmnd->pdu.datasize + 3) & -4; - sBUG_ON(cmnd->sg == NULL); - sBUG_ON(cmnd->bufflen != size); - __cmnd_send_pdu(conn, cmnd, 0, size); - return; -} - /* * Note: the code belows passes a kernel space pointer (&opt) to setsockopt() * while the declaration of setsockopt specifies that it expects a user space @@ -2144,7 +2080,7 @@ void cmnd_tx_start(struct iscsi_cmnd *cmnd) { struct iscsi_conn *conn = cmnd->conn; - TRACE_DBG("%p:%p:%x", conn, cmnd, cmnd_opcode(cmnd)); + TRACE_DBG("conn %p, cmnd %p, opcode %x", conn, cmnd, cmnd_opcode(cmnd)); iscsi_cmnd_set_length(&cmnd->pdu); iscsi_extracheck_is_wr_thread(conn); @@ -2155,16 +2091,15 @@ void cmnd_tx_start(struct iscsi_cmnd *cmnd) conn->write_iop->iov_base = (void __force __user *)(&cmnd->pdu.bhs); conn->write_iop->iov_len = sizeof(cmnd->pdu.bhs); conn->write_iop_used = 1; - conn->write_size = sizeof(cmnd->pdu.bhs); + conn->write_size = sizeof(cmnd->pdu.bhs) + cmnd->pdu.datasize; + conn->write_offset = 0; switch (cmnd_opcode(cmnd)) { case ISCSI_OP_NOOP_IN: cmnd_set_sn(cmnd, 1); - cmnd_send_pdu(conn, cmnd); break; case ISCSI_OP_SCSI_RSP: cmnd_set_sn(cmnd, 1); - cmnd_send_pdu(conn, cmnd); break; case ISCSI_OP_SCSI_TASK_MGT_RSP: cmnd_set_sn(cmnd, 1); @@ -2178,8 +2113,15 @@ void cmnd_tx_start(struct iscsi_cmnd *cmnd) (struct iscsi_data_in_hdr *)&cmnd->pdu.bhs; u32 offset = cpu_to_be32(rsp->buffer_offset); + TRACE_DBG("cmnd %p, offset %u, datasize %u, bufflen %u", cmnd, + offset, cmnd->pdu.datasize, cmnd->bufflen); + + sBUG_ON(offset > cmnd->bufflen); + sBUG_ON(offset + cmnd->pdu.datasize > cmnd->bufflen); + + conn->write_offset = offset; + cmnd_set_sn(cmnd, (rsp->flags & ISCSI_FLG_FINAL) ? 1 : 0); - __cmnd_send_pdu(conn, cmnd, offset, cmnd->pdu.datasize); break; } case ISCSI_OP_LOGOUT_RSP: @@ -2193,15 +2135,12 @@ void cmnd_tx_start(struct iscsi_cmnd *cmnd) break; case ISCSI_OP_REJECT: cmnd_set_sn(cmnd, 1); - cmnd_send_pdu(conn, cmnd); break; default: - PRINT_ERROR("unexpected cmnd op %x", cmnd_opcode(cmnd)); + PRINT_ERROR("Unexpected cmnd op %x", cmnd_opcode(cmnd)); break; } - /* move this? */ - conn->write_size = (conn->write_size + 3) & -4; iscsi_dump_pdu(&cmnd->pdu); return; } @@ -2912,6 +2851,116 @@ static void iscsi_task_mgmt_fn_done(struct scst_mgmt_cmd *scst_mcmd) return; } +static int iscsi_scsi_aen(struct scst_aen *aen) +{ + int res = SCST_AEN_RES_SUCCESS; + uint64_t lun = scst_aen_get_lun(aen); + const uint8_t *sense = scst_aen_get_sense(aen); + int sense_len = scst_aen_get_sense_len(aen); + struct iscsi_session *sess = scst_sess_get_tgt_priv( + scst_aen_get_sess(aen)); + struct iscsi_conn *conn; + bool found; + struct iscsi_cmnd *fake_req, *rsp; + struct iscsi_async_msg_hdr *rsp_hdr; + struct scatterlist *sg; + + TRACE_ENTRY(); + + TRACE_MGMT_DBG("SCSI AEN to sess %p (initiator %s)", sess, + sess->initiator_name); + + mutex_lock(&sess->target->target_mutex); + + found = false; + list_for_each_entry_reverse(conn, &sess->conn_list, conn_list_entry) { + if (!conn->conn_shutting_down && + (conn->conn_reinst_successor == NULL)) { + found = true; + break; + } + } + if (!found) { + TRACE_MGMT_DBG("Unable to find alive conn for sess %p", sess); + goto out_err; + } + + /* Create a fake request */ + fake_req = cmnd_alloc(conn, NULL); + if (fake_req == NULL) { + PRINT_ERROR("%s", "Unable to alloc fake AEN request"); + goto out_err; + } + + mutex_unlock(&sess->target->target_mutex); + + rsp = iscsi_cmnd_create_rsp_cmnd(fake_req); + if (rsp == NULL) { + PRINT_ERROR("%s", "Unable to alloc AEN rsp"); + goto out_err_free_req; + } + + fake_req->scst_state = ISCSI_CMD_STATE_AEN; + fake_req->scst_aen = aen; + + rsp_hdr = (struct iscsi_async_msg_hdr *)&rsp->pdu.bhs; + + rsp_hdr->opcode = ISCSI_OP_ASYNC_MSG; + rsp_hdr->flags = ISCSI_FLG_FINAL; + rsp_hdr->lun = lun; /* it's already in SCSI form */ + rsp_hdr->ffffffff = 0xffffffff; + rsp_hdr->async_event = ISCSI_ASYNC_SCSI; + + sg = rsp->sg = rsp->rsp_sg; + rsp->sg_cnt = 2; + rsp->own_sg = 1; + + sg_init_table(sg, 2); + sg_set_buf(&sg[0], &rsp->sense_hdr, sizeof(rsp->sense_hdr)); + sg_set_buf(&sg[1], sense, sense_len); + + rsp->sense_hdr.length = cpu_to_be16(sense_len); + rsp->pdu.datasize = sizeof(rsp->sense_hdr) + sense_len; + rsp->bufflen = rsp->pdu.datasize; + + iscsi_cmnd_init_write(rsp, ISCSI_INIT_WRITE_WAKE); + + req_cmnd_release(fake_req); + +out: + TRACE_EXIT_RES(res); + return res; + +out_err_free_req: + req_cmnd_release(fake_req); + +out_err: + mutex_unlock(&sess->target->target_mutex); + res = SCST_AEN_RES_FAILED; + goto out; +} + +static int iscsi_report_aen(struct scst_aen *aen) +{ + int res; + int event_fn = scst_aen_get_event_fn(aen); + + TRACE_ENTRY(); + + switch (event_fn) { + case SCST_AEN_SCSI: + res = iscsi_scsi_aen(aen); + break; + default: + TRACE_MGMT_DBG("Unsupported AEN %d", event_fn); + res = SCST_AEN_RES_NOT_SUPPORTED; + break; + } + + TRACE_EXIT_RES(res); + return res; +} + static int iscsi_target_detect(struct scst_tgt_template *templ) { /* Nothing to do */ @@ -2940,6 +2989,7 @@ struct scst_tgt_template iscsi_template = { .pre_exec = iscsi_pre_exec, .task_mgmt_affected_cmds_done = iscsi_task_mgmt_affected_cmds_done, .task_mgmt_fn_done = iscsi_task_mgmt_fn_done, + .report_aen = iscsi_report_aen, }; static __init int iscsi_run_threads(int count, char *name, int (*fn)(void *)) @@ -2991,6 +3041,12 @@ static int __init iscsi_init(void) PRINT_INFO("iSCSI SCST Target - version %s", ISCSI_VERSION_STRING); + sense_unexpected_unsolicited_data[0] = 0x70; + sense_unexpected_unsolicited_data[2] = ABORTED_COMMAND; + sense_unexpected_unsolicited_data[7] = 6; + sense_unexpected_unsolicited_data[12] = 0xc; + sense_unexpected_unsolicited_data[13] = 0xc; + dummy_page = alloc_pages(GFP_KERNEL, 0); if (dummy_page == NULL) { PRINT_ERROR("%s", "Dummy page allocation failed"); diff --git a/iscsi-scst/kernel/iscsi.h b/iscsi-scst/kernel/iscsi.h index a57fc6242..ed6c113dd 100644 --- a/iscsi-scst/kernel/iscsi.h +++ b/iscsi-scst/kernel/iscsi.h @@ -73,8 +73,12 @@ struct iscsi_target { struct list_head session_list; /* protected by target_mutex */ /* Both protected by target_mutex */ - struct iscsi_sess_param trgt_sess_param; struct iscsi_trgt_param trgt_param; + /* + * Put here to have uniform parameters checking and assigning + * from various places, including iscsi-scst-adm. + */ + struct iscsi_sess_param trgt_sess_param; struct list_head target_list_entry; u32 tid; @@ -214,6 +218,7 @@ struct iscsi_conn { u32 read_size; int read_state; struct iovec *read_iov; + uint32_t rpadding; struct iscsi_target *target; @@ -240,20 +245,27 @@ struct iscsi_pdu { typedef void (iscsi_show_info_t)(struct seq_file *seq, struct iscsi_target *target); -/* Command's states */ +/** Command's states **/ /* New command and SCST processes it */ #define ISCSI_CMD_STATE_NEW 0 + /* SCST processes cmd after scst_rx_cmd() */ #define ISCSI_CMD_STATE_RX_CMD 1 + /* The command returned from preprocessing_done() */ #define ISCSI_CMD_STATE_AFTER_PREPROC 2 + /* scst_restart_cmd() called and SCST processing it */ #define ISCSI_CMD_STATE_RESTARTED 3 + /* SCST done processing */ #define ISCSI_CMD_STATE_PROCESSED 4 -/* Command's reject reasons */ +/* AEN processing */ +#define ISCSI_CMD_STATE_AEN 5 + +/** Command's reject reasons **/ #define ISCSI_REJECT_SCSI_CMD 1 #define ISCSI_REJECT_CMD 2 #define ISCSI_REJECT_DATA 3 @@ -294,7 +306,10 @@ struct iscsi_cmnd { spinlock_t rsp_cmd_lock; /* BH lock */ - /* Unions are for readability and grepability */ + /* + * Unions are for readability and grepability and to save some + * cache footprint. + */ union { /* Protected by rsp_cmd_lock */ @@ -312,18 +327,37 @@ struct iscsi_cmnd { unsigned long write_timeout; /* - * Unprotected, since could be accessed from only a single + * All unprotected, since could be accessed from only a single * thread at time */ - struct list_head rx_ddigest_cmd_list; - struct list_head rx_ddigest_cmd_list_entry; - struct iscsi_cmnd *parent_req; struct iscsi_cmnd *cmd_req; - wait_queue_head_t scst_waitQ; - int scst_state; - struct scst_cmd *scst_cmd; + /* + * All unprotected, since could be accessed from only a single + * thread at time + */ + union { + /* Request only fields */ + struct { + struct list_head rx_ddigest_cmd_list; + struct list_head rx_ddigest_cmd_list_entry; + + wait_queue_head_t scst_waitQ; + int scst_state; + union { + struct scst_cmd *scst_cmd; + struct scst_aen *scst_aen; + }; + }; + + /* Response only fields */ + struct { + struct scatterlist rsp_sg[2]; + struct iscsi_sense_data sense_hdr; + }; + }; + atomic_t ref_cnt; #if defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) atomic_t net_ref_cnt; diff --git a/iscsi-scst/kernel/nthread.c b/iscsi-scst/kernel/nthread.c index 98336ec02..0b2d49d87 100644 --- a/iscsi-scst/kernel/nthread.c +++ b/iscsi-scst/kernel/nthread.c @@ -39,6 +39,9 @@ enum rx_state { RX_INIT_DATA, RX_DATA, + RX_INIT_PADDING, + RX_PADDING, + RX_INIT_DDIGEST, RX_DDIGEST, RX_CHECK_DDIGEST, @@ -49,6 +52,8 @@ enum rx_state { enum tx_state { TX_INIT, /* Must be zero. */ TX_BHS_DATA, + TX_INIT_PADDING, + TX_PADDING, TX_INIT_DDIGEST, TX_DDIGEST, TX_END, @@ -594,26 +599,25 @@ static void start_close_conn(struct iscsi_conn *conn) } static inline void iscsi_conn_init_read(struct iscsi_conn *conn, - void __user *data, - size_t len) + void __user *data, size_t len) { - len = (len + 3) & -4; /* XXX ??? */ conn->read_iov[0].iov_base = data; conn->read_iov[0].iov_len = len; conn->read_msg.msg_iov = conn->read_iov; conn->read_msg.msg_iovlen = 1; - conn->read_size = (len + 3) & -4; + conn->read_size = len; return; } static void iscsi_conn_read_ahs(struct iscsi_conn *conn, - struct iscsi_cmnd *cmnd) + struct iscsi_cmnd *cmnd) { + int asize = (cmnd->pdu.ahssize + 3) & -4; + /* ToDo: __GFP_NOFAIL ?? */ - cmnd->pdu.ahs = kmalloc(cmnd->pdu.ahssize, __GFP_NOFAIL|GFP_KERNEL); + cmnd->pdu.ahs = kmalloc(asize, __GFP_NOFAIL|GFP_KERNEL); sBUG_ON(cmnd->pdu.ahs == NULL); - iscsi_conn_init_read(conn, (void __force __user *)cmnd->pdu.ahs, - cmnd->pdu.ahssize); + iscsi_conn_init_read(conn, (void __force __user *)cmnd->pdu.ahs, asize); return; } @@ -783,10 +787,31 @@ static int recv(struct iscsi_conn *conn) if (conn->read_state != RX_DATA) break; case RX_DATA: + res = do_recv(conn, RX_INIT_PADDING); + if (res <= 0 || conn->read_state != RX_INIT_PADDING) + break; + case RX_INIT_PADDING: + { + int psz = ((cmnd->pdu.datasize + 3) & -4) - cmnd->pdu.datasize; + if (psz != 0) { + TRACE_DBG("padding %d bytes", psz); + iscsi_conn_init_read(conn, + (void __force __user *)&conn->rpadding, psz); + conn->read_state = RX_PADDING; + } else if (ddigest) { + conn->read_state = RX_INIT_DDIGEST; + goto init_ddigest; + } else { + conn->read_state = RX_END; + break; + } + } + case RX_PADDING: res = do_recv(conn, ddigest ? RX_INIT_DDIGEST : RX_END); if (res <= 0 || conn->read_state != RX_INIT_DDIGEST) break; case RX_INIT_DDIGEST: +init_ddigest: iscsi_conn_init_read(conn, (void __force __user *)&cmnd->ddigest, sizeof(u32)); conn->read_state = RX_DDIGEST; @@ -1046,17 +1071,18 @@ static int write_data(struct iscsi_conn *conn) { mm_segment_t oldfs; struct file *file; + struct iovec *iop; struct socket *sock; ssize_t (*sock_sendpage)(struct socket *, struct page *, int, size_t, int); ssize_t (*sendpage)(struct socket *, struct page *, int, size_t, int); struct iscsi_cmnd *write_cmnd = conn->write_cmnd; struct iscsi_cmnd *ref_cmd; + struct page *page; struct scatterlist *sg; - struct iovec *iop; int saved_size, size, sendsize; - int offset, idx, sg_offset; - int flags, res, count; + int length, offset, idx; + int flags, res, count, sg_size; bool do_put = false; TRACE_ENTRY(); @@ -1091,7 +1117,8 @@ static int write_data(struct iscsi_conn *conn) } file = conn->file; - saved_size = size = conn->write_size; + size = conn->write_size; + saved_size = size; iop = conn->write_iop; count = conn->write_iop_used; @@ -1102,17 +1129,16 @@ static int write_data(struct iscsi_conn *conn) sBUG_ON(count > (signed)(sizeof(conn->write_iov) / sizeof(conn->write_iov[0]))); - retry: +retry: oldfs = get_fs(); set_fs(KERNEL_DS); res = vfs_writev(file, (struct iovec __force __user *)iop, count, &off); set_fs(oldfs); - TRACE_WRITE("%#Lx:%u: %d(%ld)", + TRACE_WRITE("sid %#Lx, cid %u, res %d, iov_len %ld", (long long unsigned int)conn->session->sid, - conn->cid, - res, (long)iop->iov_len); + conn->cid, res, (long)iop->iov_len); if (unlikely(res <= 0)) { if (res == -EAGAIN) { conn->write_iop = iop; @@ -1155,11 +1181,6 @@ static int write_data(struct iscsi_conn *conn) __iscsi_get_page_callback(ref_cmd); do_put = true; - sg_offset = sg[0].offset; - offset = conn->write_offset + sg_offset; - idx = offset >> PAGE_SHIFT; - offset &= ~PAGE_MASK; - sock = conn->sock; #if defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) @@ -1173,6 +1194,31 @@ static int write_data(struct iscsi_conn *conn) #endif flags = MSG_DONTWAIT; + sg_size = size; + + if (sg != write_cmnd->rsp_sg) { + offset = conn->write_offset + sg[0].offset; + idx = offset >> PAGE_SHIFT; + offset &= ~PAGE_MASK; + length = min(size, (int)PAGE_SIZE - offset); + TRACE_WRITE("write_offset %d, sg_size %d, idx %d, offset %d, " + "length %d", conn->write_offset, sg_size, idx, offset, + length); + } else { + idx = 0; + offset = conn->write_offset; + while (offset >= sg[idx].length) { + offset -= sg[idx].length; + idx++; + } + length = sg[idx].length - offset; + offset += sg[idx].offset; + sock_sendpage = sock_no_sendpage; + TRACE_WRITE("rsp_sg: write_offset %d, sg_size %d, idx %d, " + "offset %d, length %d", conn->write_offset, sg_size, + idx, offset, length); + } + page = sg_page(&sg[idx]); while (1) { sendpage = sock_sendpage; @@ -1181,8 +1227,8 @@ static int write_data(struct iscsi_conn *conn) { static DEFINE_SPINLOCK(net_priv_lock); spin_lock(&net_priv_lock); - if (sg_page(&sg[idx])->net_priv != NULL) { - if (sg_page(&sg[idx])->net_priv != ref_cmd) { + if (unlikely(page->net_priv != NULL)) { + if (page->net_priv != ref_cmd) { /* * This might happen if user space * supplies to scst_user the same @@ -1196,26 +1242,25 @@ static int write_data(struct iscsi_conn *conn) "%p, sg %p, idx %d, page %p, " "net_priv %p)", write_cmnd, ref_cmd, sg, idx, - sg_page(&sg[idx]), - sg_page(&sg[idx])->net_priv); + page, page->net_priv); sendpage = sock_no_sendpage; } } else - sg_page(&sg[idx])->net_priv = ref_cmd; + page->net_priv = ref_cmd; spin_unlock(&net_priv_lock); } #endif - sendsize = PAGE_SIZE - offset; + sendsize = min(size, length); if (size <= sendsize) { retry2: - res = sendpage(sock, sg_page(&sg[idx]), offset, size, - flags); - TRACE_WRITE("Final %s %#Lx:%u: %d(%lu,%u,%u, cmd %p, " + res = sendpage(sock, page, offset, size, flags); + TRACE_WRITE("Final %s sid %#Lx, cid %u, res %d (page " + "index %lu, offset %u, size %u, cmd %p, " "page %p)", (sendpage != sock_no_sendpage) ? "sendpage" : "sock_no_sendpage", (long long unsigned int)conn->session->sid, - conn->cid, res, sg_page(&sg[idx])->index, - offset, size, write_cmnd, sg_page(&sg[idx])); + conn->cid, res, page->index, + offset, size, write_cmnd, page); if (unlikely(res <= 0)) { if (res == -EINTR) goto retry2; @@ -1223,7 +1268,7 @@ retry2: goto out_res; } - check_net_priv(ref_cmd, sg_page(&sg[idx])); + check_net_priv(ref_cmd, page); if (res == size) { conn->write_size = 0; res = saved_size; @@ -1232,18 +1277,18 @@ retry2: offset += res; size -= res; - continue; + goto retry2; } retry1: - res = sendpage(sock, sg_page(&sg[idx]), offset, sendsize, - flags | MSG_MORE); - TRACE_WRITE("%s %#Lx:%u: %d(%lu,%u,%u, cmd %p, page %p)", + res = sendpage(sock, page, offset, sendsize, flags | MSG_MORE); + TRACE_WRITE("%s sid %#Lx, cid %u, res %d (page index %lu, " + "offset %u, sendsize %u, size %u, cmd %p, page %p)", (sendpage != sock_no_sendpage) ? "sendpage" : "sock_no_sendpage", (unsigned long long)conn->session->sid, conn->cid, - res, sg_page(&sg[idx])->index, offset, sendsize, - write_cmnd, sg_page(&sg[idx])); + res, page->index, offset, sendsize, size, + write_cmnd, page); if (unlikely(res <= 0)) { if (res == -EINTR) goto retry1; @@ -1251,19 +1296,25 @@ retry1: goto out_res; } - check_net_priv(ref_cmd, sg_page(&sg[idx])); - if (res == sendsize) { - idx++; - offset = 0; - EXTRACHECKS_BUG_ON(idx >= ref_cmd->sg_cnt); - } else - offset += res; + check_net_priv(ref_cmd, page); size -= res; + + if (res == sendsize) { + idx++; + EXTRACHECKS_BUG_ON(idx >= ref_cmd->sg_cnt); + page = sg_page(&sg[idx]); + length = sg[idx].length; + offset = sg[idx].offset; + } else { + offset += res; + sendsize -= res; + goto retry1; + } } out_off: - conn->write_offset = (idx << PAGE_SHIFT) + offset - sg_offset; + conn->write_offset += sg_size - size; out_iov: conn->write_size = size; @@ -1281,7 +1332,7 @@ out: return res; out_res: - check_net_priv(ref_cmd, sg_page(&sg[idx])); + check_net_priv(ref_cmd, page); if (res == -EAGAIN) goto out_off; /* else go through */ @@ -1295,9 +1346,14 @@ out_err: (long long unsigned int)conn->session->sid, conn->cid, conn->write_cmnd); } - if (ref_cmd->scst_cmd != NULL) - scst_set_delivery_status(ref_cmd->scst_cmd, - SCST_CMD_DELIVERY_FAILED); + if ((ref_cmd->scst_cmd != NULL) || (ref_cmd->scst_aen != NULL)) { + if (ref_cmd->scst_state == ISCSI_CMD_STATE_AEN) + scst_set_aen_delivery_status(ref_cmd->scst_aen, + SCST_AEN_RES_FAILED); + else + scst_set_delivery_status(ref_cmd->scst_cmd, + SCST_CMD_DELIVERY_FAILED); + } goto out_put; } @@ -1374,6 +1430,31 @@ static void init_tx_hdigest(struct iscsi_cmnd *cmnd) return; } +static int tx_padding(struct iscsi_cmnd *cmnd, int state) +{ + int res, rest = cmnd->conn->write_size; + struct msghdr msg = {.msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT}; + struct kvec iov; + static const uint32_t padding; + + iscsi_extracheck_is_wr_thread(cmnd->conn); + + TRACE_DBG("Sending %d padding bytes (cmd %p)", rest, cmnd); + + iov.iov_base = (char *)(&padding) + (sizeof(uint32_t) - rest); + iov.iov_len = rest; + + res = kernel_sendmsg(cmnd->conn->sock, &msg, &iov, 1, rest); + if (res > 0) { + cmnd->conn->write_size -= res; + if (!cmnd->conn->write_size) + cmnd->conn->write_state = state; + } else + res = exit_tx(cmnd->conn, res); + + return res; +} + static int iscsi_do_send(struct iscsi_conn *conn, int state) { int res; @@ -1421,11 +1502,28 @@ int iscsi_send(struct iscsi_conn *conn) init_tx_hdigest(cmnd); conn->write_state = TX_BHS_DATA; case TX_BHS_DATA: - res = iscsi_do_send(conn, ddigest && cmnd->pdu.datasize ? - TX_INIT_DDIGEST : TX_END); + res = iscsi_do_send(conn, cmnd->pdu.datasize ? + TX_INIT_PADDING : TX_END); + if (res <= 0 || conn->write_state != TX_INIT_PADDING) + break; + case TX_INIT_PADDING: + cmnd->conn->write_size = ((cmnd->pdu.datasize + 3) & -4) - + cmnd->pdu.datasize; + if (cmnd->conn->write_size != 0) + conn->write_state = TX_PADDING; + else if (ddigest) { + conn->write_state = TX_INIT_DDIGEST; + goto init_ddigest; + } else { + conn->write_state = TX_END; + break; + } + case TX_PADDING: + res = tx_padding(cmnd, ddigest ? TX_INIT_DDIGEST : TX_END); if (res <= 0 || conn->write_state != TX_INIT_DDIGEST) break; case TX_INIT_DDIGEST: +init_ddigest: cmnd->conn->write_size = sizeof(u32); conn->write_state = TX_DDIGEST; case TX_DDIGEST: diff --git a/iscsi-scst/kernel/session.c b/iscsi-scst/kernel/session.c index 821ad9469..d1523d3f2 100644 --- a/iscsi-scst/kernel/session.c +++ b/iscsi-scst/kernel/session.c @@ -44,10 +44,7 @@ static int iscsi_session_alloc(struct iscsi_target *target, session->target = target; session->sid = info->sid; - BUILD_BUG_ON(sizeof(session->sess_param) != - sizeof(target->trgt_sess_param)); - memcpy(&session->sess_param, &target->trgt_sess_param, - sizeof(session->sess_param)); + session->sess_param = target->trgt_sess_param; session->max_queued_cmnds = target->trgt_param.queued_cmnds; atomic_set(&session->active_cmds, 0);