[Stgt-devel] [patch 2/2] passthrough target notification function
Robert Jennings
rcj
Fri May 18 20:22:50 CEST 2007
Add target logicalunit state management to scsi_tgt
(required for in-kernel pass-through)
Take the data from user space and correlate this with kernel
structures for scsi devices and then properly managing their use.
This will keep the kernel drivers from needing to go to userspace for
pass-through targets.
-Use scsi_[un]register_interface to get notifications for each scsi
device added/removed; scsi_tgt will maintain a list of all existing
scsi devices.
-Update scsi_tgt_if.h for new u->k/k->u messages
-Create/destroy requests from tgtd will associate scsi devices with
tgt logicalunits through scsi_tgt_kspace_lu_update(). Results of the
operation are sent back with scsi_tgt_uspace_send_lu_update_resp().
---
drivers/scsi/scsi_tgt_if.c | 28 ++++++
drivers/scsi/scsi_tgt_lib.c | 209 +++++++++++++++++++++++++++++++++++++++++++
drivers/scsi/scsi_tgt_priv.h | 5 +
include/scsi/scsi_tgt.h | 12 ++
include/scsi/scsi_tgt_if.h | 22 +++++
5 files changed, 276 insertions(+)
Index: b/drivers/scsi/scsi_tgt_if.c
===================================================================
--- a/drivers/scsi/scsi_tgt_if.c
+++ b/drivers/scsi/scsi_tgt_if.c
@@ -199,6 +199,25 @@ int scsi_tgt_uspace_send_it_nexus_reques
return err;
}
+int scsi_tgt_uspace_send_lu_update_resp(int tgt_host_no, u64 tgt_lun,
+ int op, int result)
+{
+ struct tgt_event ev;
+ int err;
+
+ memset(&ev, 0, sizeof(ev));
+ ev.p.lu_updt_rsp.op = op;
+ ev.p.lu_updt_rsp.result = result;
+ ev.p.lu_updt_rsp.tgt_host_no = tgt_host_no;
+ ev.p.lu_updt_rsp.tgt_lun = tgt_lun;
+ err = tgt_uspace_send_event(TGT_KEVENT_LU_UPDT_RSP, &ev);
+ if (err)
+ eprintk("tx buf is full, could not send\n");
+
+ return err;
+
+}
+
static int event_recv_msg(struct tgt_event *ev)
{
int err = 0;
@@ -226,6 +245,15 @@ static int event_recv_msg(struct tgt_eve
ev->p.it_nexus_rsp.itn_id,
ev->p.it_nexus_rsp.result);
break;
+ case TGT_UEVENT_LU_UPDT_REQ:
+ err = scsi_tgt_kspace_lu_update(ev->p.lu_updt_req.tgt_host_no,
+ ev->p.lu_updt_req.tgt_lun,
+ ev->p.lu_updt_req.local_host_no,
+ ev->p.lu_updt_req.local_channel,
+ ev->p.lu_updt_req.local_scsi_id,
+ ev->p.lu_updt_req.local_lun,
+ ev->p.lu_updt_req.op);
+ break;
default:
eprintk("unknown type %d\n", ev->hdr.type);
err = -EINVAL;
Index: b/drivers/scsi/scsi_tgt_lib.c
===================================================================
--- a/drivers/scsi/scsi_tgt_lib.c
+++ b/drivers/scsi/scsi_tgt_lib.c
@@ -20,12 +20,14 @@
* 02110-1301 USA
*/
#include <linux/blkdev.h>
+#include <linux/delay.h>
#include <linux/hash.h>
#include <linux/module.h>
#include <linux/pagemap.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
+#include <scsi/scsi_driver.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_tgt.h>
@@ -635,6 +637,205 @@ done:
return err;
}
+static LIST_HEAD(scsi_dev_list);
+static spinlock_t sdev_list_lock = SPIN_LOCK_UNLOCKED;
+
+/*
+ * Search the scsi_dev_list for a scsi_dev_node for our target lun.
+ * Need to hold sdev_list_lock prior to entry.
+ */
+struct scsi_dev_node *scsi_tgt_lun_to_dev(int host_no, u64 lun)
+{
+ struct scsi_dev_node *tmp_sdn;
+ lun = (lun) >> 56 & 0x001f; /* The incoming lun needs to be
+ stripped down to just the part
+ stored in the scsi_dev_node */
+
+ list_for_each_entry(tmp_sdn, &scsi_dev_list, node)
+ if(tmp_sdn->host_no == host_no) {
+ if(tmp_sdn->tgt_lun == lun) {
+ return tmp_sdn;
+ }
+ }
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(scsi_tgt_lun_to_dev);
+
+/*
+ * Callback when a scsi_device is added to the system
+ */
+static int add_scsi_device(struct class_device *cdev,
+ struct class_interface *cl_intf)
+{
+ struct scsi_device *sdev = to_scsi_device(cdev->dev);
+ struct scsi_dev_node *sdevnode =
+ kmalloc(sizeof(struct scsi_dev_node), GFP_ATOMIC);
+ unsigned long flags;
+
+ printk(KERN_DEBUG "add_scsi_device got %p, %d:%d:%d:%d, sdn=%p\n",
+ sdev, sdev->host->host_no, sdev->channel, sdev->id,
+ sdev->lun, sdevnode);
+
+ sdevnode->sdev = sdev;
+ sdevnode->host_no = -1;
+ sdevnode->tgt_lun = 0;
+
+ spin_lock_irqsave(&sdev_list_lock, flags);
+ list_add_tail(&sdevnode->node, &scsi_dev_list);
+ spin_unlock_irqrestore(&sdev_list_lock, flags);
+ return 0;
+}
+
+/*
+ * Callback when a scsi_device is removed from the system
+ */
+static void rem_scsi_device(struct class_device *cdev,
+ struct class_interface *cl_intf)
+{
+ struct scsi_dev_node *tmp_sdn;
+ struct scsi_device *sdev = to_scsi_device(cdev->dev);
+ unsigned long flags;
+
+ printk(KERN_DEBUG "rem_scsi_device got %p, %d:%d:%d:%d\n",
+ sdev, sdev->host->host_no, sdev->channel, sdev->id,
+ sdev->lun);
+
+ spin_lock_irqsave(&sdev_list_lock, flags);
+ list_for_each_entry(tmp_sdn, &scsi_dev_list, node) {
+ if (sdev == tmp_sdn->sdev) {
+ /* XXX RCJ need to add code to track usage */
+ list_del(&tmp_sdn->node);
+ kfree(tmp_sdn);
+ goto out;
+ }
+ }
+
+ printk(KERN_WARNING "rem_scsi_device: Could not find scsi device "
+ "%p %d:%d:%d:%d\n", sdev, sdev->host->host_no,
+ sdev->channel, sdev->id, sdev->lun);
+
+out:
+ spin_unlock_irqrestore(&sdev_list_lock, flags);
+ return;
+}
+
+static struct class_interface tgt_scsi_interface = {
+ .add = add_scsi_device,
+ .remove = rem_scsi_device,
+};
+
+static int scsi_tgt_lu_create(int tgt_host_no, u64 tgt_lun,
+ int local_host_no, int local_channel,
+ int local_scsi_id, int local_lun)
+{
+ struct scsi_dev_node *sdnp;
+ int err = 0;
+
+ sdnp = scsi_tgt_lun_to_dev(tgt_host_no, tgt_lun);
+ if (sdnp) {
+ printk(KERN_ERR "Attempt to add logical unit where "
+ "one exists, %d 0x%lx\n",
+ tgt_host_no, (u64)tgt_lun);
+ return -EEXIST;
+ }
+
+ err = -ENOENT;
+
+ list_for_each_entry(sdnp, &scsi_dev_list, node) {
+ if (sdnp->sdev->host->host_no == local_host_no &&
+ sdnp->sdev->channel == local_channel &&
+ sdnp->sdev->id == local_scsi_id &&
+ sdnp->sdev->lun == local_lun) {
+ sdnp->host_no = tgt_host_no;
+ sdnp->tgt_lun = tgt_lun;
+ err = 0;
+ break;
+ }
+ }
+ if (err)
+ printk(KERN_ERR "Unable to find scsi device "
+ "(%d:%d:%d:%d) to associate with "
+ "target (%d:0x%lx) from tgt)\n",
+ local_host_no, local_channel, local_scsi_id,
+ local_lun, tgt_host_no, (u64)tgt_lun);
+
+ return err;
+}
+
+static int scsi_tgt_lu_destroy(int tgt_host_no, u64 tgt_lun)
+{
+ struct scsi_dev_node *sdnp;
+
+ sdnp = scsi_tgt_lun_to_dev(tgt_host_no, tgt_lun);
+ if (sdnp)
+ sdnp->host_no = -1;
+ else {
+ printk(KERN_ERR "Unable to find target configuration "
+ "to be removed (%d:0%lx)\n",
+ tgt_host_no, (u64)tgt_lun);
+ return -ENOENT;
+ }
+
+ return 0;
+}
+
+int scsi_tgt_kspace_lu_update(int tgt_host_no, u64 tgt_lun,
+ int local_host_no, int local_channel,
+ int local_scsi_id, int local_lun,
+ int op)
+{
+ int flags, updt, send, retry;
+
+
+ if (op == 1) { /* TGT_LU_OP_CREATE */
+ spin_lock_irqsave(&sdev_list_lock, flags);
+ updt = scsi_tgt_lu_create(tgt_host_no, tgt_lun,
+ local_host_no, local_channel,
+ local_scsi_id, local_lun);
+ send = scsi_tgt_uspace_send_lu_update_resp(tgt_host_no,
+ tgt_lun, op, updt);
+ /*
+ * If the send failed and the lu_update create was sucessful,
+ * undo the changes.
+ */
+ if (send && !updt)
+ scsi_tgt_lu_destroy(tgt_host_no, tgt_lun);
+
+ spin_unlock_irqrestore(&sdev_list_lock, flags);
+ return send;
+ } else if (op == 0) { /* TGT_LU_OP_DESTROY */
+ spin_lock_irqsave(&sdev_list_lock, flags);
+ updt = scsi_tgt_lu_destroy(tgt_host_no, tgt_lun);
+
+ /* Try to send the result, retry for 5 seconds if busy */
+ for (retry = 24; retry; retry--) {
+ send = scsi_tgt_uspace_send_lu_update_resp(tgt_host_no,
+ tgt_lun, op, updt);
+ if (send == 0)
+ break;
+
+ msleep(250);
+ }
+
+ /*
+ * If the send failed and the lu_update destroy was sucessful,
+ * retry sending the response, else re-add target.
+ * Since the sdev_list_lock is held through this entire
+ * operation the device can be re-added safely.
+ * */
+ if (send && !updt)
+ scsi_tgt_lu_create(tgt_host_no, tgt_lun,
+ local_host_no, local_channel,
+ local_scsi_id, local_lun);
+
+ spin_unlock_irqrestore(&sdev_list_lock, flags);
+ return send;
+ }
+
+ printk(KERN_ERR "Unknown option for lu_update %d\n", op);
+ return -EINVAL;
+}
+
static int __init scsi_tgt_init(void)
{
int err;
@@ -655,6 +856,13 @@ static int __init scsi_tgt_init(void)
if (err)
goto destroy_wq;
+ err = scsi_register_interface(&tgt_scsi_interface);
+ if (err)
+ printk(KERN_WARNING "Could not register for scsi device "
+ "notification (%d)\n", err);
+ else
+ printk(KERN_WARNING "registered scsi for scsi device notification");
+
return 0;
destroy_wq:
@@ -667,6 +875,7 @@ free_kmemcache:
static void __exit scsi_tgt_exit(void)
{
destroy_workqueue(scsi_tgtd);
+ scsi_unregister_interface(&tgt_scsi_interface);
scsi_tgt_if_exit();
kmem_cache_destroy(scsi_tgt_cmd_cache);
}
Index: b/drivers/scsi/scsi_tgt_priv.h
===================================================================
--- a/drivers/scsi/scsi_tgt_priv.h
+++ b/drivers/scsi/scsi_tgt_priv.h
@@ -30,3 +30,8 @@ extern int scsi_tgt_kspace_tsk_mgmt(int
extern int scsi_tgt_uspace_send_it_nexus_request(int host_no, u64 it_nexus_id,
int function, char *initiator);
extern int scsi_tgt_kspace_it_nexus_rsp(int host_no, u64 it_nexus_id, int result);
+extern int scsi_tgt_kspace_lu_update(int tgt_host_no, u64 tgt_lun,
+ int local_host_no, int local_channel,
+ int local_scsi_id, int local_lun, int op);
+extern int scsi_tgt_uspace_send_lu_update_resp(int tgt_host_no, u64 tgt_lun,
+ int op, int result);
Index: b/include/scsi/scsi_tgt.h
===================================================================
--- a/include/scsi/scsi_tgt.h
+++ b/include/scsi/scsi_tgt.h
@@ -4,6 +4,17 @@
#include <linux/dma-mapping.h>
+/*
+ * scsi_dev_node maps scsi_device structures to their represenation from
+ * the tgt framework
+ */
+struct scsi_dev_node {
+ struct list_head node;
+ struct scsi_device *sdev;
+ u64 tgt_lun;
+ int host_no;
+};
+
struct Scsi_Host;
struct scsi_cmnd;
struct scsi_lun;
@@ -19,3 +30,4 @@ extern struct scsi_cmnd *scsi_host_get_c
extern void scsi_host_put_command(struct Scsi_Host *, struct scsi_cmnd *);
extern int scsi_tgt_it_nexus_create(struct Scsi_Host *, u64, char *);
extern int scsi_tgt_it_nexus_destroy(struct Scsi_Host *, u64);
+extern struct scsi_dev_node * scsi_tgt_lun_to_dev(int, u64);
Index: b/include/scsi/scsi_tgt_if.h
===================================================================
--- a/include/scsi/scsi_tgt_if.h
+++ b/include/scsi/scsi_tgt_if.h
@@ -26,12 +26,14 @@
#define TGT_UEVENT_CMD_RSP 0x0001
#define TGT_UEVENT_IT_NEXUS_RSP 0x0002
#define TGT_UEVENT_TSK_MGMT_RSP 0x0003
+#define TGT_UEVENT_LU_UPDT_REQ 0x0004
/* kernel -> user */
#define TGT_KEVENT_CMD_REQ 0x1001
#define TGT_KEVENT_CMD_DONE 0x1002
#define TGT_KEVENT_IT_NEXUS_REQ 0x1003
#define TGT_KEVENT_TSK_MGMT_REQ 0x1004
+#define TGT_KEVENT_LU_UPDT_RSP 0x1005
struct tgt_event_hdr {
uint16_t version;
@@ -40,6 +42,11 @@ struct tgt_event_hdr {
uint16_t len;
} __attribute__ ((aligned (sizeof(uint64_t))));
+enum {
+ TGT_LU_OP_DESTROY,
+ TGT_LU_OP_CREATE
+};
+
struct tgt_event {
struct tgt_event_hdr hdr;
@@ -68,6 +75,15 @@ struct tgt_event {
aligned_u64 itn_id;
__u32 function;
} it_nexus_rsp;
+ struct {
+ int op;
+ int local_host_no;
+ int local_channel;
+ int local_scsi_id;
+ int local_lun;
+ int tgt_host_no;
+ aligned_u64 tgt_lun;
+ } lu_updt_req;
/* kernel -> user */
struct {
@@ -100,6 +116,12 @@ struct tgt_event {
__u32 max_cmds;
__u8 initiator_id[16];
} it_nexus_req;
+ struct {
+ int op;
+ int result;
+ int tgt_host_no;
+ aligned_u64 tgt_lun;
+ } lu_updt_rsp;
} p;
} __attribute__ ((aligned (sizeof(uint64_t))));
More information about the stgt
mailing list