diff -Naurp linux-2.6.7-rc2/drivers/md/dm-pg-hp-sw.c linux-2.6.7-rc2-pg/drivers/md/dm-pg-hp-sw.c --- linux-2.6.7-rc2/drivers/md/dm-pg-hp-sw.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.7-rc2-pg/drivers/md/dm-pg-hp-sw.c 2004-06-04 22:31:50.882789752 -0700 @@ -0,0 +1,330 @@ +/* + * Basic priority group for HP Storage Works to show how to + * send a failover command. DM is bio based but in here we stick + * in the request and SCSI stuff. + * + * We are also just failing over per lun. More logic could + * be added to also failover at the controller level. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "dm.h" +#include "dm-priority-group.h" + + +/* FIXME: get rid of this */ +#define PG_FAIL_COUNT 1 + +struct path_info { + struct list_head list; + struct path *path; + unsigned fail_count; +}; + +/* + * bleh - we do not need this. put some sort of private memeber on the + * path struct along with nice accessor fns. This is only used in + * non critical code paths though. + */ +static struct path_info *path_lookup(struct list_head *head, struct path *p) +{ + struct path_info *pi; + + list_for_each_entry (pi, head, list) + if (pi->path == p) + return pi; + + return NULL; +} + +struct pgroup { + struct priority_group *pg; + + /* failover command */ + struct request rq; + char sense[SCSI_SENSE_BUFFERSIZE]; + + spinlock_t path_lock; + + struct list_head valid_paths; + struct list_head invalid_paths; +}; + +static struct pgroup *alloc_pgroup(void) +{ + struct pgroup *pgroup = kmalloc(sizeof(*pgroup), GFP_KERNEL); + + if (pgroup) { + memset(pgroup, 0, sizeof(*pgroup)); + INIT_LIST_HEAD(&pgroup->valid_paths); + INIT_LIST_HEAD(&pgroup->invalid_paths); + pgroup->path_lock = SPIN_LOCK_UNLOCKED; + } + + return pgroup; +} + +static int hpsw_ctr(struct priority_group *pg) +{ + struct pgroup *pgroup; + + pgroup = alloc_pgroup(); + if (!pgroup) + return -ENOMEM; + + pgroup->pg = pg; + pg->context = pgroup; + return 0; +} + +static void free_paths(struct list_head *paths) +{ + struct path_info *pi, *next; + + list_for_each_entry_safe (pi, next, paths, list) { + list_del(&pi->list); + kfree(pi); + } +} + +static void hpsw_dtr(struct priority_group *pg) +{ + struct pgroup *pgroup = (struct pgroup *)pg->context; + free_paths(&pgroup->valid_paths); + free_paths(&pgroup->invalid_paths); + kfree(pgroup); +} + +static int hpsw_add_path(struct priority_group *pg, struct path *path, + int argc, char **argv, char **error) +{ + struct pgroup *pgroup= (struct pgroup *) pg->context; + struct path_info *pi; + + if (argc != 0) { + *error = "hp-storage-works: incorrect number of arguments"; + return -EINVAL; + } + + pi = kmalloc(sizeof(*pi), GFP_KERNEL); + if (!pi) { + *error = "hp-storage-works: Error allocating path context"; + return -ENOMEM; + } + + pi->fail_count = 0; + pi->path = path; + + spin_lock(&pgroup->path_lock); + list_add_tail(&pi->list, &pgroup->valid_paths); + spin_unlock(&pgroup->path_lock); + + return 0; +} + +static int hpsw_update_path(struct priority_group *pg, + struct path *path, int status) +{ + unsigned long flags; + int r = 0; + struct path_info *pi; + struct pgroup *pgroup = (struct pgroup *) pg->context; + + spin_lock_irqsave(&pgroup->path_lock, flags); + + if (status) { + pi = path_lookup(&pgroup->valid_paths, path); + if (!pi) { + /* + * This can happen if a path was marked as + * failed by the init function. + * We did not tell the ps that the path + * was failed becuase we wanted to give the + * path an extra chance. + */ + r = 1; + goto done; + } + + if (++pi->fail_count == PG_FAIL_COUNT) { + list_move(&pi->list, &pgroup->invalid_paths); + r = 1; + } + + } else { + pi = path_lookup(&pgroup->invalid_paths, path); + if (!pi) + goto done; + + list_move(&pi->list, &pgroup->valid_paths); + } + + done: + spin_unlock_irqrestore(&pgroup->path_lock, flags); + + return r; +} + + +static int hpsw_status(struct priority_group *pg, struct path *path, + status_type_t type, char *result, + unsigned int maxlen) +{ + unsigned long flags; + int failed = 0; + struct path_info *pi; + int sz = 0; + struct pgroup *pgroup = (struct pgroup *) pg->context; + + if (type == STATUSTYPE_TABLE) + return 0; + + spin_lock_irqsave(&pgroup->path_lock, flags); + + pi = path_lookup(&pgroup->valid_paths, path); + if (!pi) { + failed = 1; + pi = path_lookup(&pgroup->invalid_paths, path); + } + + sz = scnprintf(result, maxlen, "%s %u ", failed ? "F" : "A", + pi->fail_count); + + spin_unlock_irqrestore(&pgroup->path_lock, flags); + + return sz; +} + +static int hpsw_init(struct priority_group *pg); + +#define rq_to_pgroup(__rq) container_of((__rq), struct pgroup, rq) + +static void hpsw_failover_cmd_done(struct request *rq) +{ + struct pgroup *pgroup = rq_to_pgroup(rq); + struct priority_group *pg = pgroup->pg; + int err = DM_PG_SUCCESS; + + /* let's try another path (you can also look at the sense) */ + if (rq->errors) + err = hpsw_init(pg); + + /* + * if we are retrying the command on another path don't + * signal dm-mpath yet. + */ + if (err != DM_PG_INITIALIZING) + dm_pg_init_complete(pg, err); +} + +/* ??? */ +#define HPSW_FAILOVER_TIMEOUT (60 * HZ) + +static int hpsw_insert_cmd(struct priority_group *pg, struct path_info *pi) +{ + struct pgroup *pgroup = (struct pgroup *) pg->context; + struct request *rq = &pgroup->rq; + request_queue_t *q; + char *sense = pgroup->sense; + struct block_device *bdev; + + bdev = dm_path_to_bdev(pi->path); + q = bdev_get_queue(bdev); + if (!q) + return -EINVAL; + + memset(rq, 0, sizeof(*rq)); + rq->flags = READ; + rq->rq_status = RQ_ACTIVE; + rq->ref_count = 1; + rq->q = q; + rq->errors = 0; + rq->bio = rq->biotail = NULL; + rq->buffer = NULL; + rq->ref_count = 2; + rq->rl = NULL; + rq->end_request = NULL; + rq->waiting = NULL; + rq->special = NULL; + + memset(sense, 0, sizeof(SCSI_SENSE_BUFFERSIZE)); + rq->sense = sense; + rq->sense_len = 0; + + rq->cmd[0] = START_STOP; + rq->cmd[1] = 1; /* return immed or wait ??? */ + rq->cmd[4] = 1; /* spin up */ + rq->cmd_len = COMMAND_SIZE(rq->cmd[0]); + + rq->data = NULL; + rq->data_len = 0; + + rq->flags |= REQ_NOMERGE | REQ_BLOCK_PC | REQ_FAILFAST; + + rq->rq_disk = bdev->bd_contains->bd_disk; + rq->timeout = HPSW_FAILOVER_TIMEOUT; + rq->end_request = hpsw_failover_cmd_done; + + elv_add_request(q, rq, ELEVATOR_INSERT_FRONT, 1); + + return 0; +} + +static int hpsw_init(struct priority_group *pg) +{ + struct path_info *pi; + struct pgroup *pgroup = (struct pgroup *) pg->context; + int err = DM_PG_FAILED; + + while (!list_empty(&pgroup->valid_paths)) { + pi = list_entry(pgroup->valid_paths.next, + struct path_info, list); + + if (hpsw_insert_cmd(pg, pi)) + list_move(&pi->list, &pgroup->invalid_paths); + else { + err = DM_PG_INITIALIZING; + break; + } + } + + return err; +} + +static struct priority_group_type hpsw_pg = { + .name = "hp-storage-works", + .module = THIS_MODULE, + + .ctr = hpsw_ctr, + .dtr = hpsw_dtr, + .init = hpsw_init, + .add_path = hpsw_add_path, + .update_path = hpsw_update_path, + .status = hpsw_status, +}; + +static int __init init_hpsw(void) +{ + return dm_register_priority_group_type(&hpsw_pg); +} + +static void __exit exit_hpsw(void) +{ + dm_unregister_priority_group_type(&hpsw_pg); +} + +module_init(init_hpsw); +module_exit(exit_hpsw); + +MODULE_DESCRIPTION("Manual Failover for HP Storage Works"); +MODULE_AUTHOR("Mike Christie"); +MODULE_LICENSE("GPL"); diff -Naurp linux-2.6.7-rc2/drivers/md/Kconfig linux-2.6.7-rc2-pg/drivers/md/Kconfig --- linux-2.6.7-rc2/drivers/md/Kconfig 2004-06-04 03:52:23.000000000 -0700 +++ linux-2.6.7-rc2-pg/drivers/md/Kconfig 2004-06-04 22:31:44.518038122 -0700 @@ -206,6 +206,12 @@ config DM_MULTIPATH ---help--- Allow volume managers to support multipath hardware. +config DM_PG_HPSW + tristate "DM Multipath HP Storage Works (EXPERIMENTAL)" + depends on BLK_DEV_DM && EXPERIMENTAL + ---help--- + Manual failover support for HP Storage Works. + config DM_FLAKEY tristate "Flakey target (EXPERIMENTAL)" depends on BLK_DEV_DM && EXPERIMENTAL diff -Naurp linux-2.6.7-rc2/drivers/md/Makefile linux-2.6.7-rc2-pg/drivers/md/Makefile --- linux-2.6.7-rc2/drivers/md/Makefile 2004-06-04 04:04:17.000000000 -0700 +++ linux-2.6.7-rc2-pg/drivers/md/Makefile 2004-06-04 22:31:41.202181278 -0700 @@ -28,6 +28,7 @@ obj-$(CONFIG_BLK_DEV_MD) += md.o obj-$(CONFIG_BLK_DEV_DM) += dm-mod.o obj-$(CONFIG_DM_CRYPT) += dm-crypt.o obj-$(CONFIG_DM_MULTIPATH) += dm-multipath.o +obj-$(CONFIG_DM_PG_HPSW) += dm-pg-hp-sw.o obj-$(CONFIG_DM_SNAPSHOT) += dm-snapshot.o obj-$(CONFIG_DM_MIRROR) += dm-mirror.o obj-$(CONFIG_DM_FLAKEY) += dm-flakey.o