[SCSI] qla2xxx: Support for asynchronous TM and Marker IOCBs.
[safe/jmp/linux-2.6] / drivers / scsi / scsi_transport_fc.c
index dcef787..0681378 100644 (file)
@@ -27,6 +27,8 @@
  */
 #include <linux/module.h>
 #include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
 #include <scsi/scsi_device.h>
 #include <scsi/scsi_host.h>
 #include <scsi/scsi_transport.h>
@@ -35,6 +37,7 @@
 #include <linux/netlink.h>
 #include <net/netlink.h>
 #include <scsi/scsi_netlink_fc.h>
+#include <scsi/scsi_bsg_fc.h>
 #include "scsi_priv.h"
 #include "scsi_transport_fc_internal.h"
 
@@ -43,6 +46,10 @@ static void fc_vport_sched_delete(struct work_struct *work);
 static int fc_vport_setup(struct Scsi_Host *shost, int channel,
        struct device *pdev, struct fc_vport_identifiers  *ids,
        struct fc_vport **vport);
+static int fc_bsg_hostadd(struct Scsi_Host *, struct fc_host_attrs *);
+static int fc_bsg_rportadd(struct Scsi_Host *, struct fc_rport *);
+static void fc_bsg_remove(struct request_queue *);
+static void fc_bsg_goose_queue(struct fc_rport *);
 
 /*
  * Redefine so that we can have same named attributes in the
@@ -95,7 +102,7 @@ static struct {
        { FC_PORTTYPE_NPORT,    "NPort (fabric via point-to-point)" },
        { FC_PORTTYPE_NLPORT,   "NLPort (fabric via loop)" },
        { FC_PORTTYPE_LPORT,    "LPort (private loop)" },
-       { FC_PORTTYPE_PTP,      "Point-To-Point (direct nport connection" },
+       { FC_PORTTYPE_PTP,      "Point-To-Point (direct nport connection)" },
        { FC_PORTTYPE_NPIV,             "NPIV VPORT" },
 };
 fc_enum_name_search(port_type, fc_port_type, fc_port_type_names)
@@ -286,7 +293,7 @@ static void fc_scsi_scan_rport(struct work_struct *work);
 #define FC_STARGET_NUM_ATTRS   3
 #define FC_RPORT_NUM_ATTRS     10
 #define FC_VPORT_NUM_ATTRS     9
-#define FC_HOST_NUM_ATTRS      21
+#define FC_HOST_NUM_ATTRS      22
 
 struct fc_internal {
        struct scsi_transport_template t;
@@ -411,13 +418,26 @@ static int fc_host_setup(struct transport_container *tc, struct device *dev,
                return -ENOMEM;
        }
 
+       fc_bsg_hostadd(shost, fc_host);
+       /* ignore any bsg add error - we just can't do sgio */
+
+       return 0;
+}
+
+static int fc_host_remove(struct transport_container *tc, struct device *dev,
+                        struct device *cdev)
+{
+       struct Scsi_Host *shost = dev_to_shost(dev);
+       struct fc_host_attrs *fc_host = shost_to_fc_host(shost);
+
+       fc_bsg_remove(fc_host->rqst_q);
        return 0;
 }
 
 static DECLARE_TRANSPORT_CLASS(fc_host_class,
                               "fc_host",
                               fc_host_setup,
-                              NULL,
+                              fc_host_remove,
                               NULL);
 
 /*
@@ -456,7 +476,8 @@ MODULE_PARM_DESC(dev_loss_tmo,
                 "Maximum number of seconds that the FC transport should"
                 " insulate the loss of a remote port. Once this value is"
                 " exceeded, the scsi target is removed. Value should be"
-                " between 1 and SCSI_DEVICE_BLOCK_MAX_TIMEOUT.");
+                " between 1 and SCSI_DEVICE_BLOCK_MAX_TIMEOUT if"
+                " fast_io_fail_tmo is not set.");
 
 /*
  * Netlink Infrastructure
@@ -533,12 +554,8 @@ fc_host_post_event(struct Scsi_Host *shost, u32 event_number,
        event->event_code = event_code;
        event->event_data = event_data;
 
-       err = nlmsg_multicast(scsi_nl_sock, skb, 0, SCSI_NL_GRP_FC_EVENTS,
-                             GFP_KERNEL);
-       if (err && (err != -ESRCH))     /* filter no recipient errors */
-               /* nlmsg_multicast already kfree_skb'd */
-               goto send_fail;
-
+       nlmsg_multicast(scsi_nl_sock, skb, 0, SCSI_NL_GRP_FC_EVENTS,
+                       GFP_KERNEL);
        return;
 
 send_fail_skb:
@@ -607,12 +624,8 @@ fc_host_post_vendor_event(struct Scsi_Host *shost, u32 event_number,
        event->event_code = FCH_EVT_VENDOR_UNIQUE;
        memcpy(&event->event_data, data_buf, data_len);
 
-       err = nlmsg_multicast(scsi_nl_sock, skb, 0, SCSI_NL_GRP_FC_EVENTS,
-                             GFP_KERNEL);
-       if (err && (err != -ESRCH))     /* filter no recipient errors */
-               /* nlmsg_multicast already kfree_skb'd */
-               goto send_vendor_fail;
-
+       nlmsg_multicast(scsi_nl_sock, skb, 0, SCSI_NL_GRP_FC_EVENTS,
+                       GFP_KERNEL);
        return;
 
 send_vendor_fail_skb:
@@ -638,11 +651,22 @@ static __init int fc_transport_init(void)
                return error;
        error = transport_class_register(&fc_vport_class);
        if (error)
-               return error;
+               goto unreg_host_class;
        error = transport_class_register(&fc_rport_class);
        if (error)
-               return error;
-       return transport_class_register(&fc_transport_class);
+               goto unreg_vport_class;
+       error = transport_class_register(&fc_transport_class);
+       if (error)
+               goto unreg_rport_class;
+       return 0;
+
+unreg_rport_class:
+       transport_class_unregister(&fc_rport_class);
+unreg_vport_class:
+       transport_class_unregister(&fc_vport_class);
+unreg_host_class:
+       transport_class_unregister(&fc_host_class);
+       return error;
 }
 
 static void __exit fc_transport_exit(void)
@@ -810,7 +834,7 @@ static ssize_t
 store_fc_rport_dev_loss_tmo(struct device *dev, struct device_attribute *attr,
                            const char *buf, size_t count)
 {
-       int val;
+       unsigned long val;
        struct fc_rport *rport = transport_class_to_rport(dev);
        struct Scsi_Host *shost = rport_to_shost(rport);
        struct fc_internal *i = to_fc_internal(shost->transportt);
@@ -820,9 +844,23 @@ store_fc_rport_dev_loss_tmo(struct device *dev, struct device_attribute *attr,
            (rport->port_state == FC_PORTSTATE_NOTPRESENT))
                return -EBUSY;
        val = simple_strtoul(buf, &cp, 0);
-       if ((*cp && (*cp != '\n')) ||
-           (val < 0) || (val > SCSI_DEVICE_BLOCK_MAX_TIMEOUT))
+       if ((*cp && (*cp != '\n')) || (val < 0))
+               return -EINVAL;
+
+       /*
+        * Check for overflow; dev_loss_tmo is u32
+        */
+       if (val > UINT_MAX)
                return -EINVAL;
+
+       /*
+        * If fast_io_fail is off we have to cap
+        * dev_loss_tmo at SCSI_DEVICE_BLOCK_MAX_TIMEOUT
+        */
+       if (rport->fast_io_fail_tmo == -1 &&
+           val > SCSI_DEVICE_BLOCK_MAX_TIMEOUT)
+               return -EINVAL;
+
        i->f->set_rport_dev_loss_tmo(rport, val);
        return count;
 }
@@ -903,9 +941,16 @@ store_fc_rport_fast_io_fail_tmo(struct device *dev,
                rport->fast_io_fail_tmo = -1;
        else {
                val = simple_strtoul(buf, &cp, 0);
-               if ((*cp && (*cp != '\n')) ||
-                   (val < 0) || (val >= rport->dev_loss_tmo))
+               if ((*cp && (*cp != '\n')) || (val < 0))
                        return -EINVAL;
+               /*
+                * Cap fast_io_fail by dev_loss_tmo or
+                * SCSI_DEVICE_BLOCK_MAX_TIMEOUT.
+                */
+               if ((val >= rport->dev_loss_tmo) ||
+                   (val > SCSI_DEVICE_BLOCK_MAX_TIMEOUT))
+                       return -EINVAL;
+
                rport->fast_io_fail_tmo = val;
        }
        return count;
@@ -1194,6 +1239,15 @@ store_fc_vport_delete(struct device *dev, struct device_attribute *attr,
 {
        struct fc_vport *vport = transport_class_to_vport(dev);
        struct Scsi_Host *shost = vport_to_shost(vport);
+       unsigned long flags;
+
+       spin_lock_irqsave(shost->host_lock, flags);
+       if (vport->flags & (FC_VPORT_DEL | FC_VPORT_CREATING)) {
+               spin_unlock_irqrestore(shost->host_lock, flags);
+               return -EBUSY;
+       }
+       vport->flags |= FC_VPORT_DELETING;
+       spin_unlock_irqrestore(shost->host_lock, flags);
 
        fc_queue_work(shost, &vport->vport_delete_work);
        return count;
@@ -1783,6 +1837,9 @@ store_fc_host_vport_delete(struct device *dev, struct device_attribute *attr,
        list_for_each_entry(vport, &fc_host->vports, peers) {
                if ((vport->channel == 0) &&
                    (vport->port_name == wwpn) && (vport->node_name == wwnn)) {
+                       if (vport->flags & (FC_VPORT_DEL | FC_VPORT_CREATING))
+                               break;
+                       vport->flags |= FC_VPORT_DELETING;
                        match = 1;
                        break;
                }
@@ -2374,6 +2431,7 @@ fc_rport_final_delete(struct work_struct *work)
        struct Scsi_Host *shost = rport_to_shost(rport);
        struct fc_internal *i = to_fc_internal(shost->transportt);
        unsigned long flags;
+       int do_callback = 0;
 
        /*
         * if a scan is pending, flush the SCSI Host work_q so that
@@ -2383,6 +2441,7 @@ fc_rport_final_delete(struct work_struct *work)
                scsi_flush_work(shost);
 
        fc_terminate_rport_io(rport);
+
        /*
         * Cancel any outstanding timers. These should really exist
         * only when rmmod'ing the LLDD and we're asking for
@@ -2407,10 +2466,23 @@ fc_rport_final_delete(struct work_struct *work)
        /*
         * Notify the driver that the rport is now dead. The LLDD will
         * also guarantee that any communication to the rport is terminated
+        *
+        * Avoid this call if we already called it when we preserved the
+        * rport for the binding.
         */
-       if (i->f->dev_loss_tmo_callbk)
+       spin_lock_irqsave(shost->host_lock, flags);
+       if (!(rport->flags & FC_RPORT_DEVLOSS_CALLBK_DONE) &&
+           (i->f->dev_loss_tmo_callbk)) {
+               rport->flags |= FC_RPORT_DEVLOSS_CALLBK_DONE;
+               do_callback = 1;
+       }
+       spin_unlock_irqrestore(shost->host_lock, flags);
+
+       if (do_callback)
                i->f->dev_loss_tmo_callbk(rport);
 
+       fc_bsg_remove(rport->rqst_q);
+
        transport_remove_device(dev);
        device_del(dev);
        transport_destroy_device(dev);
@@ -2498,6 +2570,9 @@ fc_rport_create(struct Scsi_Host *shost, int channel,
        transport_add_device(dev);
        transport_configure_device(dev);
 
+       fc_bsg_rportadd(shost, rport);
+       /* ignore any bsg add error - we just can't do sgio */
+
        if (rport->roles & FC_PORT_ROLE_FCP_TARGET) {
                /* initiate a scan of the target */
                rport->flags |= FC_RPORT_SCAN_PENDING;
@@ -2647,7 +2722,8 @@ fc_remote_port_add(struct Scsi_Host *shost, int channel,
                                spin_lock_irqsave(shost->host_lock, flags);
 
                                rport->flags &= ~(FC_RPORT_FAST_FAIL_TIMEDOUT |
-                                                 FC_RPORT_DEVLOSS_PENDING);
+                                                 FC_RPORT_DEVLOSS_PENDING |
+                                                 FC_RPORT_DEVLOSS_CALLBK_DONE);
 
                                /* if target, initiate a scan */
                                if (rport->scsi_target_id != -1) {
@@ -2661,6 +2737,8 @@ fc_remote_port_add(struct Scsi_Host *shost, int channel,
                                        spin_unlock_irqrestore(shost->host_lock,
                                                        flags);
 
+                               fc_bsg_goose_queue(rport);
+
                                return rport;
                        }
                }
@@ -2793,7 +2871,7 @@ void
 fc_remote_port_delete(struct fc_rport  *rport)
 {
        struct Scsi_Host *shost = rport_to_shost(rport);
-       int timeout = rport->dev_loss_tmo;
+       unsigned long timeout = rport->dev_loss_tmo;
        unsigned long flags;
 
        /*
@@ -2944,8 +3022,10 @@ fc_timeout_deleted_rport(struct work_struct *work)
        struct fc_rport *rport =
                container_of(work, struct fc_rport, dev_loss_work.work);
        struct Scsi_Host *shost = rport_to_shost(rport);
+       struct fc_internal *i = to_fc_internal(shost->transportt);
        struct fc_host_attrs *fc_host = shost_to_fc_host(shost);
        unsigned long flags;
+       int do_callback = 0;
 
        spin_lock_irqsave(shost->host_lock, flags);
 
@@ -3020,34 +3100,52 @@ fc_timeout_deleted_rport(struct work_struct *work)
        spin_unlock_irqrestore(shost->host_lock, flags);
        fc_terminate_rport_io(rport);
 
-       BUG_ON(rport->port_state != FC_PORTSTATE_NOTPRESENT);
+       spin_lock_irqsave(shost->host_lock, flags);
 
-       /* remove the identifiers that aren't used in the consisting binding */
-       switch (fc_host->tgtid_bind_type) {
-       case FC_TGTID_BIND_BY_WWPN:
-               rport->node_name = -1;
-               rport->port_id = -1;
-               break;
-       case FC_TGTID_BIND_BY_WWNN:
-               rport->port_name = -1;
-               rport->port_id = -1;
-               break;
-       case FC_TGTID_BIND_BY_ID:
-               rport->node_name = -1;
-               rport->port_name = -1;
-               break;
-       case FC_TGTID_BIND_NONE:        /* to keep compiler happy */
-               break;
+       if (rport->port_state == FC_PORTSTATE_NOTPRESENT) {     /* still missing */
+
+               /* remove the identifiers that aren't used in the consisting binding */
+               switch (fc_host->tgtid_bind_type) {
+               case FC_TGTID_BIND_BY_WWPN:
+                       rport->node_name = -1;
+                       rport->port_id = -1;
+                       break;
+               case FC_TGTID_BIND_BY_WWNN:
+                       rport->port_name = -1;
+                       rport->port_id = -1;
+                       break;
+               case FC_TGTID_BIND_BY_ID:
+                       rport->node_name = -1;
+                       rport->port_name = -1;
+                       break;
+               case FC_TGTID_BIND_NONE:        /* to keep compiler happy */
+                       break;
+               }
+
+               /*
+                * As this only occurs if the remote port (scsi target)
+                * went away and didn't come back - we'll remove
+                * all attached scsi devices.
+                */
+               rport->flags |= FC_RPORT_DEVLOSS_CALLBK_DONE;
+               fc_queue_work(shost, &rport->stgt_delete_work);
+
+               do_callback = 1;
        }
 
+       spin_unlock_irqrestore(shost->host_lock, flags);
+
        /*
-        * As this only occurs if the remote port (scsi target)
-        * went away and didn't come back - we'll remove
-        * all attached scsi devices.
+        * Notify the driver that the rport is now dead. The LLDD will
+        * also guarantee that any communication to the rport is terminated
+        *
+        * Note: we set the CALLBK_DONE flag above to correspond
         */
-       fc_queue_work(shost, &rport->stgt_delete_work);
+       if (do_callback && i->f->dev_loss_tmo_callbk)
+               i->f->dev_loss_tmo_callbk(rport);
 }
 
+
 /**
  * fc_timeout_fail_rport_io - Timeout handler for a fast io failing on a disconnected SCSI target.
  * @work:      rport to terminate io on.
@@ -3093,6 +3191,41 @@ fc_scsi_scan_rport(struct work_struct *work)
        spin_unlock_irqrestore(shost->host_lock, flags);
 }
 
+/**
+ * fc_block_scsi_eh - Block SCSI eh thread for blocked fc_rport
+ * @cmnd: SCSI command that scsi_eh is trying to recover
+ *
+ * This routine can be called from a FC LLD scsi_eh callback. It
+ * blocks the scsi_eh thread until the fc_rport leaves the
+ * FC_PORTSTATE_BLOCKED, or the fast_io_fail_tmo fires. This is
+ * necessary to avoid the scsi_eh failing recovery actions for blocked
+ * rports which would lead to offlined SCSI devices.
+ *
+ * Returns: 0 if the fc_rport left the state FC_PORTSTATE_BLOCKED.
+ *         FAST_IO_FAIL if the fast_io_fail_tmo fired, this should be
+ *         passed back to scsi_eh.
+ */
+int fc_block_scsi_eh(struct scsi_cmnd *cmnd)
+{
+       struct Scsi_Host *shost = cmnd->device->host;
+       struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device));
+       unsigned long flags;
+
+       spin_lock_irqsave(shost->host_lock, flags);
+       while (rport->port_state == FC_PORTSTATE_BLOCKED &&
+              !(rport->flags & FC_RPORT_FAST_FAIL_TIMEDOUT)) {
+               spin_unlock_irqrestore(shost->host_lock, flags);
+               msleep(1000);
+               spin_lock_irqsave(shost->host_lock, flags);
+       }
+       spin_unlock_irqrestore(shost->host_lock, flags);
+
+       if (rport->flags & FC_RPORT_FAST_FAIL_TIMEDOUT)
+               return FAST_IO_FAIL;
+
+       return 0;
+}
+EXPORT_SYMBOL(fc_block_scsi_eh);
 
 /**
  * fc_vport_setup - allocates and creates a FC virtual port.
@@ -3266,18 +3399,6 @@ fc_vport_terminate(struct fc_vport *vport)
        unsigned long flags;
        int stat;
 
-       spin_lock_irqsave(shost->host_lock, flags);
-       if (vport->flags & FC_VPORT_CREATING) {
-               spin_unlock_irqrestore(shost->host_lock, flags);
-               return -EBUSY;
-       }
-       if (vport->flags & (FC_VPORT_DEL)) {
-               spin_unlock_irqrestore(shost->host_lock, flags);
-               return -EALREADY;
-       }
-       vport->flags |= FC_VPORT_DELETING;
-       spin_unlock_irqrestore(shost->host_lock, flags);
-
        if (i->f->vport_delete)
                stat = i->f->vport_delete(vport);
        else
@@ -3334,6 +3455,606 @@ fc_vport_sched_delete(struct work_struct *work)
 }
 
 
+/*
+ * BSG support
+ */
+
+
+/**
+ * fc_destroy_bsgjob - routine to teardown/delete a fc bsg job
+ * @job:       fc_bsg_job that is to be torn down
+ */
+static void
+fc_destroy_bsgjob(struct fc_bsg_job *job)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&job->job_lock, flags);
+       if (job->ref_cnt) {
+               spin_unlock_irqrestore(&job->job_lock, flags);
+               return;
+       }
+       spin_unlock_irqrestore(&job->job_lock, flags);
+
+       put_device(job->dev);   /* release reference for the request */
+
+       kfree(job->request_payload.sg_list);
+       kfree(job->reply_payload.sg_list);
+       kfree(job);
+}
+
+/**
+ * fc_bsg_jobdone - completion routine for bsg requests that the LLD has
+ *                  completed
+ * @job:       fc_bsg_job that is complete
+ */
+static void
+fc_bsg_jobdone(struct fc_bsg_job *job)
+{
+       struct request *req = job->req;
+       struct request *rsp = req->next_rq;
+       int err;
+
+       err = job->req->errors = job->reply->result;
+
+       if (err < 0)
+               /* we're only returning the result field in the reply */
+               job->req->sense_len = sizeof(uint32_t);
+       else
+               job->req->sense_len = job->reply_len;
+
+       /* we assume all request payload was transferred, residual == 0 */
+       req->resid_len = 0;
+
+       if (rsp) {
+               WARN_ON(job->reply->reply_payload_rcv_len > rsp->resid_len);
+
+               /* set reply (bidi) residual */
+               rsp->resid_len -= min(job->reply->reply_payload_rcv_len,
+                                     rsp->resid_len);
+       }
+       blk_complete_request(req);
+}
+
+/**
+ * fc_bsg_softirq_done - softirq done routine for destroying the bsg requests
+ * @rq:        BSG request that holds the job to be destroyed
+ */
+static void fc_bsg_softirq_done(struct request *rq)
+{
+       struct fc_bsg_job *job = rq->special;
+       unsigned long flags;
+
+       spin_lock_irqsave(&job->job_lock, flags);
+       job->state_flags |= FC_RQST_STATE_DONE;
+       job->ref_cnt--;
+       spin_unlock_irqrestore(&job->job_lock, flags);
+
+       blk_end_request_all(rq, rq->errors);
+       fc_destroy_bsgjob(job);
+}
+
+/**
+ * fc_bsg_job_timeout - handler for when a bsg request timesout
+ * @req:       request that timed out
+ */
+static enum blk_eh_timer_return
+fc_bsg_job_timeout(struct request *req)
+{
+       struct fc_bsg_job *job = (void *) req->special;
+       struct Scsi_Host *shost = job->shost;
+       struct fc_internal *i = to_fc_internal(shost->transportt);
+       unsigned long flags;
+       int err = 0, done = 0;
+
+       if (job->rport && job->rport->port_state == FC_PORTSTATE_BLOCKED)
+               return BLK_EH_RESET_TIMER;
+
+       spin_lock_irqsave(&job->job_lock, flags);
+       if (job->state_flags & FC_RQST_STATE_DONE)
+               done = 1;
+       else
+               job->ref_cnt++;
+       spin_unlock_irqrestore(&job->job_lock, flags);
+
+       if (!done && i->f->bsg_timeout) {
+               /* call LLDD to abort the i/o as it has timed out */
+               err = i->f->bsg_timeout(job);
+               if (err == -EAGAIN) {
+                       job->ref_cnt--;
+                       return BLK_EH_RESET_TIMER;
+               } else if (err)
+                       printk(KERN_ERR "ERROR: FC BSG request timeout - LLD "
+                               "abort failed with status %d\n", err);
+       }
+
+       /* the blk_end_sync_io() doesn't check the error */
+       if (done)
+               return BLK_EH_NOT_HANDLED;
+       else
+               return BLK_EH_HANDLED;
+}
+
+static int
+fc_bsg_map_buffer(struct fc_bsg_buffer *buf, struct request *req)
+{
+       size_t sz = (sizeof(struct scatterlist) * req->nr_phys_segments);
+
+       BUG_ON(!req->nr_phys_segments);
+
+       buf->sg_list = kzalloc(sz, GFP_KERNEL);
+       if (!buf->sg_list)
+               return -ENOMEM;
+       sg_init_table(buf->sg_list, req->nr_phys_segments);
+       buf->sg_cnt = blk_rq_map_sg(req->q, req, buf->sg_list);
+       buf->payload_len = blk_rq_bytes(req);
+       return 0;
+}
+
+
+/**
+ * fc_req_to_bsgjob - Allocate/create the fc_bsg_job structure for the
+ *                   bsg request
+ * @shost:     SCSI Host corresponding to the bsg object
+ * @rport:     (optional) FC Remote Port corresponding to the bsg object
+ * @req:       BSG request that needs a job structure
+ */
+static int
+fc_req_to_bsgjob(struct Scsi_Host *shost, struct fc_rport *rport,
+       struct request *req)
+{
+       struct fc_internal *i = to_fc_internal(shost->transportt);
+       struct request *rsp = req->next_rq;
+       struct fc_bsg_job *job;
+       int ret;
+
+       BUG_ON(req->special);
+
+       job = kzalloc(sizeof(struct fc_bsg_job) + i->f->dd_bsg_size,
+                       GFP_KERNEL);
+       if (!job)
+               return -ENOMEM;
+
+       /*
+        * Note: this is a bit silly.
+        * The request gets formatted as a SGIO v4 ioctl request, which
+        * then gets reformatted as a blk request, which then gets
+        * reformatted as a fc bsg request. And on completion, we have
+        * to wrap return results such that SGIO v4 thinks it was a scsi
+        * status.  I hope this was all worth it.
+        */
+
+       req->special = job;
+       job->shost = shost;
+       job->rport = rport;
+       job->req = req;
+       if (i->f->dd_bsg_size)
+               job->dd_data = (void *)&job[1];
+       spin_lock_init(&job->job_lock);
+       job->request = (struct fc_bsg_request *)req->cmd;
+       job->request_len = req->cmd_len;
+       job->reply = req->sense;
+       job->reply_len = SCSI_SENSE_BUFFERSIZE; /* Size of sense buffer
+                                                * allocated */
+       if (req->bio) {
+               ret = fc_bsg_map_buffer(&job->request_payload, req);
+               if (ret)
+                       goto failjob_rls_job;
+       }
+       if (rsp && rsp->bio) {
+               ret = fc_bsg_map_buffer(&job->reply_payload, rsp);
+               if (ret)
+                       goto failjob_rls_rqst_payload;
+       }
+       job->job_done = fc_bsg_jobdone;
+       if (rport)
+               job->dev = &rport->dev;
+       else
+               job->dev = &shost->shost_gendev;
+       get_device(job->dev);           /* take a reference for the request */
+
+       job->ref_cnt = 1;
+
+       return 0;
+
+
+failjob_rls_rqst_payload:
+       kfree(job->request_payload.sg_list);
+failjob_rls_job:
+       kfree(job);
+       return -ENOMEM;
+}
+
+
+enum fc_dispatch_result {
+       FC_DISPATCH_BREAK,      /* on return, q is locked, break from q loop */
+       FC_DISPATCH_LOCKED,     /* on return, q is locked, continue on */
+       FC_DISPATCH_UNLOCKED,   /* on return, q is unlocked, continue on */
+};
+
+
+/**
+ * fc_bsg_host_dispatch - process fc host bsg requests and dispatch to LLDD
+ * @q:         fc host request queue
+ * @shost:     scsi host rport attached to
+ * @job:       bsg job to be processed
+ */
+static enum fc_dispatch_result
+fc_bsg_host_dispatch(struct request_queue *q, struct Scsi_Host *shost,
+                        struct fc_bsg_job *job)
+{
+       struct fc_internal *i = to_fc_internal(shost->transportt);
+       int cmdlen = sizeof(uint32_t);  /* start with length of msgcode */
+       int ret;
+
+       /* Validate the host command */
+       switch (job->request->msgcode) {
+       case FC_BSG_HST_ADD_RPORT:
+               cmdlen += sizeof(struct fc_bsg_host_add_rport);
+               break;
+
+       case FC_BSG_HST_DEL_RPORT:
+               cmdlen += sizeof(struct fc_bsg_host_del_rport);
+               break;
+
+       case FC_BSG_HST_ELS_NOLOGIN:
+               cmdlen += sizeof(struct fc_bsg_host_els);
+               /* there better be a xmt and rcv payloads */
+               if ((!job->request_payload.payload_len) ||
+                   (!job->reply_payload.payload_len)) {
+                       ret = -EINVAL;
+                       goto fail_host_msg;
+               }
+               break;
+
+       case FC_BSG_HST_CT:
+               cmdlen += sizeof(struct fc_bsg_host_ct);
+               /* there better be xmt and rcv payloads */
+               if ((!job->request_payload.payload_len) ||
+                   (!job->reply_payload.payload_len)) {
+                       ret = -EINVAL;
+                       goto fail_host_msg;
+               }
+               break;
+
+       case FC_BSG_HST_VENDOR:
+               cmdlen += sizeof(struct fc_bsg_host_vendor);
+               if ((shost->hostt->vendor_id == 0L) ||
+                   (job->request->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->f->bsg_request(job);
+       if (!ret)
+               return FC_DISPATCH_UNLOCKED;
+
+fail_host_msg:
+       /* return the errno failure code as the only status */
+       BUG_ON(job->reply_len < sizeof(uint32_t));
+       job->reply->reply_payload_rcv_len = 0;
+       job->reply->result = ret;
+       job->reply_len = sizeof(uint32_t);
+       fc_bsg_jobdone(job);
+       return FC_DISPATCH_UNLOCKED;
+}
+
+
+/*
+ * fc_bsg_goose_queue - restart rport queue in case it was stopped
+ * @rport:     rport to be restarted
+ */
+static void
+fc_bsg_goose_queue(struct fc_rport *rport)
+{
+       int flagset;
+       unsigned long flags;
+
+       if (!rport->rqst_q)
+               return;
+
+       get_device(&rport->dev);
+
+       spin_lock_irqsave(rport->rqst_q->queue_lock, flags);
+       flagset = test_bit(QUEUE_FLAG_REENTER, &rport->rqst_q->queue_flags) &&
+                 !test_bit(QUEUE_FLAG_REENTER, &rport->rqst_q->queue_flags);
+       if (flagset)
+               queue_flag_set(QUEUE_FLAG_REENTER, rport->rqst_q);
+       __blk_run_queue(rport->rqst_q);
+       if (flagset)
+               queue_flag_clear(QUEUE_FLAG_REENTER, rport->rqst_q);
+       spin_unlock_irqrestore(rport->rqst_q->queue_lock, flags);
+
+       put_device(&rport->dev);
+}
+
+
+/**
+ * fc_bsg_rport_dispatch - process rport bsg requests and dispatch to LLDD
+ * @q:         rport request queue
+ * @shost:     scsi host rport attached to
+ * @rport:     rport request destined to
+ * @job:       bsg job to be processed
+ */
+static enum fc_dispatch_result
+fc_bsg_rport_dispatch(struct request_queue *q, struct Scsi_Host *shost,
+                        struct fc_rport *rport, struct fc_bsg_job *job)
+{
+       struct fc_internal *i = to_fc_internal(shost->transportt);
+       int cmdlen = sizeof(uint32_t);  /* start with length of msgcode */
+       int ret;
+
+       /* Validate the rport command */
+       switch (job->request->msgcode) {
+       case FC_BSG_RPT_ELS:
+               cmdlen += sizeof(struct fc_bsg_rport_els);
+               goto check_bidi;
+
+       case FC_BSG_RPT_CT:
+               cmdlen += sizeof(struct fc_bsg_rport_ct);
+check_bidi:
+               /* there better be xmt and rcv payloads */
+               if ((!job->request_payload.payload_len) ||
+                   (!job->reply_payload.payload_len)) {
+                       ret = -EINVAL;
+                       goto fail_rport_msg;
+               }
+               break;
+       default:
+               ret = -EBADR;
+               goto fail_rport_msg;
+       }
+
+       /* check if we really have all the request data needed */
+       if (job->request_len < cmdlen) {
+               ret = -ENOMSG;
+               goto fail_rport_msg;
+       }
+
+       ret = i->f->bsg_request(job);
+       if (!ret)
+               return FC_DISPATCH_UNLOCKED;
+
+fail_rport_msg:
+       /* return the errno failure code as the only status */
+       BUG_ON(job->reply_len < sizeof(uint32_t));
+       job->reply->reply_payload_rcv_len = 0;
+       job->reply->result = ret;
+       job->reply_len = sizeof(uint32_t);
+       fc_bsg_jobdone(job);
+       return FC_DISPATCH_UNLOCKED;
+}
+
+
+/**
+ * fc_bsg_request_handler - generic handler for bsg requests
+ * @q:         request queue to manage
+ * @shost:     Scsi_Host related to the bsg object
+ * @rport:     FC remote port related to the bsg object (optional)
+ * @dev:       device structure for bsg object
+ */
+static void
+fc_bsg_request_handler(struct request_queue *q, struct Scsi_Host *shost,
+                      struct fc_rport *rport, struct device *dev)
+{
+       struct request *req;
+       struct fc_bsg_job *job;
+       enum fc_dispatch_result ret;
+
+       if (!get_device(dev))
+               return;
+
+       while (!blk_queue_plugged(q)) {
+               if (rport && (rport->port_state == FC_PORTSTATE_BLOCKED) &&
+                   !(rport->flags & FC_RPORT_FAST_FAIL_TIMEDOUT))
+                       break;
+
+               req = blk_fetch_request(q);
+               if (!req)
+                       break;
+
+               if (rport && (rport->port_state != FC_PORTSTATE_ONLINE)) {
+                       req->errors = -ENXIO;
+                       spin_unlock_irq(q->queue_lock);
+                       blk_end_request_all(req, -ENXIO);
+                       spin_lock_irq(q->queue_lock);
+                       continue;
+               }
+
+               spin_unlock_irq(q->queue_lock);
+
+               ret = fc_req_to_bsgjob(shost, rport, req);
+               if (ret) {
+                       req->errors = ret;
+                       blk_end_request_all(req, ret);
+                       spin_lock_irq(q->queue_lock);
+                       continue;
+               }
+
+               job = req->special;
+
+               /* check if we have the msgcode value at least */
+               if (job->request_len < sizeof(uint32_t)) {
+                       BUG_ON(job->reply_len < sizeof(uint32_t));
+                       job->reply->reply_payload_rcv_len = 0;
+                       job->reply->result = -ENOMSG;
+                       job->reply_len = sizeof(uint32_t);
+                       fc_bsg_jobdone(job);
+                       spin_lock_irq(q->queue_lock);
+                       continue;
+               }
+
+               /* the dispatch routines will unlock the queue_lock */
+               if (rport)
+                       ret = fc_bsg_rport_dispatch(q, shost, rport, job);
+               else
+                       ret = fc_bsg_host_dispatch(q, shost, job);
+
+               /* did dispatcher hit state that can't process any more */
+               if (ret == FC_DISPATCH_BREAK)
+                       break;
+
+               /* did dispatcher had released the lock */
+               if (ret == FC_DISPATCH_UNLOCKED)
+                       spin_lock_irq(q->queue_lock);
+       }
+
+       spin_unlock_irq(q->queue_lock);
+       put_device(dev);
+       spin_lock_irq(q->queue_lock);
+}
+
+
+/**
+ * fc_bsg_host_handler - handler for bsg requests for a fc host
+ * @q:         fc host request queue
+ */
+static void
+fc_bsg_host_handler(struct request_queue *q)
+{
+       struct Scsi_Host *shost = q->queuedata;
+
+       fc_bsg_request_handler(q, shost, NULL, &shost->shost_gendev);
+}
+
+
+/**
+ * fc_bsg_rport_handler - handler for bsg requests for a fc rport
+ * @q:         rport request queue
+ */
+static void
+fc_bsg_rport_handler(struct request_queue *q)
+{
+       struct fc_rport *rport = q->queuedata;
+       struct Scsi_Host *shost = rport_to_shost(rport);
+
+       fc_bsg_request_handler(q, shost, rport, &rport->dev);
+}
+
+
+/**
+ * fc_bsg_hostadd - Create and add the bsg hooks so we can receive requests
+ * @shost:     shost for fc_host
+ * @fc_host:   fc_host adding the structures to
+ */
+static int
+fc_bsg_hostadd(struct Scsi_Host *shost, struct fc_host_attrs *fc_host)
+{
+       struct device *dev = &shost->shost_gendev;
+       struct fc_internal *i = to_fc_internal(shost->transportt);
+       struct request_queue *q;
+       int err;
+       char bsg_name[20];
+
+       fc_host->rqst_q = NULL;
+
+       if (!i->f->bsg_request)
+               return -ENOTSUPP;
+
+       snprintf(bsg_name, sizeof(bsg_name),
+                "fc_host%d", shost->host_no);
+
+       q = __scsi_alloc_queue(shost, fc_bsg_host_handler);
+       if (!q) {
+               printk(KERN_ERR "fc_host%d: bsg interface failed to "
+                               "initialize - no request queue\n",
+                                shost->host_no);
+               return -ENOMEM;
+       }
+
+       q->queuedata = shost;
+       queue_flag_set_unlocked(QUEUE_FLAG_BIDI, q);
+       blk_queue_softirq_done(q, fc_bsg_softirq_done);
+       blk_queue_rq_timed_out(q, fc_bsg_job_timeout);
+       blk_queue_rq_timeout(q, FC_DEFAULT_BSG_TIMEOUT);
+
+       err = bsg_register_queue(q, dev, bsg_name, NULL);
+       if (err) {
+               printk(KERN_ERR "fc_host%d: bsg interface failed to "
+                               "initialize - register queue\n",
+                               shost->host_no);
+               blk_cleanup_queue(q);
+               return err;
+       }
+
+       fc_host->rqst_q = q;
+       return 0;
+}
+
+
+/**
+ * fc_bsg_rportadd - Create and add the bsg hooks so we can receive requests
+ * @shost:     shost that rport is attached to
+ * @rport:     rport that the bsg hooks are being attached to
+ */
+static int
+fc_bsg_rportadd(struct Scsi_Host *shost, struct fc_rport *rport)
+{
+       struct device *dev = &rport->dev;
+       struct fc_internal *i = to_fc_internal(shost->transportt);
+       struct request_queue *q;
+       int err;
+
+       rport->rqst_q = NULL;
+
+       if (!i->f->bsg_request)
+               return -ENOTSUPP;
+
+       q = __scsi_alloc_queue(shost, fc_bsg_rport_handler);
+       if (!q) {
+               printk(KERN_ERR "%s: bsg interface failed to "
+                               "initialize - no request queue\n",
+                                dev->kobj.name);
+               return -ENOMEM;
+       }
+
+       q->queuedata = rport;
+       queue_flag_set_unlocked(QUEUE_FLAG_BIDI, q);
+       blk_queue_softirq_done(q, fc_bsg_softirq_done);
+       blk_queue_rq_timed_out(q, fc_bsg_job_timeout);
+       blk_queue_rq_timeout(q, BLK_DEFAULT_SG_TIMEOUT);
+
+       err = bsg_register_queue(q, dev, NULL, NULL);
+       if (err) {
+               printk(KERN_ERR "%s: bsg interface failed to "
+                               "initialize - register queue\n",
+                                dev->kobj.name);
+               blk_cleanup_queue(q);
+               return err;
+       }
+
+       rport->rqst_q = q;
+       return 0;
+}
+
+
+/**
+ * fc_bsg_remove - Deletes the bsg hooks on fchosts/rports
+ * @q: the request_queue that is to be torn down.
+ */
+static void
+fc_bsg_remove(struct request_queue *q)
+{
+       if (q) {
+               bsg_unregister_queue(q);
+               blk_cleanup_queue(q);
+       }
+}
+
+
 /* Original Author:  Martin Hicks */
 MODULE_AUTHOR("James Smart");
 MODULE_DESCRIPTION("FC Transport Attributes");