[dm-devel] [PATCH 3/3] multipath: implement controller framework for hardware handlers
Hannes Reinecke
hare at suse.de
Thu Nov 15 09:16:42 UTC 2007
Some hardware handler have a need to identify the controller
handling the paths, as the controller might only be able to
process one path-switching command in a row.
This patch implements a framework for selecting controllers
and allows for throttling (single-threading) commands to
this controller.
The RDAC hardware handler is converted to use this new
framework.
Signed-off-by: Hannes Reinecke <hare at suse.de>
---
drivers/md/dm-hw-handler.c | 119 ++++++++++++++++++++++++++++++-
drivers/md/dm-hw-handler.h | 29 ++++++++
drivers/md/dm-mpath-rdac.c | 171 ++++++++++++++++++--------------------------
3 files changed, 216 insertions(+), 103 deletions(-)
diff --git a/drivers/md/dm-hw-handler.c b/drivers/md/dm-hw-handler.c
index 1d02c6a..266bd00 100644
--- a/drivers/md/dm-hw-handler.c
+++ b/drivers/md/dm-hw-handler.c
@@ -27,6 +27,9 @@ struct hwh_internal {
static LIST_HEAD(_hw_handlers);
static DECLARE_RWSEM(_hwh_lock);
+static LIST_HEAD(_ctlr_list);
+static DEFINE_SPINLOCK(_ctlr_lock);
+
static struct hwh_internal *__find_hw_handler_type(const char *name)
{
struct hwh_internal *hwhi;
@@ -177,15 +180,45 @@ int dm_unregister_hw_handler(struct hw_handler_type *hwht)
return 0;
}
+static void _release_hw_controller(struct kref *kref)
+{
+ struct hw_controller *ctlr = container_of(kref, struct hw_controller, kref);
+
+ spin_lock(&_ctlr_lock);
+ list_del(&ctlr->node);
+ spin_unlock(&_ctlr_lock);
+ if (ctlr->type && ctlr->type->destroy)
+ ctlr->type->destroy(ctlr);
+
+ kfree(ctlr);
+}
+
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);
+ struct hw_controller *ctlr = hwh->ctlr;
if (hwht->workq_fn)
hwht->workq_fn(hwh);
+ if (ctlr && ctlr->submitted) {
+ /* Resubmit first queued request */
+ spin_lock(&ctlr->lock);
+
+ ctlr->submitted = 0;
+ if (!list_empty(&ctlr->cmd_list)) {
+ hwh = list_first_entry(&ctlr->cmd_list, struct hw_handler, entry);
+ list_del(&hwh->entry);
+ queue_work(hwhi->workq, &hwh->work);
+ ctlr->submitted = 1;
+ }
+ spin_unlock(&ctlr->lock);
+
+ /* Drop controller reference */
+ kref_put(&ctlr->kref, _release_hw_controller);
+ }
}
int dm_create_hw_handler(struct hw_handler *hwh, unsigned int argc,
@@ -200,6 +233,8 @@ int dm_create_hw_handler(struct hw_handler *hwh, unsigned int argc,
if (hwht->workq_fn) {
INIT_WORK(&hwh->work, dm_service_hw_workq);
+ hwh->ctlr = NULL;
+ dm_select_hw_controller(hwh, NULL, hwht->name);
}
return 0;
@@ -209,6 +244,9 @@ void dm_destroy_hw_handler(struct hw_handler *hwh)
{
struct hw_handler_type *hwht = hwh->type;
+ if (hwh->ctlr)
+ kref_put(&hwh->ctlr->kref, _release_hw_controller);
+
hwht->destroy(hwh);
}
@@ -221,14 +259,89 @@ void dm_enqueue_hw_workq(struct hw_handler *hwh)
if (!hwhi->workq)
goto out;
- DMWARN("submit %s request", hwh->type->name);
- queue_work(hwhi->workq, &hwh->work);
+ if (!hwh->ctlr) {
+ DMWARN("%s: no controller link", hwh->type->name);
+ queue_work(hwhi->workq, &hwh->work);
+ goto out;
+ }
+
+ spin_lock(&hwh->ctlr->lock);
+ kref_get(&hwh->ctlr->kref);
+ if (hwh->ctlr->submitted)
+ list_add(&hwh->entry, &hwh->ctlr->cmd_list);
+ else
+ queue_work(hwhi->workq, &hwh->work);
+
+ spin_unlock(&hwh->ctlr->lock);
out:
up_read(&_hwh_lock);
return;
}
+void dm_throttle_hw_controller(struct hw_handler *hwh)
+{
+ if (hwh->ctlr) {
+ spin_lock(&hwh->ctlr->lock);
+ hwh->ctlr->submitted = 1;
+ spin_unlock(&hwh->ctlr->lock);
+ }
+}
+
+int dm_select_hw_controller(struct hw_handler *hwh,
+ struct hw_controller_type *ctlr_type,
+ const char *ctlr_id)
+{
+ int retval = 0, ctlr_id_len = strlen(ctlr_id);
+ struct hw_controller *ctlr = NULL, *old_ctlr = NULL;
+
+ spin_lock(&_ctlr_lock);
+
+ list_for_each_entry(ctlr, &_ctlr_list, node) {
+ if ((ctlr_type && ctlr->type == ctlr_type) &&
+ !strcmp(ctlr->ctlr_id, ctlr_id)) {
+ kref_get(&ctlr->kref);
+ goto done;
+ }
+ }
+ ctlr = kzalloc(sizeof(*ctlr), GFP_NOIO);
+ if (!ctlr) {
+ ctlr = hwh->ctlr;
+ retval = -ENOMEM;
+ goto done;
+ }
+
+ if (ctlr_type && ctlr_type->create) {
+ retval = ctlr_type->create(ctlr);
+ if (retval) {
+ ctlr = hwh->ctlr;
+ goto done;
+ }
+ }
+
+ /* Save old controller instance */
+ old_ctlr = hwh->ctlr;
+
+ strncpy(ctlr->ctlr_id, ctlr_id, ctlr_id_len);
+ ctlr->ctlr_id[ctlr_id_len] = '\0';
+ kref_init(&ctlr->kref);
+ spin_lock_init(&ctlr->lock);
+ ctlr->submitted = 0;
+ ctlr->type = ctlr_type;
+ INIT_LIST_HEAD(&ctlr->cmd_list);
+ list_add(&ctlr->node, &_ctlr_list);
+
+ done:
+ hwh->ctlr = ctlr;
+ spin_unlock(&_ctlr_lock);
+
+ /* Release old controller; might take _ctlr_lock */
+ if (old_ctlr)
+ kref_put(&old_ctlr->kref, _release_hw_controller);
+
+ return retval;
+}
+
unsigned dm_scsi_err_handler(struct hw_handler *hwh, struct bio *bio)
{
#if 0
@@ -294,4 +407,6 @@ 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_select_hw_controller);
+EXPORT_SYMBOL_GPL(dm_throttle_hw_controller);
EXPORT_SYMBOL_GPL(dm_scsi_err_handler);
diff --git a/drivers/md/dm-hw-handler.h b/drivers/md/dm-hw-handler.h
index 9216682..f74e9d6 100644
--- a/drivers/md/dm-hw-handler.h
+++ b/drivers/md/dm-hw-handler.h
@@ -15,10 +15,31 @@
struct hw_handler_type;
+#define CTLR_ID_LEN 256
+
+struct hw_controller {
+ struct list_head node;
+ struct hw_controller_type *type;
+ unsigned char ctlr_id[CTLR_ID_LEN];
+ struct kref kref;
+ spinlock_t lock;
+ struct list_head cmd_list;
+ int submitted;
+ void *context;
+};
+
+struct hw_controller_type {
+ char *name;
+
+ int (*create) (struct hw_controller *ctlr);
+ void (*destroy) (struct hw_controller *ctlr);
+};
+
struct hw_handler {
struct list_head entry;
struct hw_handler_type *type;
struct mapped_device *md;
+ struct hw_controller *ctlr;
struct work_struct work;
void *context;
};
@@ -62,6 +83,14 @@ int dm_create_hw_handler(struct hw_handler *handler, unsigned int argc,
/* Destroys a hardware handler */
void dm_destroy_hw_handler(struct hw_handler *handler);
+/* Selects a controller instance */
+int dm_select_hw_controller(struct hw_handler *hwh,
+ struct hw_controller_type *ctlr_type,
+ const char *ctrl_id);
+
+/* Throttle command submission to the controller */
+void dm_throttle_hw_controller(struct hw_handler *hwh);
+
/* Enqueue an element to the workqueue */
void dm_enqueue_hw_workq(struct hw_handler *hwh);
diff --git a/drivers/md/dm-mpath-rdac.c b/drivers/md/dm-mpath-rdac.c
index 99f755c..8b6368d 100644
--- a/drivers/md/dm-mpath-rdac.c
+++ b/drivers/md/dm-mpath-rdac.c
@@ -131,14 +131,7 @@ struct c4_inquiry {
};
struct rdac_controller {
- u8 subsys_id[SUBSYS_ID_LEN];
- u8 slot_id[SLOT_ID_LEN];
int use_10_ms;
- struct kref kref;
- struct list_head node; /* list of all controllers */
- spinlock_t lock;
- int submitted;
- struct list_head cmd_list; /* list of commands to be submitted */
union {
struct rdac_pg_legacy legacy;
struct rdac_pg_expanded expanded;
@@ -177,7 +170,6 @@ struct c2_inquiry {
struct rdac_handler {
unsigned timeout;
- struct rdac_controller *ctlr;
#define UNINITIALIZED_LUN (1 << 8)
unsigned lun;
unsigned char sense[SCSI_SENSE_BUFFERSIZE];
@@ -196,31 +188,41 @@ struct rdac_handler {
} inq;
};
-static LIST_HEAD(ctlr_list);
-static DEFINE_SPINLOCK(list_lock);
-
static inline int had_failures(struct request *req, int error)
{
return (error || host_byte(req->errors) != DID_OK ||
msg_byte(req->errors) != COMMAND_COMPLETE);
}
-static void rdac_resubmit_all(struct rdac_controller *ctlr)
+static int rdac_controller_create(struct hw_controller *ctlr)
{
- struct rdac_handler *h;
- struct hw_handler *tmp, *h1;
-
- spin_lock(&ctlr->lock);
- list_for_each_entry_safe(h1, tmp, &ctlr->cmd_list, entry) {
- h = h1->context;
- h->cmd_to_send = SEND_C9_INQUIRY;
- dm_enqueue_hw_workq(h1);
- list_del(&h1->entry);
- }
- ctlr->submitted = 0;
- spin_unlock(&ctlr->lock);
+ struct rdac_controller *rdac_ctlr;
+
+ rdac_ctlr = kzalloc(sizeof(struct rdac_controller),
+ GFP_KERNEL);
+
+ if (!rdac_ctlr)
+ return -ENOMEM;
+
+ rdac_ctlr->use_10_ms = -1;
+ ctlr->context = rdac_ctlr;
+
+ return 0;
+}
+
+static void rdac_controller_destroy(struct hw_controller *ctlr)
+{
+ if (ctlr->context)
+ kfree(ctlr->context);
+ ctlr->context = NULL;
}
+static struct hw_controller_type rdac_controller = {
+ .name = "rdac",
+ .create = rdac_controller_create,
+ .destroy = rdac_controller_destroy,
+};
+
static void mode_select_endio(struct request *req, int error)
{
struct hw_handler *hwh = req->end_io_data;
@@ -260,7 +262,6 @@ failed:
dm_pg_init_complete(h->path, 0);
done:
- rdac_resubmit_all(h->ctlr);
__blk_put_request(req->q, req);
}
@@ -299,15 +300,16 @@ static struct request *get_rdac_req(struct hw_handler *hwh,
static struct request *rdac_failover_get(struct hw_handler *hwh)
{
struct rdac_handler *h = hwh->context;
+ struct rdac_controller *ctlr = hwh->ctlr->context;
struct request *rq;
struct rdac_mode_common *common;
unsigned data_size;
- if (h->ctlr->use_10_ms) {
+ if (ctlr->use_10_ms) {
struct rdac_pg_expanded *rdac_pg;
data_size = sizeof(struct rdac_pg_expanded);
- rdac_pg = &h->ctlr->mode_select.expanded;
+ rdac_pg = &ctlr->mode_select.expanded;
memset(rdac_pg, 0, data_size);
common = &rdac_pg->common;
rdac_pg->page_code = RDAC_PAGE_CODE_REDUNDANT_CONTROLLER + 0x40;
@@ -319,7 +321,7 @@ static struct request *rdac_failover_get(struct hw_handler *hwh)
struct rdac_pg_legacy *rdac_pg;
data_size = sizeof(struct rdac_pg_legacy);
- rdac_pg = &h->ctlr->mode_select.legacy;
+ rdac_pg = &ctlr->mode_select.legacy;
memset(rdac_pg, 0, data_size);
common = &rdac_pg->common;
rdac_pg->page_code = RDAC_PAGE_CODE_REDUNDANT_CONTROLLER;
@@ -331,14 +333,14 @@ static struct request *rdac_failover_get(struct hw_handler *hwh)
common->rdac_options = RDAC_FORCED_QUIESENCE;
/* get request for block layer packet command */
- rq = get_rdac_req(hwh, &h->ctlr->mode_select, data_size, WRITE);
+ rq = get_rdac_req(hwh, &ctlr->mode_select, data_size, WRITE);
if (!rq) {
DMERR("rdac_failover_get: no rq");
return NULL;
}
/* Prepare the command. */
- if (h->ctlr->use_10_ms) {
+ if (ctlr->use_10_ms) {
rq->cmd[0] = MODE_SELECT_10;
rq->cmd[7] = data_size >> 8;
rq->cmd[8] = data_size & 0xff;
@@ -351,19 +353,12 @@ static struct request *rdac_failover_get(struct hw_handler *hwh)
return rq;
}
-/* Acquires h->ctlr->lock */
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(&hwh->entry, &h->ctlr->cmd_list);
- goto drop_lock;
- }
-
if (!q) {
DMINFO("submit_mode_select: no queue");
goto fail_path;
@@ -378,55 +373,10 @@ static void submit_mode_select(struct hw_handler *hwh)
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;
- goto drop_lock;
+ dm_throttle_hw_controller(hwh);
+ return;
fail_path:
dm_pg_init_complete(h->path, MP_FAIL_PATH);
-drop_lock:
- spin_unlock(&h->ctlr->lock);
-}
-
-static void release_ctlr(struct kref *kref)
-{
- struct rdac_controller *ctlr;
- ctlr = container_of(kref, struct rdac_controller, kref);
-
- spin_lock(&list_lock);
- list_del(&ctlr->node);
- spin_unlock(&list_lock);
- kfree(ctlr);
-}
-
-static struct rdac_controller *get_controller(u8 *subsys_id, u8 *slot_id)
-{
- struct rdac_controller *ctlr, *tmp;
-
- spin_lock(&list_lock);
-
- list_for_each_entry(tmp, &ctlr_list, node) {
- if ((memcmp(tmp->subsys_id, subsys_id, SUBSYS_ID_LEN) == 0) &&
- (memcmp(tmp->slot_id, slot_id, SLOT_ID_LEN) == 0)) {
- kref_get(&tmp->kref);
- spin_unlock(&list_lock);
- return tmp;
- }
- }
- ctlr = kmalloc(sizeof(*ctlr), GFP_ATOMIC);
- if (!ctlr)
- goto done;
-
- /* initialize fields of controller */
- memcpy(ctlr->subsys_id, subsys_id, SUBSYS_ID_LEN);
- memcpy(ctlr->slot_id, slot_id, SLOT_ID_LEN);
- kref_init(&ctlr->kref);
- spin_lock_init(&ctlr->lock);
- ctlr->submitted = 0;
- ctlr->use_10_ms = -1;
- INIT_LIST_HEAD(&ctlr->cmd_list);
- list_add(&ctlr->node, &ctlr_list);
-done:
- spin_unlock(&list_lock);
- return ctlr;
}
static void c4_endio(struct request *req, int error)
@@ -434,6 +384,7 @@ static void c4_endio(struct request *req, int error)
struct hw_handler *hwh = req->end_io_data;
struct rdac_handler *h = hwh->context;
struct c4_inquiry *sp;
+ char ctlr_id[CTLR_ID_LEN];
if (had_failures(req, error)) {
dm_pg_init_complete(h->path, MP_FAIL_PATH);
@@ -441,14 +392,32 @@ static void c4_endio(struct request *req, int error)
}
sp = &h->inq.c4;
+ memset(ctlr_id, ' ', CTLR_ID_LEN);
+ if (!memcmp(sp->subsys_id, ctlr_id, SUBSYS_ID_LEN)) {
+ strncpy(sp->subsys_id, sp->revision, 4);
+ sp->subsys_id[4] = '\0';
+ }
- h->ctlr = get_controller(sp->subsys_id, sp->slot_id);
-
- if (h->ctlr) {
- h->cmd_to_send = SEND_C9_INQUIRY;
- dm_enqueue_hw_workq(hwh);
- } else
+ /* If the subsystem ID is empty use the revision */
+ memset(ctlr_id, ' ', CTLR_ID_LEN);
+ if (!memcmp(sp->subsys_id, ctlr_id, SUBSYS_ID_LEN)) {
+ strncpy(sp->subsys_id, sp->revision, 4);
+ sp->subsys_id[4] = '\0';
+ }
+ memset(ctlr_id, 0, CTLR_ID_LEN);
+ strncpy(ctlr_id, sp->subsys_id, SUBSYS_ID_LEN);
+ strcat(ctlr_id,":");
+ strncat(ctlr_id, sp->slot_id, 4);
+
+ if (dm_select_hw_controller(hwh, &rdac_controller, ctlr_id)) {
+ DMINFO("could not select controller on %s",
+ h->path->dev->name);
dm_pg_init_complete(h->path, MP_FAIL_PATH);
+ goto done;
+ }
+ h->cmd_to_send = SEND_C9_INQUIRY;
+ dm_enqueue_hw_workq(hwh);
+
done:
__blk_put_request(req->q, req);
}
@@ -457,6 +426,7 @@ static void c2_endio(struct request *req, int error)
{
struct hw_handler *hwh = req->end_io_data;
struct rdac_handler *h = hwh->context;
+ struct rdac_controller *ctlr = hwh->ctlr->context;
struct c2_inquiry *sp;
if (had_failures(req, error)) {
@@ -468,9 +438,9 @@ static void c2_endio(struct request *req, int error)
/* If more than MODE6_MAX_LUN luns are supported, use mode select 10 */
if (sp->max_lun_supported >= MODE6_MAX_LUN)
- h->ctlr->use_10_ms = 1;
+ ctlr->use_10_ms = 1;
else
- h->ctlr->use_10_ms = 0;
+ ctlr->use_10_ms = 0;
h->cmd_to_send = SEND_MODE_SELECT;
dm_enqueue_hw_workq(hwh);
@@ -482,6 +452,7 @@ static void c9_endio(struct request *req, int error)
{
struct hw_handler *hwh = req->end_io_data;
struct rdac_handler *h = hwh->context;
+ struct rdac_controller *ctlr = hwh->ctlr?hwh->ctlr->context:NULL;
struct c9_inquiry *sp;
if (had_failures(req, error)) {
@@ -502,14 +473,14 @@ static void c9_endio(struct request *req, int error)
goto done;
}
- /* If the controller on this path owns the LUN, return success */
- if (sp->avte_cvp & 0x1) {
- dm_pg_init_complete(h->path, 0);
- goto done;
- }
+ if (ctlr) {
+ /* If the controller on this path owns the LUN, return success */
+ if (sp->avte_cvp & 0x1) {
+ dm_pg_init_complete(h->path, 0);
+ goto done;
+ }
- if (h->ctlr) {
- if (h->ctlr->use_10_ms == -1)
+ if (ctlr->use_10_ms == -1)
h->cmd_to_send = SEND_C2_INQUIRY;
else
h->cmd_to_send = SEND_MODE_SELECT;
@@ -632,8 +603,6 @@ static void rdac_destroy(struct hw_handler *hwh)
{
struct rdac_handler *h = hwh->context;
- if (h->ctlr)
- kref_put(&h->ctlr->kref, release_ctlr);
kfree(h);
hwh->context = NULL;
}
--
1.5.3.2
More information about the dm-devel
mailing list