[SCSI] iscsi class: add bsg support to iscsi class
authorMike Christie <michaelc@cs.wisc.edu>
Mon, 25 Jul 2011 18:48:50 +0000 (13:48 -0500)
committerJames Bottomley <JBottomley@Parallels.com>
Sat, 27 Aug 2011 14:36:21 +0000 (08:36 -0600)
This patch adds bsg support to the iscsi class. There is only
1 request, the host vendor one, supported. It is expected that
this would be used for things like flash updates.

This patch is made over this one
http://marc.info/?l=linux-scsi&m=131149780020992&w=2

Signed-off-by: Mike Christie <michaelc@cs.wisc.edu>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
drivers/scsi/Kconfig
drivers/scsi/scsi_transport_iscsi.c
include/scsi/scsi_bsg_iscsi.h [new file with mode: 0644]
include/scsi/scsi_transport_iscsi.h

index 8d9dae89f065cfc650fdb9c8a3873425a904d741..4a79b9d8615617463048b37badb6cfc95918b481 100644 (file)
@@ -309,6 +309,7 @@ config SCSI_FC_TGT_ATTRS
 config SCSI_ISCSI_ATTRS
        tristate "iSCSI Transport Attributes"
        depends on SCSI && NET
+       select BLK_DEV_BSGLIB
        help
          If you wish to export transport-specific information about
          each attached iSCSI device to sysfs, say Y.
index 13f90515ff6eabfb8916a9267ba514259730e5a4..cde679f1b01d14778fb342d5539b96a003436a25 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/module.h>
 #include <linux/mutex.h>
 #include <linux/slab.h>
+#include <linux/bsg-lib.h>
 #include <net/tcp.h>
 #include <scsi/scsi.h>
 #include <scsi/scsi_host.h>
@@ -31,6 +32,7 @@
 #include <scsi/scsi_transport_iscsi.h>
 #include <scsi/iscsi_if.h>
 #include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_bsg_iscsi.h>
 
 #define ISCSI_TRANSPORT_VERSION "2.0-870"
 
@@ -447,6 +449,99 @@ void iscsi_destroy_iface(struct iscsi_iface *iface)
 }
 EXPORT_SYMBOL_GPL(iscsi_destroy_iface);
 
+/*
+ * BSG support
+ */
+/**
+ * iscsi_bsg_host_dispatch - Dispatch command to LLD.
+ * @job: bsg job to be processed
+ */
+static int iscsi_bsg_host_dispatch(struct bsg_job *job)
+{
+       struct Scsi_Host *shost = iscsi_job_to_shost(job);
+       struct iscsi_bsg_request *req = job->request;
+       struct iscsi_bsg_reply *reply = job->reply;
+       struct iscsi_internal *i = to_iscsi_internal(shost->transportt);
+       int cmdlen = sizeof(uint32_t);  /* start with length of msgcode */
+       int ret;
+
+       /* check if we have the msgcode value at least */
+       if (job->request_len < sizeof(uint32_t)) {
+               ret = -ENOMSG;
+               goto fail_host_msg;
+       }
+
+       /* Validate the host command */
+       switch (req->msgcode) {
+       case ISCSI_BSG_HST_VENDOR:
+               cmdlen += sizeof(struct iscsi_bsg_host_vendor);
+               if ((shost->hostt->vendor_id == 0L) ||
+                   (req->rqst_data.h_vendor.vendor_id !=
+                       shost->hostt->vendor_id)) {
+                       ret = -ESRCH;
+                       goto fail_host_msg;
+               }
+               break;
+       default:
+               ret = -EBADR;
+               goto fail_host_msg;
+       }
+
+       /* check if we really have all the request data needed */
+       if (job->request_len < cmdlen) {
+               ret = -ENOMSG;
+               goto fail_host_msg;
+       }
+
+       ret = i->iscsi_transport->bsg_request(job);
+       if (!ret)
+               return 0;
+
+fail_host_msg:
+       /* return the errno failure code as the only status */
+       BUG_ON(job->reply_len < sizeof(uint32_t));
+       reply->reply_payload_rcv_len = 0;
+       reply->result = ret;
+       job->reply_len = sizeof(uint32_t);
+       bsg_job_done(job, ret, 0);
+       return 0;
+}
+
+/**
+ * iscsi_bsg_host_add - Create and add the bsg hooks to receive requests
+ * @shost: shost for iscsi_host
+ * @cls_host: iscsi_cls_host adding the structures to
+ */
+static int
+iscsi_bsg_host_add(struct Scsi_Host *shost, struct iscsi_cls_host *ihost)
+{
+       struct device *dev = &shost->shost_gendev;
+       struct iscsi_internal *i = to_iscsi_internal(shost->transportt);
+       struct request_queue *q;
+       char bsg_name[20];
+       int ret;
+
+       if (!i->iscsi_transport->bsg_request)
+               return -ENOTSUPP;
+
+       snprintf(bsg_name, sizeof(bsg_name), "iscsi_host%d", shost->host_no);
+
+       q = __scsi_alloc_queue(shost, bsg_request_fn);
+       if (!q)
+               return -ENOMEM;
+
+       ret = bsg_setup_queue(dev, q, bsg_name, iscsi_bsg_host_dispatch, 0);
+       if (ret) {
+               shost_printk(KERN_ERR, shost, "bsg interface failed to "
+                            "initialize - no request queue\n");
+               blk_cleanup_queue(q);
+               return ret;
+       }
+
+       ihost->bsg_q = q;
+       return 0;
+}
+
 static int iscsi_setup_host(struct transport_container *tc, struct device *dev,
                            struct device *cdev)
 {
@@ -456,13 +551,30 @@ static int iscsi_setup_host(struct transport_container *tc, struct device *dev,
        memset(ihost, 0, sizeof(*ihost));
        atomic_set(&ihost->nr_scans, 0);
        mutex_init(&ihost->mutex);
+
+       iscsi_bsg_host_add(shost, ihost);
+       /* ignore any bsg add error - we just can't do sgio */
+
+       return 0;
+}
+
+static int iscsi_remove_host(struct transport_container *tc,
+                            struct device *dev, struct device *cdev)
+{
+       struct Scsi_Host *shost = dev_to_shost(dev);
+       struct iscsi_cls_host *ihost = shost->shost_data;
+
+       if (ihost->bsg_q) {
+               bsg_remove_queue(ihost->bsg_q);
+               blk_cleanup_queue(ihost->bsg_q);
+       }
        return 0;
 }
 
 static DECLARE_TRANSPORT_CLASS(iscsi_host_class,
                               "iscsi_host",
                               iscsi_setup_host,
-                              NULL,
+                              iscsi_remove_host,
                               NULL);
 
 static DECLARE_TRANSPORT_CLASS(iscsi_session_class,
diff --git a/include/scsi/scsi_bsg_iscsi.h b/include/scsi/scsi_bsg_iscsi.h
new file mode 100644 (file)
index 0000000..fd5689d
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ *  iSCSI Transport BSG Interface
+ *
+ *  Copyright (C) 2009   James Smart, Emulex Corporation
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef SCSI_BSG_ISCSI_H
+#define SCSI_BSG_ISCSI_H
+
+/*
+ * This file intended to be included by both kernel and user space
+ */
+
+#include <scsi/scsi.h>
+
+/*
+ * iSCSI Transport SGIO v4 BSG Message Support
+ */
+
+/* Default BSG request timeout (in seconds) */
+#define ISCSI_DEFAULT_BSG_TIMEOUT      (10 * HZ)
+
+
+/*
+ * Request Message Codes supported by the iSCSI Transport
+ */
+
+/* define the class masks for the message codes */
+#define ISCSI_BSG_CLS_MASK     0xF0000000      /* find object class */
+#define ISCSI_BSG_HST_MASK     0x80000000      /* iscsi host class */
+
+/* iscsi host Message Codes */
+#define ISCSI_BSG_HST_VENDOR           (ISCSI_BSG_HST_MASK | 0x000000FF)
+
+
+/*
+ * iSCSI Host Messages
+ */
+
+/* ISCSI_BSG_HST_VENDOR : */
+
+/* Request:
+ * Note: When specifying vendor_id, be sure to read the Vendor Type and ID
+ *   formatting requirements specified in scsi_netlink.h
+ */
+struct iscsi_bsg_host_vendor {
+       /*
+        * Identifies the vendor that the message is formatted for. This
+        * should be the recipient of the message.
+        */
+       uint64_t vendor_id;
+
+       /* start of vendor command area */
+       uint32_t vendor_cmd[0];
+};
+
+/* Response:
+ */
+struct iscsi_bsg_host_vendor_reply {
+       /* start of vendor response area */
+       uint32_t vendor_rsp[0];
+};
+
+
+/* request (CDB) structure of the sg_io_v4 */
+struct iscsi_bsg_request {
+       uint32_t msgcode;
+       union {
+               struct iscsi_bsg_host_vendor    h_vendor;
+       } rqst_data;
+} __attribute__((packed));
+
+
+/* response (request sense data) structure of the sg_io_v4 */
+struct iscsi_bsg_reply {
+       /*
+        * The completion result. Result exists in two forms:
+        * if negative, it is an -Exxx system errno value. There will
+        * be no further reply information supplied.
+        * else, it's the 4-byte scsi error result, with driver, host,
+        * msg and status fields. The per-msgcode reply structure
+        * will contain valid data.
+        */
+       uint32_t result;
+
+       /* If there was reply_payload, how much was recevied ? */
+       uint32_t reply_payload_rcv_len;
+
+       union {
+               struct iscsi_bsg_host_vendor_reply      vendor_reply;
+       } reply_data;
+};
+
+
+#endif /* SCSI_BSG_ISCSI_H */
index 77e6dd60fb4bf070b5b72c69121a9a391f3b6a28..4a3edeeae8f655c83e042fe1aa635a2798f68f61 100644 (file)
@@ -38,6 +38,7 @@ struct iscsi_conn;
 struct iscsi_task;
 struct sockaddr;
 struct iscsi_iface;
+struct bsg_job;
 
 /**
  * struct iscsi_transport - iSCSI Transport template
@@ -141,9 +142,9 @@ struct iscsi_transport {
                                enum iscsi_param_type param_type,
                                int param, char *buf);
        mode_t (*attr_is_visible)(int param_type, int param);
+       int (*bsg_request)(struct bsg_job *job);
 };
 
-
 /*
  * transport registration upcalls
  */
@@ -227,8 +228,12 @@ struct iscsi_cls_session {
 struct iscsi_cls_host {
        atomic_t nr_scans;
        struct mutex mutex;
+       struct request_queue *bsg_q;
 };
 
+#define iscsi_job_to_shost(_job) \
+        dev_to_shost(_job->dev)
+
 extern void iscsi_host_for_each_session(struct Scsi_Host *shost,
                                void (*fn)(struct iscsi_cls_session *));