- Support for SCSI AENs added
authorvlnb <vlnb@d57e44dd-8a1f-0410-8b47-8ef2f437770f>
Tue, 17 Mar 2009 18:05:19 +0000 (18:05 +0000)
committervlnb <vlnb@d57e44dd-8a1f-0410-8b47-8ef2f437770f>
Tue, 17 Mar 2009 18:05:19 +0000 (18:05 +0000)
 - Now sense data are sent without additional memory allocation and copy

 - Sending and receiving padding bytes reimplemented

 - Cleanups

git-svn-id: https://scst.svn.sourceforge.net/svnroot/scst/trunk@699 d57e44dd-8a1f-0410-8b47-8ef2f437770f

iscsi-scst/kernel/config.c
iscsi-scst/kernel/digest.c
iscsi-scst/kernel/iscsi.c
iscsi-scst/kernel/iscsi.h
iscsi-scst/kernel/nthread.c
iscsi-scst/kernel/session.c

index 5bdd98f..149f230 100644 (file)
@@ -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 */
index a5dc7f1..5c6d00a 100644 (file)
@@ -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));
 }
index 3e1fffd..de7992c 100644 (file)
@@ -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 */;
-               }
+
+               sg = rsp->sg = rsp->rsp_sg;
+               rsp->sg_cnt = 2;
                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_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");
index a57fc62..ed6c113 100644 (file)
@@ -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;
index 98336ec..0b2d49d 100644 (file)
@@ -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]));
+               check_net_priv(ref_cmd, page);
+
+               size -= res;
+
                if (res == sendsize) {
                        idx++;
-                       offset = 0;
                        EXTRACHECKS_BUG_ON(idx >= ref_cmd->sg_cnt);
-               } else
+                       page = sg_page(&sg[idx]);
+                       length = sg[idx].length;
+                       offset = sg[idx].offset;
+               } else {
                        offset += res;
-
-               size -= 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:
index 821ad94..d1523d3 100644 (file)
@@ -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);