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 <hare@xxxxxxx> --- 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 majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html