From: Brian Wood <[email-masked]>

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 device 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 end_io function. It is here that testing for BIO error conditions
take place. It determines the exact stripe that cause the error,
records this in the new atomic variable, and calls the queue_work() function

- New trigger_event function to process failure events. This
calls dm_table_event()

Signed-off-by: Brian Wood <[email-masked]>
Signed-off-by: Alasdair G Kergon <[email-masked]>
---
 drivers/md/dm-stripe.c |   82 ++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 81 insertions(+), 1 deletion(-)

Index: linux-2.6.24/drivers/md/dm-stripe.c
===================================================================
--- linux-2.6.24.orig/drivers/md/dm-stripe.c	2008-02-07 13:44:22.000000000 +0000
+++ linux-2.6.24/drivers/md/dm-stripe.c	2008-02-07 13:48:41.000000000 +0000
@@ -14,10 +14,13 @@
 #include <linux/log2.h>
 
 #define DM_MSG_PREFIX "striped"
+#define DM_IO_ERROR_THRESHOLD 15
 
 struct stripe {
 	struct dm_dev *dev;
 	sector_t physical_start;
+
+	atomic_t error_count;
 };
 
 struct stripe_c {
@@ -30,9 +33,29 @@ struct stripe_c {
 	uint32_t chunk_shift;
 	sector_t chunk_mask;
 
+	/* Needed for handling events */
+	struct dm_target *ti;
+
+	/* Work struct used for triggering events*/
+	struct work_struct kstriped_ws;
+
 	struct stripe stripe[0];
 };
 
+static 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)
+{
+	struct stripe_c *sc = container_of(work, struct stripe_c, kstriped_ws);
+
+	dm_table_event(sc->ti->table);
+
+}
+
 static inline struct stripe_c *alloc_context(unsigned int stripes)
 {
 	size_t len;
@@ -63,6 +86,7 @@ static int get_stripe(struct dm_target *
 		return -ENXIO;
 
 	sc->stripe[stripe].physical_start = start;
+
 	return 0;
 }
 
@@ -135,6 +159,11 @@ static int stripe_ctr(struct dm_target *
 		return -ENOMEM;
 	}
 
+	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;
 	ti->split_io = chunk_size;
@@ -158,9 +187,11 @@ static int stripe_ctr(struct dm_target *
 			kfree(sc);
 			return r;
 		}
+		atomic_set(&(sc->stripe[i].error_count), 0);
 	}
 
 	ti->private = sc;
+
 	return 0;
 }
 
@@ -172,6 +203,7 @@ static void stripe_dtr(struct dm_target 
 	for (i = 0; i < sc->stripes; i++)
 		dm_put_device(ti, sc->stripe[i].dev);
 
+	flush_workqueue(kstriped);
 	kfree(sc);
 }
 
@@ -213,13 +245,52 @@ static int stripe_status(struct dm_targe
 	return 0;
 }
 
+static int stripe_end_io(struct dm_target *ti, struct bio *bio,
+			 int error, union map_info *map_context)
+{
+	unsigned i;
+	char major_minor[16];
+	struct stripe_c *sc = ti->private;
+
+	if (!error)
+		return 0; /* I/O complete */
+
+	if ((error == -EWOULDBLOCK) && bio_rw_ahead(bio))
+		return error;
+
+	if (error == -EOPNOTSUPP)
+		return error;
+
+	memset(major_minor, 0, sizeof(major_minor));
+	sprintf(major_minor, "%d:%d",
+		bio->bi_bdev->bd_disk->major,
+		bio->bi_bdev->bd_disk->first_minor);
+
+	/*
+	 * Test to see which stripe drive triggered the event
+	 * and increment error count for all stripes on that device.
+	 * If the error count for a given device exceeds the threshold
+	 * value we will no longer trigger any further events.
+	 */
+	for (i = 0; i < sc->stripes; i++)
+		if (!strcmp(sc->stripe[i].dev->name, major_minor)) {
+			atomic_inc(&(sc->stripe[i].error_count));
+			if (atomic_read(&(sc->stripe[i].error_count)) <
+			    DM_IO_ERROR_THRESHOLD)
+				queue_work(kstriped, &sc->kstriped_ws);
+		}
+
+	return error;
+}
+
 static struct target_type stripe_target = {
 	.name   = "striped",
-	.version= {1, 0, 2},
+	.version = {1, 1, 0},
 	.module = THIS_MODULE,
 	.ctr    = stripe_ctr,
 	.dtr    = stripe_dtr,
 	.map    = stripe_map,
+	.end_io = stripe_end_io,
 	.status = stripe_status,
 };
 
@@ -231,6 +302,13 @@ int __init dm_stripe_init(void)
 	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;
 }
 
@@ -239,5 +317,7 @@ void dm_stripe_exit(void)
 	if (dm_unregister_target(&stripe_target))
 		DMWARN("target unregistration failed");
 
+	destroy_workqueue(kstriped);
+
 	return;
 }