[PATCH 09/10] scsi tgt: add task management function support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This patch addes task management function support to tgt.

- 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.

- 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   |   43 +++++++++++++++---
 drivers/scsi/scsi_tgt_lib.c  |  103 +++++++++++++++++++++++++++++-------------
 drivers/scsi/scsi_tgt_priv.h |   11 +++-
 include/scsi/scsi_host.h     |    3 +
 include/scsi/scsi_tgt.h      |    6 ++
 include/scsi/scsi_tgt_if.h   |    7 ++-
 6 files changed, 125 insertions(+), 48 deletions(-)

b9579b62f8d6309815a60da2e6f9a7638df074aa
diff --git a/drivers/scsi/scsi_tgt_if.c b/drivers/scsi/scsi_tgt_if.c
index a31c8d5..ba1b75b 100644
--- a/drivers/scsi/scsi_tgt_if.c
+++ b/drivers/scsi/scsi_tgt_if.c
@@ -56,7 +56,8 @@ static int send_event_rsp(uint16_t type,
 	return netlink_unicast(nl_sk, skb, pid, 0);
 }
 
-int scsi_tgt_uspace_send(struct scsi_cmnd *cmd, struct scsi_lun *lun, gfp_t gfp_mask)
+int scsi_tgt_uspace_send(struct scsi_cmnd *cmd, struct scsi_lun *lun, u64 tag,
+			 gfp_t flags)
 {
 	struct Scsi_Host *shost = scsi_tgt_cmd_to_host(cmd);
 	struct sk_buff *skb;
@@ -71,7 +72,7 @@ int scsi_tgt_uspace_send(struct scsi_cmn
 	/*
 	 * TODO: add MAX_COMMAND_SIZE to ev and add mempool
 	 */
-	skb = alloc_skb(NLMSG_SPACE(len), gfp_mask);
+	skb = alloc_skb(NLMSG_SPACE(len), flags);
 	if (!skb)
 		return -ENOMEM;
 
@@ -85,9 +86,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 = tag;
 
-	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 +112,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 +151,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 +169,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 +185,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..5a98fc4 100644
--- a/drivers/scsi/scsi_tgt_lib.c
+++ b/drivers/scsi/scsi_tgt_lib.c
@@ -49,6 +49,7 @@ struct scsi_tgt_cmd {
 
 	struct list_head hash_list;
 	struct request *rq;
+	u64 tag;
 };
 
 #define TGT_HASH_ORDER	4
@@ -106,7 +107,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);
 }
@@ -118,19 +118,11 @@ static void init_scsi_tgt_cmd(struct req
 	struct list_head *head;
 	static u32 tag = 0;
 
-	tcmd->lun = rq->end_io_data;
-	bio_list_init(&tcmd->xfer_list);
-	bio_list_init(&tcmd->xfer_done_list);
-
 	spin_lock_irqsave(&qdata->cmd_hash_lock, flags);
 	rq->tag = tag++;
 	head = &qdata->cmd_hash[cmd_hashfn(rq->tag)];
 	list_add(&tcmd->hash_list, head);
 	spin_unlock_irqrestore(&qdata->cmd_hash_lock, flags);
-
-	tcmd->rq = rq;
-	rq->end_io_data = tcmd;
-	rq->flags |= REQ_DONTPREP;
 }
 
 static void scsi_tgt_uspace_send_fn(void *data)
@@ -148,33 +140,22 @@ retry:
 	if (list_empty(&qdata->cmd_req))
 		return;
 
-	tcmd = kmem_cache_alloc(scsi_tgt_cmd_cache, GFP_ATOMIC);
-	if (!tcmd) {
-		err = -ENOMEM;
-		goto out;
-	}
-
 	mutex_lock(&qdata->cmd_req_mutex);
 
 	spin_lock_irqsave(&qdata->cmd_req_lock, flags);
 	if (list_empty(&qdata->cmd_req)) {
 		spin_unlock_irqrestore(&qdata->cmd_req_lock, flags);
 		mutex_unlock(&qdata->cmd_req_mutex);
-		kmem_cache_free(scsi_tgt_cmd_cache, tcmd);
 		goto out;
 	}
 	rq = list_entry_rq(qdata->cmd_req.next);
 	list_del_init(&rq->queuelist);
 	spin_unlock_irqrestore(&qdata->cmd_req_lock, flags);
 
-	if ((rq->flags & REQ_DONTPREP)) {
-		kmem_cache_free(scsi_tgt_cmd_cache, tcmd);
-		tcmd = rq->end_io_data;
-	} else
-		init_scsi_tgt_cmd(rq, tcmd);
-
+	tcmd = rq->end_io_data;
+	init_scsi_tgt_cmd(rq, tcmd);
 	cmd = rq->special;
-	err = scsi_tgt_uspace_send(cmd, tcmd->lun, GFP_ATOMIC);
+	err = scsi_tgt_uspace_send(cmd, tcmd->lun, tcmd->tag, GFP_ATOMIC);
 	if (err < 0) {
 		eprintk("failed to send: %p %d\n", cmd, err);
 
@@ -266,20 +247,35 @@ EXPORT_SYMBOL_GPL(scsi_tgt_cmd_to_host);
  * @scsilun:	scsi lun
  * @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)
+int scsi_tgt_queue_command(struct scsi_cmnd *cmd, struct scsi_lun *scsilun,
+			   u64 tag)
 {
 	struct request_queue *q = cmd->request->q;
 	struct scsi_tgt_queuedata *qdata = q->queuedata;
 	unsigned long flags;
+	struct scsi_tgt_cmd *tcmd;
+
+	/*
+	 * It would be better to allocate scsi_tgt_cmd structure in
+	 * scsi_host_get_command and not to fail due to OOM.
+	 */
+	tcmd = kmem_cache_alloc(scsi_tgt_cmd_cache, GFP_ATOMIC);
+	if (!tcmd)
+		return -ENOMEM;
+	cmd->request->end_io_data = tcmd;
 
-	cmd->request->end_io_data = scsilun;
+	bio_list_init(&tcmd->xfer_list);
+	bio_list_init(&tcmd->xfer_done_list);
+	tcmd->lun = scsilun;
+	tcmd->tag = tag;
+	tcmd->rq = cmd->request;
 
 	spin_lock_irqsave(&qdata->cmd_req_lock, flags);
 	list_add_tail(&cmd->request->queuelist, &qdata->cmd_req);
 	spin_unlock_irqrestore(&qdata->cmd_req_lock, flags);
 
 	queue_work(scsi_tgtd, &qdata->uspace_send_work);
+	return 0;
 }
 EXPORT_SYMBOL_GPL(scsi_tgt_queue_command);
 
@@ -293,12 +289,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 +486,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 +548,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 +592,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..77a1d06 100644
--- a/drivers/scsi/scsi_tgt_priv.h
+++ b/drivers/scsi/scsi_tgt_priv.h
@@ -4,18 +4,21 @@ struct Scsi_Host;
 struct task_struct;
 
 /* tmp - will replace with SCSI logging stuff */
-#define dprintk(fmt, args...)					\
+#define eprintk(fmt, args...)					\
 do {								\
 	printk("%s(%d) " fmt, __FUNCTION__, __LINE__, ##args);	\
 } while (0)
 
-#define eprintk dprintk
+#define dprintk eprintk
 
 extern void scsi_tgt_if_exit(void);
 extern int scsi_tgt_if_init(void);
 
-extern int scsi_tgt_uspace_send(struct scsi_cmnd *cmd, struct scsi_lun *lun, gfp_t flags);
+extern int scsi_tgt_uspace_send(struct scsi_cmnd *cmd, struct scsi_lun *lun,
+				u64 tag, gfp_t flags);
 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..2d65be7 100644
--- a/include/scsi/scsi_tgt.h
+++ b/include/scsi/scsi_tgt.h
@@ -6,6 +6,8 @@ struct Scsi_Host;
 struct scsi_cmnd;
 struct scsi_lun;
 
-extern struct Scsi_Host *scsi_tgt_cmd_to_host(struct scsi_cmnd *cmd);
+extern struct Scsi_Host *scsi_tgt_cmd_to_host(struct scsi_cmnd *);
 extern int scsi_tgt_alloc_queue(struct Scsi_Host *);
-extern void scsi_tgt_queue_command(struct scsi_cmnd *, struct scsi_lun *, int);
+extern int 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.5
-
: 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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [SCSI Target Devel]     [Linux SCSI Target Infrastructure]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Linux IIO]     [Samba]     [Device Mapper]
  Powered by Linux