[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[dm-devel] [PATCH 2/4] add manual failover support for HP storage works



hp-sw-init.patch - this is a priority-group module for the HP storage works device. I have only tested this with scsi_debug, so I know that the start_stop is making its way to scsi and is returning safely.
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 <linux/slab.h>
+#include <linux/list.h>
+#include <linux/fs.h>
+#include <linux/blkdev.h>
+#include <linux/init.h>
+#include <linux/module.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+
+#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

[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]