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

[dm-devel] [PATCH 1/2] add stripe end_io function and event trigger



From: Brian Wood <brian j wood intel com>

This patch adds the stripe_end_io function to process errors that might
occur after an IO operation. As part of this there are a number of
enhancements made to record and trigger events:
	- New atomic variable in struct stripe to record the number of
errors each stripe volume has experienced (could be used later with
uevents to report back directly to userspace)
	- New workqueue/work struct setup to process the trigger_event
function
	- New trigger_event function to process failure events. This
will determine the exact stripe that cause the IO error, record the
error and call dm_table_event()
	- With this change Alasdair requested that the version number be
incremented

Signed-off-by: Brian Wood <brian j wood intel com>

--- linux-2.6.24-rc3/drivers/md/dm-stripe.c	2007-11-16
21:16:36.000000000 -0800
+++ linux-2.6.24-rc3.mod/drivers/md/dm-stripe.c	2007-12-03
07:08:16.000000000 -0800
@@ -12,10 +12,14 @@
 #include <linux/bio.h>
 #include <linux/slab.h>
 #include <linux/log2.h>
+#include <linux/namei.h>
 
 #define DM_MSG_PREFIX "striped"
+#define IO_ERROR_COUNT 15
+#define DEV_STR_LEN 15
 
 struct stripe {
+	atomic_t error_count;
 	struct dm_dev *dev;
 	sector_t physical_start;
 };
@@ -30,9 +34,69 @@
 	uint32_t chunk_shift;
 	sector_t chunk_mask;
 
-	struct stripe stripe[0];
+	/* Needed for handling events */
+	struct dm_target *ti;
+	
+	/* Work struct used for triggering events*/
+	struct work_struct kstriped_ws;
+
+	struct stripe stripe[0];	
 };
 
+struct workqueue_struct *kstriped;
+
+/*
+ * An event is triggered whenever a drive 
+ * drops out of a stripe volume.
+ */
+static void trigger_event(struct work_struct *work)
+{
+	int i, len;
+	char dev_path[DEV_STR_LEN];
+	struct stripe_c *sc = container_of(work, struct stripe_c,
kstriped_ws);
+	struct nameidata nd;
+	struct inode *inode;
+
+	/* Test to see which stripe drive triggered the event. */
+	for (i = 0; i < sc->stripes; i++) {
+		memset(dev_path, 0, DEV_STR_LEN); 
+		memcpy(dev_path, "/dev/", len = strlen("/dev/"));
+		/* Copy name of the drive stored in the gendisk
structure
+		 * (this is the same name as what is stored in the /proc
and /sysfs filesystems */
+		memcpy(dev_path+len,
sc->stripe[i].dev->bdev->bd_disk->disk_name, 
+
strlen(sc->stripe[i].dev->bdev->bd_disk->disk_name)); 			
+		
+		/* Check to see if path can be used to fill nameidata
structure */
+		if (path_lookup(dev_path, LOOKUP_FOLLOW, &nd)) {
+			atomic_inc(&(sc->stripe[i].error_count));
+			goto out;
+		}
+		
+		/* Next check to see if device has an inode entry 
+		 * (this is where it will likely find the disabled
device) */
+		inode = nd.dentry->d_inode;
+		if (!inode) {
+			atomic_inc(&(sc->stripe[i].error_count));
+			goto out;
+		}
+
+		/* Finally as a last check, see if the inode mode is set

+		 * to be a block device, record error if not */
+		if (!S_ISBLK(inode->i_mode)) {
+			atomic_inc(&(sc->stripe[i].error_count));
+		}
+out:		
+		/* We don't want to generate hundreds of work queue
calls
+		 * when IO errors occur for a device, so we only queue
up the 
+		 * first IO_ERROR_COUNT for a given underlying hardware
device. 
+		 * After that point is reached we still record the
errors, 
+		 * but won't call dm_table_event() to report them. */
+		if(atomic_read(&(sc->stripe[i].error_count)) <
IO_ERROR_COUNT) {
+			dm_table_event(sc->ti->table);
+		}
+	}
+}
+
 static inline struct stripe_c *alloc_context(unsigned int stripes)
 {
 	size_t len;
@@ -63,6 +127,7 @@
 		return -ENXIO;
 
 	sc->stripe[stripe].physical_start = start;
+	
 	return 0;
 }
 
@@ -134,6 +199,12 @@
 		    "failed";
 		return -ENOMEM;
 	}
+	
+	/* Initialize the work_struct */	
+	INIT_WORK(&sc->kstriped_ws, trigger_event);
+
+	/* Set pointer to dm target; used in trigger_event */
+	sc->ti = ti;
 
 	sc->stripes = stripes;
 	sc->stripe_width = width;
@@ -143,13 +214,12 @@
 	for (sc->chunk_shift = 0; chunk_size; sc->chunk_shift++)
 		chunk_size >>= 1;
 	sc->chunk_shift--;
-
+	
 	/*
 	 * Get the stripe destinations.
 	 */
 	for (i = 0; i < stripes; i++) {
 		argv += 2;
-
 		r = get_stripe(ti, sc, i, argv);
 		if (r < 0) {
 			ti->error = "Couldn't parse stripe destination";
@@ -158,9 +228,16 @@
 			kfree(sc);
 			return r;
 		}
+		/* Initialize error_count */
+		atomic_set(&(sc->stripe[i].error_count), 0);
 	}
 
 	ti->private = sc;
+	
+	/* Trigger event to let dmeventd daemon know to
+	 * check device status when stripe's DSO is registered*/
+	queue_work(kstriped, &sc->kstriped_ws);
+	
 	return 0;
 }
 
@@ -172,6 +249,7 @@
 	for (i = 0; i < sc->stripes; i++)
 		dm_put_device(ti, sc->stripe[i].dev);
 
+	flush_workqueue(kstriped);
 	kfree(sc);
 }
 
@@ -213,13 +291,38 @@
 	return 0;
 }
 
+static int stripe_end_io(struct dm_target *ti, struct bio *bio,
+			 int error, union map_info *map_context)
+{
+	struct stripe_c *sc = (struct stripe_c *) ti->private;
+	
+	if (!error)
+		return 0; /* I/O complete */
+	
+	if ((error == -EWOULDBLOCK) && bio_rw_ahead(bio))
+		return error;
+	
+	if (error == -EOPNOTSUPP)
+		return error;
+	
+	/* Error not caught above, trigger event. 
+	 * For example, if member device gets 
+	 * disconnected this sets "error == -5" */
+	if (error < 0) {
+		queue_work(kstriped, &sc->kstriped_ws);
+	}
+
+	return error;
+}
+
 static struct target_type stripe_target = {
 	.name   = "striped",
-	.version= {1, 0, 2},
+	.version= {1, 1, 2},
 	.module = THIS_MODULE,
 	.ctr    = stripe_ctr,
 	.dtr    = stripe_dtr,
 	.map    = stripe_map,
+	.end_io = stripe_end_io,
 	.status = stripe_status,
 };
 
@@ -230,7 +333,14 @@
 	r = dm_register_target(&stripe_target);
 	if (r < 0)
 		DMWARN("target registration failed");
-
+	
+	kstriped = create_singlethread_workqueue("kstriped");
+	if (!kstriped) {
+		DMERR("failed to create workqueue kstriped");
+		dm_unregister_target(&stripe_target);
+		return -ENOMEM;
+	}
+	
 	return r;
 }
 
@@ -238,6 +348,8 @@
 {
 	if (dm_unregister_target(&stripe_target))
 		DMWARN("target unregistration failed");
-
+	
+	destroy_workqueue(kstriped);
+	
 	return;
 }


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