- Support for SCSI AENs added

- Now sense data are sent without additional memory allocation and copy

 - Sending and receiving padding bytes reimplemented

 - Cleanups



git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@699 d57e44dd-8a1f-0410-8b47-8ef2f437770f
This commit is contained in:
Vladislav Bolkhovitin
2009-03-17 18:05:19 +00:00
parent e252aadf35
commit 159ef466dc
6 changed files with 384 additions and 208 deletions

View 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 */

View 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));
}

View 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 */;
}
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");

View 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;

View 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]));
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:

View 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);