Some hardware handler might prefer to queue the commands to
the controller so as not to flood the controller with commands.
This patch implements a generic workqueue framework for
hardware handler and converts dm-mpath-rdac to use it.

Signed-off-by: Hannes Reinecke <[EMAIL PROTECTED]>
---
 drivers/md/dm-hw-handler.c |   88 ++++++++++++++++++++++++++++++++++++++-
 drivers/md/dm-hw-handler.h |   14 ++++++
 drivers/md/dm-mpath-rdac.c |   99 ++++++++++++++++++++++----------------------
 drivers/md/dm-mpath.c      |    7 ++-
 4 files changed, 153 insertions(+), 55 deletions(-)

diff --git a/drivers/md/dm-hw-handler.c b/drivers/md/dm-hw-handler.c
index 2ee84d8..1d02c6a 100644
--- a/drivers/md/dm-hw-handler.c
+++ b/drivers/md/dm-hw-handler.c
@@ -11,10 +11,14 @@
 
 #include <linux/slab.h>
 
+#define DM_MSG_PREFIX "multipath hwh"
+
 struct hwh_internal {
        struct hw_handler_type hwht;
 
        struct list_head list;
+       struct workqueue_struct *workq;
+       char *workq_name;
        long use;
 };
 
@@ -99,6 +103,24 @@ static struct hwh_internal *_alloc_hw_handler(struct 
hw_handler_type *hwht)
        return hwhi;
 }
 
+static int _alloc_hw_workq(struct hwh_internal *hwhi,
+                         struct hw_handler_type *hwht)
+{
+       int r = 0;
+
+       hwhi->workq_name = kzalloc(strlen(hwht->name) + 6, GFP_KERNEL);
+       if (!hwhi->workq_name)
+               return -ENOMEM;
+
+       sprintf(hwhi->workq_name, "%s_wkqd", hwht->name);
+       hwhi->workq = create_singlethread_workqueue(hwhi->workq_name);
+
+       if (!hwhi->workq)
+               r = -EEXIST;
+
+       return r;
+}
+
 int dm_register_hw_handler(struct hw_handler_type *hwht)
 {
        int r = 0;
@@ -112,9 +134,13 @@ int dm_register_hw_handler(struct hw_handler_type *hwht)
        if (__find_hw_handler_type(hwht->name)) {
                kfree(hwhi);
                r = -EEXIST;
-       } else
-               list_add(&hwhi->list, &_hw_handlers);
+       } else {
+               if (hwht->workq_fn)
+                       r = _alloc_hw_workq(hwhi, hwht);
 
+               if (!r)
+                       list_add(&hwhi->list, &_hw_handlers);
+       }
        up_write(&_hwh_lock);
 
        return r;
@@ -141,11 +167,68 @@ int dm_unregister_hw_handler(struct hw_handler_type *hwht)
 
        up_write(&_hwh_lock);
 
+       if (hwhi->workq) {
+               destroy_workqueue(hwhi->workq);
+               kfree(hwhi->workq_name);
+       }
+
        kfree(hwhi);
 
        return 0;
 }
 
+static void dm_service_hw_workq(struct work_struct *work)
+{
+       struct hw_handler *hwh = container_of(work, struct hw_handler, work);
+       struct hw_handler_type *hwht = hwh->type;
+       struct hwh_internal *hwhi = container_of(hwht, struct hwh_internal, 
hwht);
+
+       if (hwht->workq_fn)
+               hwht->workq_fn(hwh);
+
+}
+
+int dm_create_hw_handler(struct hw_handler *hwh, unsigned int argc,
+                        char **argv)
+{
+       struct hw_handler_type *hwht = hwh->type;
+       int r;
+
+       r = hwht->create(hwh, argc, argv);
+       if (r)
+               return r;
+
+       if (hwht->workq_fn) {
+               INIT_WORK(&hwh->work, dm_service_hw_workq);
+       }
+
+       return 0;
+}
+
+void dm_destroy_hw_handler(struct hw_handler *hwh)
+{
+       struct hw_handler_type *hwht = hwh->type;
+
+       hwht->destroy(hwh);
+}
+
+void dm_enqueue_hw_workq(struct hw_handler *hwh)
+{
+       struct hw_handler_type *hwht = hwh->type;
+       struct hwh_internal *hwhi = container_of(hwht, struct hwh_internal, 
hwht);
+
+       down_read(&_hwh_lock);
+       if (!hwhi->workq)
+               goto out;
+
+       DMWARN("submit %s request", hwh->type->name);
+       queue_work(hwhi->workq, &hwh->work);
+
+ out:
+       up_read(&_hwh_lock);
+       return;
+}
+
 unsigned dm_scsi_err_handler(struct hw_handler *hwh, struct bio *bio)
 {
 #if 0
@@ -210,4 +293,5 @@ unsigned dm_scsi_err_handler(struct hw_handler *hwh, struct 
bio *bio)
 
 EXPORT_SYMBOL_GPL(dm_register_hw_handler);
 EXPORT_SYMBOL_GPL(dm_unregister_hw_handler);
+EXPORT_SYMBOL_GPL(dm_enqueue_hw_workq);
 EXPORT_SYMBOL_GPL(dm_scsi_err_handler);
diff --git a/drivers/md/dm-hw-handler.h b/drivers/md/dm-hw-handler.h
index 46809dc..9216682 100644
--- a/drivers/md/dm-hw-handler.h
+++ b/drivers/md/dm-hw-handler.h
@@ -14,9 +14,12 @@
 #include "dm-mpath.h"
 
 struct hw_handler_type;
+
 struct hw_handler {
+       struct list_head entry;
        struct hw_handler_type *type;
        struct mapped_device *md;
+       struct work_struct work;
        void *context;
 };
 
@@ -37,6 +40,7 @@ struct hw_handler_type {
        unsigned (*error) (struct hw_handler *hwh, struct bio *bio);
        int (*status) (struct hw_handler *hwh, status_type_t type,
                       char *result, unsigned int maxlen);
+       void (*workq_fn) (struct hw_handler *hwh);
 };
 
 /* Register a hardware handler */
@@ -51,6 +55,16 @@ struct hw_handler_type *dm_get_hw_handler(const char *name);
 /* Releases a hardware handler  */
 void dm_put_hw_handler(struct hw_handler_type *hwht);
 
+/* Creates a hardware handler */
+int dm_create_hw_handler(struct hw_handler *handler, unsigned int argc,
+                        char **argv);
+
+/* Destroys a hardware handler */
+void dm_destroy_hw_handler(struct hw_handler *handler);
+
+/* Enqueue an element to the workqueue */
+void dm_enqueue_hw_workq(struct hw_handler *hwh);
+
 /* Default err function */
 unsigned dm_scsi_err_handler(struct hw_handler *hwh, struct bio *bio);
 
diff --git a/drivers/md/dm-mpath-rdac.c b/drivers/md/dm-mpath-rdac.c
index e04eb5c..99f755c 100644
--- a/drivers/md/dm-mpath-rdac.c
+++ b/drivers/md/dm-mpath-rdac.c
@@ -176,14 +176,12 @@ struct c2_inquiry {
 };
 
 struct rdac_handler {
-       struct list_head        entry; /* list waiting to submit MODE SELECT */
        unsigned                timeout;
        struct rdac_controller  *ctlr;
 #define UNINITIALIZED_LUN      (1 << 8)
        unsigned                lun;
        unsigned char           sense[SCSI_SENSE_BUFFERSIZE];
        struct dm_path          *path;
-       struct work_struct      work;
 #define        SEND_C2_INQUIRY         1
 #define        SEND_C4_INQUIRY         2
 #define        SEND_C8_INQUIRY         3
@@ -200,7 +198,6 @@ struct rdac_handler {
 
 static LIST_HEAD(ctlr_list);
 static DEFINE_SPINLOCK(list_lock);
-static struct workqueue_struct *rdac_wkqd;
 
 static inline int had_failures(struct request *req, int error)
 {
@@ -208,15 +205,16 @@ static inline int had_failures(struct request *req, int 
error)
                        msg_byte(req->errors) != COMMAND_COMPLETE);
 }
 
-static void rdac_resubmit_all(struct rdac_handler *h)
+static void rdac_resubmit_all(struct rdac_controller *ctlr)
 {
-       struct rdac_controller *ctlr = h->ctlr;
-       struct rdac_handler *tmp, *h1;
+       struct rdac_handler *h;
+       struct hw_handler *tmp, *h1;
 
        spin_lock(&ctlr->lock);
        list_for_each_entry_safe(h1, tmp, &ctlr->cmd_list, entry) {
-               h1->cmd_to_send = SEND_C9_INQUIRY;
-               queue_work(rdac_wkqd, &h1->work);
+               h = h1->context;
+               h->cmd_to_send = SEND_C9_INQUIRY;
+               dm_enqueue_hw_workq(h1);
                list_del(&h1->entry);
        }
        ctlr->submitted = 0;
@@ -225,7 +223,8 @@ static void rdac_resubmit_all(struct rdac_handler *h)
 
 static void mode_select_endio(struct request *req, int error)
 {
-       struct rdac_handler *h = req->end_io_data;
+       struct hw_handler *hwh = req->end_io_data;
+       struct rdac_handler *h = hwh->context;
        struct scsi_sense_hdr sense_hdr;
        int sense = 0, fail = 0;
 
@@ -247,13 +246,13 @@ static void mode_select_endio(struct request *req, int 
error)
                         * 0x62900    - Power On, Reset, or Bus Device Reset
                         */
                        h->cmd_to_send = SEND_C9_INQUIRY;
-                       queue_work(rdac_wkqd, &h->work);
+                       dm_enqueue_hw_workq(hwh);
                        goto done;
                }
                if (sense)
                        DMINFO("MODE_SELECT failed on %s with sense 0x%x",
                                                h->path->dev->name, sense);
-       }
+       }
 failed:
        if (fail || sense)
                dm_pg_init_complete(h->path, MP_FAIL_PATH);
@@ -261,13 +260,14 @@ failed:
                dm_pg_init_complete(h->path, 0);
 
 done:
-       rdac_resubmit_all(h);
+       rdac_resubmit_all(h->ctlr);
        __blk_put_request(req->q, req);
 }
 
-static struct request *get_rdac_req(struct rdac_handler *h,
+static struct request *get_rdac_req(struct hw_handler *hwh,
                        void *buffer, unsigned buflen, int rw)
 {
+       struct rdac_handler *h = hwh->context;
        struct request *rq;
        struct request_queue *q = bdev_get_queue(h->path->dev->bdev);
 
@@ -284,20 +284,21 @@ static struct request *get_rdac_req(struct rdac_handler 
*h,
                return NULL;
        }
 
-       memset(&rq->cmd, 0, BLK_MAX_CDB);
+       memset(&rq->cmd, 0, BLK_MAX_CDB);
        rq->sense = h->sense;
        memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
        rq->sense_len = 0;
 
-       rq->end_io_data = h;
+       rq->end_io_data = hwh;
        rq->timeout = h->timeout;
        rq->cmd_type = REQ_TYPE_BLOCK_PC;
        rq->cmd_flags |= REQ_FAILFAST | REQ_NOMERGE;
        return rq;
 }
 
-static struct request *rdac_failover_get(struct rdac_handler *h)
+static struct request *rdac_failover_get(struct hw_handler *hwh)
 {
+       struct rdac_handler *h = hwh->context;
        struct request *rq;
        struct rdac_mode_common *common;
        unsigned data_size;
@@ -330,7 +331,7 @@ static struct request *rdac_failover_get(struct 
rdac_handler *h)
        common->rdac_options = RDAC_FORCED_QUIESENCE;
 
        /* get request for block layer packet command */
-       rq = get_rdac_req(h, &h->ctlr->mode_select, data_size, WRITE);
+       rq = get_rdac_req(hwh, &h->ctlr->mode_select, data_size, WRITE);
        if (!rq) {
                DMERR("rdac_failover_get: no rq");
                return NULL;
@@ -351,14 +352,15 @@ static struct request *rdac_failover_get(struct 
rdac_handler *h)
 }
 
 /* Acquires h->ctlr->lock */
-static void submit_mode_select(struct rdac_handler *h)
+static void submit_mode_select(struct hw_handler *hwh)
 {
+       struct rdac_handler *h = hwh->context;
        struct request *rq;
        struct request_queue *q = bdev_get_queue(h->path->dev->bdev);
 
        spin_lock(&h->ctlr->lock);
        if (h->ctlr->submitted) {
-               list_add(&h->entry, &h->ctlr->cmd_list);
+               list_add(&hwh->entry, &h->ctlr->cmd_list);
                goto drop_lock;
        }
 
@@ -367,13 +369,13 @@ static void submit_mode_select(struct rdac_handler *h)
                goto fail_path;
        }
 
-       rq = rdac_failover_get(h);
+       rq = rdac_failover_get(hwh);
        if (!rq) {
                DMERR("submit_mode_select: no rq");
                goto fail_path;
        }
 
-       DMINFO("queueing MODE_SELECT command on %s", h->path->dev->name);
+       DMINFO("submit MODE_SELECT command on %s", h->path->dev->name);
 
        blk_execute_rq_nowait(q, NULL, rq, 1, mode_select_endio);
        h->ctlr->submitted = 1;
@@ -429,7 +431,8 @@ done:
 
 static void c4_endio(struct request *req, int error)
 {
-       struct rdac_handler *h = req->end_io_data;
+       struct hw_handler *hwh = req->end_io_data;
+       struct rdac_handler *h = hwh->context;
        struct c4_inquiry *sp;
 
        if (had_failures(req, error)) {
@@ -443,7 +446,7 @@ static void c4_endio(struct request *req, int error)
 
        if (h->ctlr) {
                h->cmd_to_send = SEND_C9_INQUIRY;
-               queue_work(rdac_wkqd, &h->work);
+               dm_enqueue_hw_workq(hwh);
        } else
                dm_pg_init_complete(h->path, MP_FAIL_PATH);
 done:
@@ -452,7 +455,8 @@ done:
 
 static void c2_endio(struct request *req, int error)
 {
-       struct rdac_handler *h = req->end_io_data;
+       struct hw_handler *hwh = req->end_io_data;
+       struct rdac_handler *h = hwh->context;
        struct c2_inquiry *sp;
 
        if (had_failures(req, error)) {
@@ -469,14 +473,15 @@ static void c2_endio(struct request *req, int error)
                h->ctlr->use_10_ms = 0;
 
        h->cmd_to_send = SEND_MODE_SELECT;
-       queue_work(rdac_wkqd, &h->work);
+       dm_enqueue_hw_workq(hwh);
 done:
        __blk_put_request(req->q, req);
 }
 
 static void c9_endio(struct request *req, int error)
 {
-       struct rdac_handler *h = req->end_io_data;
+       struct hw_handler *hwh = req->end_io_data;
+       struct rdac_handler *h = hwh->context;
        struct c9_inquiry *sp;
 
        if (had_failures(req, error)) {
@@ -510,14 +515,15 @@ static void c9_endio(struct request *req, int error)
                        h->cmd_to_send = SEND_MODE_SELECT;
        } else
                h->cmd_to_send = SEND_C4_INQUIRY;
-       queue_work(rdac_wkqd, &h->work);
+       dm_enqueue_hw_workq(hwh);
 done:
        __blk_put_request(req->q, req);
 }
 
 static void c8_endio(struct request *req, int error)
 {
-       struct rdac_handler *h = req->end_io_data;
+       struct hw_handler *hwh = req->end_io_data;
+       struct rdac_handler *h = hwh->context;
        struct c8_inquiry *sp;
 
        if (had_failures(req, error)) {
@@ -531,21 +537,22 @@ static void c8_endio(struct request *req, int error)
        sp = &h->inq.c8;
        h->lun = sp->lun[7]; /* currently it uses only one byte */
        h->cmd_to_send = SEND_C9_INQUIRY;
-       queue_work(rdac_wkqd, &h->work);
+       dm_enqueue_hw_workq(hwh);
 done:
        __blk_put_request(req->q, req);
 }
 
-static void submit_inquiry(struct rdac_handler *h, int page_code,
+static void submit_inquiry(struct hw_handler *hwh, int page_code,
                unsigned int len, rq_end_io_fn endio)
 {
+       struct rdac_handler *h = hwh->context;
        struct request *rq;
        struct request_queue *q = bdev_get_queue(h->path->dev->bdev);
 
        if (!q)
                goto fail_path;
 
-       rq = get_rdac_req(h, &h->inq, len, READ);
+       rq = get_rdac_req(hwh, &h->inq, len, READ);
        if (!rq)
                goto fail_path;
 
@@ -562,25 +569,25 @@ fail_path:
        dm_pg_init_complete(h->path, MP_FAIL_PATH);
 }
 
-static void service_wkq(struct work_struct *work)
+static void rdac_service_wkq(struct hw_handler *hwh)
 {
-       struct rdac_handler *h = container_of(work, struct rdac_handler, work);
+       struct rdac_handler *h = hwh->context;
 
        switch (h->cmd_to_send) {
        case SEND_C2_INQUIRY:
-               submit_inquiry(h, 0xC2, sizeof(struct c2_inquiry), c2_endio);
+               submit_inquiry(hwh, 0xC2, sizeof(struct c2_inquiry), c2_endio);
                break;
        case SEND_C4_INQUIRY:
-               submit_inquiry(h, 0xC4, sizeof(struct c4_inquiry), c4_endio);
+               submit_inquiry(hwh, 0xC4, sizeof(struct c4_inquiry), c4_endio);
                break;
        case SEND_C8_INQUIRY:
-               submit_inquiry(h, 0xC8, sizeof(struct c8_inquiry), c8_endio);
+               submit_inquiry(hwh, 0xC8, sizeof(struct c8_inquiry), c8_endio);
                break;
        case SEND_C9_INQUIRY:
-               submit_inquiry(h, 0xC9, sizeof(struct c9_inquiry), c9_endio);
+               submit_inquiry(hwh, 0xC9, sizeof(struct c9_inquiry), c9_endio);
                break;
        case SEND_MODE_SELECT:
-               submit_mode_select(h);
+               submit_mode_select(hwh);
                break;
        default:
                BUG();
@@ -603,7 +610,7 @@ static int rdac_create(struct hw_handler *hwh, unsigned 
argc, char **argv)
                DMWARN("incorrect number of arguments");
                return -EINVAL;
        } else {
-               if (sscanf(argv[1], "%u", &timeout) != 1) {
+               if (sscanf(argv[0], "%u", &timeout) != 1) {
                        DMWARN("invalid timeout value");
                        return -EINVAL;
                }
@@ -616,7 +623,6 @@ static int rdac_create(struct hw_handler *hwh, unsigned 
argc, char **argv)
        hwh->context = h;
        h->timeout = timeout;
        h->lun = UNINITIALIZED_LUN;
-       INIT_WORK(&h->work, service_wkq);
        DMWARN("using RDAC command with timeout %u", h->timeout);
 
        return 0;
@@ -646,10 +652,10 @@ static void rdac_pg_init(struct hw_handler *hwh, unsigned 
bypassed,
        h->path = path;
        switch (h->lun) {
        case UNINITIALIZED_LUN:
-               submit_inquiry(h, 0xC8, sizeof(struct c8_inquiry), c8_endio);
+               submit_inquiry(hwh, 0xC8, sizeof(struct c8_inquiry), c8_endio);
                break;
        default:
-               submit_inquiry(h, 0xC9, sizeof(struct c9_inquiry), c9_endio);
+               submit_inquiry(hwh, 0xC9, sizeof(struct c9_inquiry), c9_endio);
        }
 }
 
@@ -660,22 +666,16 @@ static struct hw_handler_type rdac_handler = {
        .destroy = rdac_destroy,
        .pg_init = rdac_pg_init,
        .error = rdac_error,
+       .workq_fn = rdac_service_wkq,
 };
 
 static int __init rdac_init(void)
 {
        int r;
 
-       rdac_wkqd = create_singlethread_workqueue("rdac_wkqd");
-       if (!rdac_wkqd) {
-               DMERR("Failed to create workqueue rdac_wkqd.");
-               return -ENOMEM;
-       }
-
        r = dm_register_hw_handler(&rdac_handler);
        if (r < 0) {
                DMERR("%s: register failed %d", RDAC_DM_HWH_NAME, r);
-               destroy_workqueue(rdac_wkqd);
                return r;
        }
 
@@ -687,7 +687,6 @@ static void __exit rdac_exit(void)
 {
        int r = dm_unregister_hw_handler(&rdac_handler);
 
-       destroy_workqueue(rdac_wkqd);
        if (r < 0)
                DMERR("%s: unregister failed %d", RDAC_DM_HWH_NAME, r);
 }
diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c
index 24b2b1e..65a52b7 100644
--- a/drivers/md/dm-mpath.c
+++ b/drivers/md/dm-mpath.c
@@ -201,7 +201,7 @@ static void free_multipath(struct multipath *m)
        }
 
        if (hwh->type) {
-               hwh->type->destroy(hwh);
+               dm_destroy_hw_handler(hwh);
                dm_put_hw_handler(hwh->type);
        }
 
@@ -677,14 +677,15 @@ static int parse_hw_handler(struct arg_set *as, struct 
multipath *m)
        m->hw_handler.md = dm_table_get_md(ti->table);
        dm_put(m->hw_handler.md);
 
-       r = hwht->create(&m->hw_handler, hw_argc - 1, as->argv);
+       m->hw_handler.type = hwht;
+
+       r = dm_create_hw_handler(&m->hw_handler, hw_argc - 1, as->argv);
        if (r) {
                dm_put_hw_handler(hwht);
                ti->error = "hardware handler constructor failed";
                return r;
        }
 
-       m->hw_handler.type = hwht;
        consume(as, hw_argc - 1);
 
        return 0;
-- 
1.5.3.2

-
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to