[Stgt-devel] [patch 1/2] passthrough target notification function

Robert Jennings rcj
Fri May 18 20:17:45 CEST 2007


We spoke about this last week on the mailing list in relation to 
pass-through for kernel lld's.  This would notify scsi_tgt of any
target logical units that should be handled in the kernel.  Here is a
first pass, this is the user-space portion.

Send target state updates to the kernel (in-kernel pass-through enablement)

 When a target is bound to an lld which resides in the kernel all of the 
 devices that will be handled in the kernel (pass-through) will be
 communicated to scsi_tgt.  Whenever a logicalunit is created or destroyed
 which has a target bound to an in-kernel lld, scsi_tgt will be notified.

 -Adding request and response handlers to pass information to the kernel
   -lu_update_notify function pointer to the tgt_driver structure.  
     This allows llds to track updates to logical units when needed.
   -kspace_send_lu_update(...) to be used by lu_update_notify.
     It should send enough info to the kernel to create or destroy a
     logicalunit mapping in the kernel.
   -kspace_recv_lu_update(...) will handle kernel responses to changes.
     The response isn't processed before the request returns back the user
     this is a bit limited, in that it can't warn the user of tgtadm of
     the failure before tgtadm returns.  In the case that the request
     asked for a device to be created or bound and the response from
     the kernel indicates a failure the device is destroyed.

 -The scsi_lu struct has several new fields.
   Adding local_host_no, local_channel, local_scsi_id, and local_lun to 
   store the scsi device data for the SG device associated with the 
   logicalunit.  Also added in_kernel_bypass flag to show that this device
   will use in-kernel processing of requests when an lld is kernel-based.

 -Updated the open function for sgv3/sgv4 to populate new scsi_lu fields.
   SG will get the use SG_GET_SCSI_ID to gather data for the new fields.

 -Modified tgt_device_create, tgt_device_destroy, tgt_bind_host_to_target,
  and tgt_target_destroy to call the lld's lu_update_notify function.

---
 usr/driver.h        |    1 +
 usr/ibmvio/ibmvio.c |    1 +
 usr/spt_sgv3.c      |   13 ++++++++
 usr/spt_sgv4.c      |   13 ++++++++
 usr/target.c        |   87 ++++++++++++++++++++++++++++++++++++++++++++--------
 usr/tgtd.h          |   14 ++++++++
 usr/tgtif.c         |   72 +++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 188 insertions(+), 13 deletions(-)

Index: b/usr/driver.h
===================================================================
--- a/usr/driver.h
+++ b/usr/driver.h
@@ -16,6 +16,7 @@ struct tgt_driver {
 
 	int (*cmd_end_notify)(uint64_t nid, int result, struct scsi_cmd *);
 	int (*mgmt_end_notify)(struct mgmt_req *);
+	int (*lu_update_notify)(struct scsi_lu *, int, int);
 
 	struct backingstore_template *default_bst;
 };
Index: b/usr/ibmvio/ibmvio.c
===================================================================
--- a/usr/ibmvio/ibmvio.c
+++ b/usr/ibmvio/ibmvio.c
@@ -277,5 +277,6 @@ struct tgt_driver ibmvio = {
 	.target_create		= ibmvio_target_create,
 	.cmd_end_notify		= kspace_send_cmd_res,
 	.mgmt_end_notify	= kspace_send_tsk_mgmt_res,
+	.lu_update_notify	= kspace_send_lu_update,
 	.default_bst		= &mmap_bst,
 };
Index: b/usr/spt_sgv3.c
===================================================================
--- a/usr/spt_sgv3.c
+++ b/usr/spt_sgv3.c
@@ -74,6 +74,7 @@ int spt_sg_open(struct scsi_lu *lu, char
 {
 	int err;
 	int nr_queue_cmd;
+	Sg_scsi_id scsi_id;
 
 	*size = 0;
 	*fd = open(path, O_RDWR | O_NONBLOCK);
@@ -88,6 +89,18 @@ int spt_sg_open(struct scsi_lu *lu, char
 		goto close_fd;
 	}
 
+	err = ioctl(*fd, SG_GET_SCSI_ID, &scsi_id);
+	if (err) {
+		eprintf("can't get the scsi id for %s\n", path);
+		goto close_fd;
+	}
+
+	lu->in_kernel_bypass = 1;
+	lu->local_host_no = scsi_id.host_no;
+	lu->local_channel = scsi_id.channel;
+	lu->local_scsi_id = scsi_id.scsi_id;
+	lu->local_lun = scsi_id.lun;
+
 	err = tgt_event_add(*fd, EPOLLIN, sgv3_handler, lu);
 	if (err) {
 		free(lu);
Index: b/usr/spt_sgv4.c
===================================================================
--- a/usr/spt_sgv4.c
+++ b/usr/spt_sgv4.c
@@ -82,6 +82,7 @@ int spt_sg_open(struct scsi_lu *lu, char
 	struct timeval t;
 	struct sg_io_v4 hdr, *h;
 	int nr_queue_cmd;
+	Sg_scsi_id scsi_id;
 
 	/* we assume something like /dev/sda */
 	eprintf("%Zd %Zd %Zd\n", sizeof(hdr), sizeof(*h), sizeof(struct sg_io_v4));
@@ -165,6 +166,18 @@ int spt_sg_open(struct scsi_lu *lu, char
 		goto close_fd;
 	}
 
+	err = ioctl(*fd, SG_GET_SCSI_ID, &scsi_id);
+	if (err) {
+		eprintf("can't get the scsi id for %s\n", path);
+		goto close_fd;
+	}
+
+	lu->in_kernel_bypass = 1;
+	lu->local_host_no = scsi_id.host_no;
+	lu->local_channel = scsi_id.channel;
+	lu->local_scsi_id = scsi_id.scsi_id;
+	lu->local_lun = scsi_id.lun;
+
 	err = tgt_event_add(*fd, EPOLLIN, sgv4_handler, lu);
 	if (err) {
 		free(lu);
Index: b/usr/target.c
===================================================================
--- a/usr/target.c
+++ b/usr/target.c
@@ -42,7 +42,7 @@ extern struct device_type_template sbc_t
 
 static LIST_HEAD(target_list);
 
-static struct target *target_lookup(int tid)
+struct target *target_lookup(int tid)
 {
 	struct target *target;
 	list_for_each_entry(target, &target_list, target_siblings)
@@ -121,7 +121,7 @@ int it_nexus_destroy(int tid, uint64_t i
 	return 0;
 }
 
-static struct scsi_lu *device_lookup(struct target *target, uint64_t lun)
+struct scsi_lu *device_lookup(struct target *target, uint64_t lun)
 {
 	struct scsi_lu *lu;
 
@@ -207,12 +207,37 @@ __device_lookup(int tid, uint64_t lun, s
 	return lu;
 }
 
+/*
+ * if we have lots of host, use something like radix tree for
+ * efficiency.
+ */
+static LIST_HEAD(bound_host_list);
+
+struct bound_host {
+	int host_no;
+	struct target *target;
+	struct list_head bhost_siblings;
+};
+
+int target_host_no(struct *target) {
+	struct bound_host *bhost;
+	int host_no = -1;
+	list_for_each_entry(bhost, &bound_host_list, bhost_siblings) {
+		if (bhost->target == target) {
+			host_no = bhost->host_no;
+			break;
+		}
+	}
+	return host_no;
+}
+
 int tgt_device_create(int tid, uint64_t lun, char *args)
 {
 	char *p;
 	int err;
 	struct target *target;
 	struct scsi_lu *lu, *pos;
+	int host_no;
 
 	dprintf("%d %" PRIu64 "\n", tid, lun);
 
@@ -255,6 +280,17 @@ int tgt_device_create(int tid, uint64_t 
 	if (target->dev_type_template.device_init)
 		target->dev_type_template.device_init(lu);
 
+	if (lu->in_kernel_bypass) {
+		host_no = target_host_no(target);
+
+		if (host_no >= 0 &&
+		    (tgt_drivers[target->lid])->lu_update_notify) {
+			(tgt_drivers[target->lid])->lu_update_notify(lu,
+			                                     host_no,
+			                                     TGT_LU_OP_CREATE);
+		}
+	}
+
 	list_for_each_entry(pos, &target->device_list, device_siblings) {
 		if (lu->lun < pos->lun)
 			break;
@@ -269,6 +305,7 @@ int tgt_device_destroy(int tid, uint64_t
 {
 	struct target *target;
 	struct scsi_lu *lu;
+	int host_no;
 
 	dprintf("%u %" PRIu64 "\n", tid, lun);
 
@@ -284,6 +321,17 @@ int tgt_device_destroy(int tid, uint64_t
 	free(lu->path);
 	list_del(&lu->device_siblings);
 
+	if (lu->in_kernel_bypass) {
+		host_no = target_host_no(target);
+
+		if (host_no >= 0 &&
+		    (tgt_drivers[target->lid])->lu_update_notify) {
+			(tgt_drivers[target->lid])->lu_update_notify(lu,
+			                                     host_no,
+			                                     TGT_LU_OP_DESTROY);
+		}
+	}
+
 	target->bst->bs_close(lu);
 	free(lu);
 	return 0;
@@ -969,17 +1017,20 @@ char *acl_get(int tid, int idx)
 	return NULL;
 }
 
-/*
- * if we have lots of host, use something like radix tree for
- * efficiency.
- */
-static LIST_HEAD(bound_host_list);
+static void tgt_bound_host_update_lu(struct bound_host *bhost, int op)
+{
+	struct tgt_driver *drv;
+	struct scsi_lu *lu;
 
-struct bound_host {
-	int host_no;
-	struct target *target;
-	struct list_head bhost_siblings;
-};
+	drv = tgt_drivers[bhost->target->lid];
+	if(drv->lu_update_notify) {
+		list_for_each_entry(lu, &bhost->target->device_list,
+		                    device_siblings) {
+			if(lu->in_kernel_bypass)
+				drv->lu_update_notify(lu, bhost->host_no, op);
+		}
+	}
+}
 
 int tgt_bind_host_to_target(int tid, int host_no)
 {
@@ -1006,6 +1057,8 @@ int tgt_bind_host_to_target(int tid, int
 	bhost->host_no = host_no;
 	bhost->target = target;
 
+	tgt_bound_host_update_lu(bhost, TGT_LU_OP_CREATE);
+
 	list_add(&bhost->bhost_siblings, &bound_host_list);
 
 	dprintf("bound the scsi host %d to the target %d\n", host_no, host_no);
@@ -1023,6 +1076,9 @@ int tgt_unbind_host_to_target(int tid, i
 				eprintf("the target has IT_nexus\n");
 				return -EBUSY;
 			}
+
+			tgt_bound_host_update_lu(bhost, TGT_LU_OP_DESTROY);
+
 			list_del(&bhost->bhost_siblings);
 			free(bhost);
 			return 0;
@@ -1309,7 +1365,12 @@ int tgt_target_destroy(int lld_no, int t
 	while (!list_empty(&target->device_list)) {
 		lu = list_entry(target->device_list.next, struct scsi_lu,
 				device_siblings);
-		tgt_device_destroy(tid, lu->lun);
+		ret = tgt_device_destroy(tid, lu->lun);
+		if (ret == TGTADM_LUN_ACTIVE) {
+			eprintf("lun %#lld on target %d is still active\n",
+				lu->lun, tid);
+			return ret;
+		}
 	}
 
 	if (tgt_drivers[lld_no]->target_destroy) {
Index: b/usr/tgtd.h
===================================================================
--- a/usr/tgtd.h
+++ b/usr/tgtd.h
@@ -38,6 +38,14 @@ struct scsi_lu {
 	char scsi_sn[SCSI_SN_LEN];
 	char *path;
 
+	/* the scsi id of the local device backing this logical unit,
+	 * used for pass-through when notifying kernel llds */
+	char in_kernel_bypass;
+	int local_host_no;
+	int local_channel;
+	int local_scsi_id;
+	int local_lun;
+
 	/* the list of devices belonging to a target */
 	struct list_head device_siblings;
 
@@ -51,6 +59,9 @@ struct scsi_lu {
 	uint8_t d_sense;
 };
 
+#define TGT_LU_OP_DESTROY	0x0000
+#define TGT_LU_OP_CREATE	0x0001
+
 struct scsi_cmd {
 	struct target *c_target;
 	/* linked it_nexus->cmd_hash_list */
@@ -128,6 +139,7 @@ struct device_type_template {
 
 extern int kspace_send_tsk_mgmt_res(struct mgmt_req *mreq);
 extern int kspace_send_cmd_res(uint64_t nid, int result, struct scsi_cmd *);
+extern int kspace_send_lu_update(struct scsi_lu *, int, int);
 
 extern int ipc_init(void);
 extern int tgt_device_create(int tid, uint64_t lun, char *args);
@@ -136,6 +148,7 @@ extern int tgt_device_update(int tid, ui
 extern int device_reserve(struct scsi_cmd *cmd);
 extern int device_release(int tid, uint64_t itn_id, uint64_t lun, int force);
 extern int device_reserved(struct scsi_cmd *cmd);
+extern struct scsi_lu *device_lookup(struct target *target, uint64_t lun);
 
 extern int tgt_target_create(int lld, int tid, char *args, int t_type);
 extern int tgt_target_destroy(int lld, int tid);
@@ -152,6 +165,7 @@ extern void tgt_event_del(int fd);
 extern int tgt_event_modify(int fd, int events);
 extern int target_cmd_queue(int tid, struct scsi_cmd *cmd);
 extern void target_cmd_done(struct scsi_cmd *cmd);
+extern struct target *target_lookup(int tid);
 struct scsi_cmd *target_cmd_lookup(int tid, uint64_t itn_id, uint64_t tag);
 extern void target_mgmt_request(int tid, uint64_t itn_id, uint64_t req_id,
 				int function, uint8_t *lun, uint64_t tag,
Index: b/usr/tgtif.c
===================================================================
--- a/usr/tgtif.c
+++ b/usr/tgtif.c
@@ -33,6 +33,7 @@
 #include <sys/stat.h>
 #include <sys/sysmacros.h>
 #include <linux/types.h>
+#include <linux/errno.h>
 #ifndef aligned_u64
 #define aligned_u64 unsigned long long __attribute__((aligned(8)))
 #endif
@@ -88,6 +89,74 @@ static int kreq_send(struct tgt_event *p
 	return 0;
 }
 
+int kspace_send_lu_update(struct scsi_lu *lu, int host_no, int op) {
+	struct tgt_event ev;
+	int ret;
+
+	memset(&ev, 0, sizeof(ev));
+
+	ev.hdr.type = TGT_UEVENT_LU_UPDT_REQ;
+	ev.p.lu_updt_req.op = op;
+	ev.p.lu_updt_req.local_host_no = lu->local_host_no;
+	ev.p.lu_updt_req.local_channel = lu->local_channel;
+	ev.p.lu_updt_req.local_scsi_id = lu->local_scsi_id;
+	ev.p.lu_updt_req.local_lun = lu->local_lun;
+	ev.p.lu_updt_req.tgt_lun = lu->lun;
+	ev.p.lu_updt_req.tgt_host_no = host_no;
+
+	ret = kreq_send(&ev);
+
+	return ret;
+}
+
+/* Process returning event from the kernel for logicalunit update operations.
+ * Find the original update and mark complete and store the return value
+ * from the kernel.
+ */
+int kspace_recv_lu_update(struct tgt_event *ev)
+{
+	int host_no;
+	uint64_t lun;
+	int tid;
+	void *tgt;
+	struct scsi_lu *lu;
+
+	/*
+	 * Reverse the operation (limited function)
+	 *
+	 * If there was a error binding the logical unit with a physical sg
+	 * device in the kernel, update the device to reflect that.
+	 *
+	 * Can't recreate a deleted device, what if the target has been
+	 * deleted already?  At best this can delete an added device,
+	 * but not add a deleted device.
+	 */
+	if (ev->p.lu_updt_rsp.result &&
+			ev->p.lu_updt_rsp.op == TGT_LU_OP_CREATE) {
+		host_no = ev->p.lu_updt_rsp.tgt_host_no;
+		lun = ev->p.lu_updt_rsp.tgt_lun;
+		tid = tgt_bound_target_lookup(host_no);
+		tgt = target_lookup(tid);
+		if(!tgt) {
+			/* target does not exist */
+			return -ENOENT;
+		}
+
+		lu = device_lookup(tgt, lun);
+		if (lu) {
+			dprintf("Kernel result of create for "
+			        "(%d:%#08llx) was %d, removing device\n.",
+				host_no, lun, ev->p.lu_updt_rsp.result);
+			/* Do not notify kernel */
+			lu->in_kernel_bypass = 0;
+			tgt_device_destroy(tid, lun);
+		}
+	}
+
+	return 0;
+}
+
+
 int kspace_send_tsk_mgmt_res(struct mgmt_req *mreq)
 {
 	struct tgt_event ev;
@@ -281,6 +350,9 @@ retry:
 	case TGT_KEVENT_TSK_MGMT_REQ:
 		kern_mgmt_request(ev);
 		break;
+	case TGT_KEVENT_LU_UPDT_RSP:
+		kspace_recv_lu_update(ev);
+		break;
 	default:
 		eprintf("unknown event %u\n", ev->hdr.type);
 	}



More information about the stgt mailing list