This patch addes task management function support to tgt. This assumes that all the previous patchsets are applied. - add callback to task management function to scsi_host_template structure. It is used notify LLDs of the completion of a TMF request. - this patch doesn't use a single queue for TMF requests and SCSI commands yet. We'll work on it later on. - when LLDs queue scsi commands to tgt (scsi_tgt_queue_command), they need to specify unique 'tag' for each command for ABORT_TASK. We use 64-bit tag because of SRP though 32-bit works for FCP and iSCSI. There is no room for it in scsi_cmnd structure so this patch abuses sense_buffer. Better solution? - when tgt aborts a command, it calls eh_abort_handler in scsi_host_template structure. Would be better to add tgt_eh_abort_handler for LLDs support target and initiator modes at the same time? tgt TMF works in the followings: - When LLDs queue scsi commands to tgt (scsi_tgt_queue_command), they need to specify unique 'tag' for each command. - LLDs call 'int scsi_tgt_tsk_mgmt_request(struct Scsi_Host *host, int, u64 tag, struct scsi_lun *lun, void *data)'. - int (* tsk_mgmt_response)(u64 data, int result) is added to scsi_host_template. When an initiator sends a task management request, the LLD calls scsi_tgt_tsk_mgmt_request. the LLD can use whatever it wants for the data arg. The data arg is used later as the arg in the tsk_mgmt_response callback. tgt core just sends the task management request to user space (by using TGT_KEVENT_TSK_MGMT_REQ). In the case of ABORT_TASK, tgtd finds a single command to abort and sends TGT_UEVENT_CMD_RSP and TGT_UEVENT_TSK_MGMT_RSP events. tgt core calls eh_abort_handler for TGT_UEVENT_CMD_RSP and then tsk_mgmt_response for TGT_UEVENT_TSK_MGMT_RSP. If tgtd fails to find a command to abort, it sends only TGT_UEVENT_TSK_MGMT_RSP event (no TGT_UEVENT_CMD_RSP event). In the case of the rests task management function (like ABORT_TASK_SET), tgt needs to abort multiple commands. Thus, tgtd finds multiple commands to abort and sends multiple TGT_UEVENT_CMD_RSP events and a single TGT_UEVENT_TSK_MGMT_RSP event. tgt core calls eh_abort_handler multiple times and tsk_mgmt_response once. eh_abort_handler enables LLDs to safely free resource related with a command to abort. Signed-off-by: FUJITA Tomonori <fujita.tomonori@xxxxxxxxxxxxx> Signed-off-by: Mike Christie <michaelc@xxxxxxxxxxx> --- drivers/scsi/scsi_tgt_if.c | 38 +++++++++++++++++++++++---- drivers/scsi/scsi_tgt_lib.c | 60 ++++++++++++++++++++++++++++++++++++------ drivers/scsi/scsi_tgt_priv.h | 4 ++- include/scsi/scsi_host.h | 3 ++ include/scsi/scsi_tgt.h | 4 ++- include/scsi/scsi_tgt_if.h | 7 +++-- 6 files changed, 98 insertions(+), 18 deletions(-) ec93e261d7a627b743377c6d47deead88332f16d diff --git a/drivers/scsi/scsi_tgt_if.c b/drivers/scsi/scsi_tgt_if.c index a31c8d5..9fc2d37 100644 --- a/drivers/scsi/scsi_tgt_if.c +++ b/drivers/scsi/scsi_tgt_if.c @@ -85,9 +85,11 @@ int scsi_tgt_uspace_send(struct scsi_cmn memcpy(ev->k.cmd_req.scb, cmd->cmnd, sizeof(ev->k.cmd_req.scb)); memcpy(ev->k.cmd_req.lun, lun, sizeof(ev->k.cmd_req.lun)); ev->k.cmd_req.attribute = cmd->tag; + ev->k.cmd_req.tag = *((u64 *) (cmd->sense_buffer)); - dprintk("%d %u %u\n", ev->k.cmd_req.host_no, ev->k.cmd_req.cid, - ev->k.cmd_req.data_len); + dprintk("%p %d %u %u %x %llx\n", cmd, shost->host_no, ev->k.cmd_req.cid, + ev->k.cmd_req.data_len, cmd->tag, + (unsigned long long) ev->k.cmd_req.tag); err = netlink_unicast(nl_sk, skb, tgtd_pid, 0); if (err < 0) @@ -109,6 +111,24 @@ int scsi_tgt_uspace_send_status(struct s return send_event_rsp(TGT_KEVENT_CMD_DONE, &ev, gfp_mask, tgtd_pid); } +int scsi_tgt_uspace_send_tsk_mgmt(int host_no, int function, u64 tag, + struct scsi_lun *scsilun, void *data) +{ + struct tgt_event ev; + + memset(&ev, 0, sizeof(ev)); + ev.k.tsk_mgmt_req.host_no = host_no; + ev.k.tsk_mgmt_req.function = function; + ev.k.tsk_mgmt_req.tag = tag; + memcpy(ev.k.tsk_mgmt_req.lun, scsilun, sizeof(ev.k.tsk_mgmt_req.lun)); + ev.k.tsk_mgmt_req.mid = (u64) data; + + dprintk("%d %x %llx %llx\n", host_no, function, (unsigned long long) tag, + (unsigned long long) ev.k.tsk_mgmt_req.mid); + + return send_event_rsp(TGT_KEVENT_TSK_MGMT_REQ, &ev, GFP_KERNEL, tgtd_pid); +} + static int event_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) { struct tgt_event *ev = NLMSG_DATA(nlh); @@ -130,6 +150,11 @@ static int event_recv_msg(struct sk_buff ev->u.cmd_rsp.uaddr, ev->u.cmd_rsp.rw); break; + case TGT_UEVENT_TSK_MGMT_RSP: + err = scsi_tgt_kspace_tsk_mgmt(ev->u.tsk_mgmt_rsp.host_no, + ev->u.tsk_mgmt_rsp.mid, + ev->u.tsk_mgmt_rsp.result); + break; default: eprintk("unknown type %d\n", nlh->nlmsg_type); err = -EINVAL; @@ -143,6 +168,7 @@ static int event_recv_skb(struct sk_buff int err; uint32_t rlen; struct nlmsghdr *nlh; + struct tgt_event ev; while (skb->len >= NLMSG_SPACE(0)) { nlh = (struct nlmsghdr *) skb->data; @@ -158,9 +184,11 @@ static int event_recv_skb(struct sk_buff * TODO for passthru commands the lower level should * probably handle the result or we should modify this */ - if (nlh->nlmsg_type != TGT_UEVENT_CMD_RSP) { - struct tgt_event ev; - + switch (nlh->nlmsg_type) { + case TGT_UEVENT_CMD_RSP: + case TGT_UEVENT_TSK_MGMT_RSP: + break; + default: memset(&ev, 0, sizeof(ev)); ev.k.event_rsp.err = err; send_event_rsp(TGT_KEVENT_RSP, &ev, diff --git a/drivers/scsi/scsi_tgt_lib.c b/drivers/scsi/scsi_tgt_lib.c index 2cbc749..593bfe9 100644 --- a/drivers/scsi/scsi_tgt_lib.c +++ b/drivers/scsi/scsi_tgt_lib.c @@ -106,7 +106,6 @@ static void scsi_tgt_cmd_destroy(void *d cmd->request->flags &= ~1UL; scsi_unmap_user_pages(tcmd); - scsi_tgt_uspace_send_status(cmd, GFP_KERNEL); kmem_cache_free(scsi_tgt_cmd_cache, tcmd); scsi_host_put_command(scsi_tgt_cmd_to_host(cmd), cmd); } @@ -267,13 +266,15 @@ EXPORT_SYMBOL_GPL(scsi_tgt_cmd_to_host); * @noblock: set to nonzero if the command should be queued **/ void scsi_tgt_queue_command(struct scsi_cmnd *cmd, struct scsi_lun *scsilun, - int noblock) + u64 tag) { struct request_queue *q = cmd->request->q; struct scsi_tgt_queuedata *qdata = q->queuedata; unsigned long flags; cmd->request->end_io_data = scsilun; + /* FIXME */ + *((u64 *) (cmd->sense_buffer)) = tag; spin_lock_irqsave(&qdata->cmd_req_lock, flags); list_add_tail(&cmd->request->queuelist, &qdata->cmd_req); @@ -293,12 +294,7 @@ static void scsi_tgt_cmd_done(struct scs dprintk("cmd %p %lu\n", cmd, rq_data_dir(cmd->request)); - /* don't we have to call this if result is set or not */ - if (cmd->result) { - scsi_tgt_uspace_send_status(cmd, GFP_ATOMIC); - return; - } - + scsi_tgt_uspace_send_status(cmd, GFP_ATOMIC); INIT_WORK(&tcmd->work, scsi_tgt_cmd_destroy, cmd); queue_work(scsi_tgtd, &tcmd->work); } @@ -495,6 +491,18 @@ static int scsi_tgt_copy_sense(struct sc return 0; } +static int scsi_tgt_abort_cmd(struct Scsi_Host *host, struct scsi_cmnd *cmd) +{ + int err; + + err = host->hostt->eh_abort_handler(cmd); + if (err) + eprintk("fail to abort %p\n", cmd); + + scsi_tgt_cmd_destroy(cmd); + return err; +} + static struct request *tgt_cmd_hash_lookup(struct request_queue *q, u32 cid) { struct scsi_tgt_queuedata *qdata = q->queuedata; @@ -545,6 +553,10 @@ int scsi_tgt_kspace_exec(int host_no, u3 dprintk("cmd %p result %d len %d bufflen %u %lu %x\n", cmd, result, len, cmd->request_bufflen, rq_data_dir(rq), cmd->cmnd[0]); + if (result == TASK_ABORTED) { + scsi_tgt_abort_cmd(shost, cmd); + goto done; + } /* * store the userspace values here, the working values are * in the request_* values @@ -585,6 +597,38 @@ done: return err; } +int scsi_tgt_tsk_mgmt_request(struct Scsi_Host *shost, int function, u64 tag, + struct scsi_lun *scsilun, void *data) +{ + int err; + + /* TODO: need to retry if this fails. */ + err = scsi_tgt_uspace_send_tsk_mgmt(shost->host_no, function, + tag, scsilun, data); + if (err < 0) + eprintk("The task management request lost!\n"); + return err; +} +EXPORT_SYMBOL_GPL(scsi_tgt_tsk_mgmt_request); + +int scsi_tgt_kspace_tsk_mgmt(int host_no, u64 mid, int result) +{ + struct Scsi_Host *shost; + int err; + + dprintk("%d %d %llx\n", host_no, result, (unsigned long long) mid); + + shost = scsi_host_lookup(host_no); + if (IS_ERR(shost)) { + printk(KERN_ERR "Could not find host no %d\n", host_no); + return -EINVAL; + } + err = shost->hostt->tsk_mgmt_response(mid, result); + scsi_host_put(shost); + + return err; +} + static int __init scsi_tgt_init(void) { int err; diff --git a/drivers/scsi/scsi_tgt_priv.h b/drivers/scsi/scsi_tgt_priv.h index 6fedcec..c518e0e 100644 --- a/drivers/scsi/scsi_tgt_priv.h +++ b/drivers/scsi/scsi_tgt_priv.h @@ -18,4 +18,6 @@ extern int scsi_tgt_uspace_send(struct s extern int scsi_tgt_uspace_send_status(struct scsi_cmnd *cmd, gfp_t flags); extern int scsi_tgt_kspace_exec(int host_no, u32 cid, int result, u32 len, unsigned long uaddr, u8 rw); - +extern int scsi_tgt_uspace_send_tsk_mgmt(int host_no, int function, u64 tag, + struct scsi_lun *scsilun, void *data); +extern int scsi_tgt_kspace_tsk_mgmt(int host_no, u64 mid, int result); diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index 8b799db..eca5721 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -153,6 +153,9 @@ struct scsi_host_template { int (* transfer_data)(struct scsi_cmnd *, void (*done)(struct scsi_cmnd *)); + /* Used as callback for the completion of task management request. */ + int (* tsk_mgmt_response)(u64 mid, int result); + /* * This is an error handling strategy routine. You don't need to * define one of these if you don't want to - there is a default diff --git a/include/scsi/scsi_tgt.h b/include/scsi/scsi_tgt.h index 91ad6bc..3d09a1a 100644 --- a/include/scsi/scsi_tgt.h +++ b/include/scsi/scsi_tgt.h @@ -8,4 +8,6 @@ struct scsi_lun; extern struct Scsi_Host *scsi_tgt_cmd_to_host(struct scsi_cmnd *cmd); extern int scsi_tgt_alloc_queue(struct Scsi_Host *); -extern void scsi_tgt_queue_command(struct scsi_cmnd *, struct scsi_lun *, int); +extern void scsi_tgt_queue_command(struct scsi_cmnd *, struct scsi_lun *, u64); +extern int scsi_tgt_tsk_mgmt_request(struct Scsi_Host *, int, u64, struct scsi_lun *, + void *); diff --git a/include/scsi/scsi_tgt_if.h b/include/scsi/scsi_tgt_if.h index ebca452..63b2e3a 100644 --- a/include/scsi/scsi_tgt_if.h +++ b/include/scsi/scsi_tgt_if.h @@ -52,7 +52,7 @@ struct tgt_event { } cmd_rsp; struct { int host_no; - int mid; + uint64_t mid; int result; } tsk_mgmt_rsp; } u; @@ -69,6 +69,7 @@ struct tgt_event { uint8_t scb[16]; uint8_t lun[8]; int attribute; + uint64_t tag; } cmd_req; struct { int host_no; @@ -77,10 +78,10 @@ struct tgt_event { } cmd_done; struct { int host_no; - int mid; + int function; uint64_t tag; uint8_t lun[8]; - int function; + uint64_t mid; } tsk_mgmt_req; } k; -- 1.1.3 - : send the line "unsubscribe linux-scsi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html