[dm-devel] [PATCH 1/2] add stripe end_io function and event trigger
Wood, Brian J
brian.j.wood at intel.com
Tue Dec 4 17:36:29 UTC 2007
From: Brian Wood <brian.j.wood at 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 at 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;
}
More information about the dm-devel
mailing list