From: Brian Wood <brian.j.wood@xxxxxxxxx> 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@xxxxxxxxx> --- 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; } -- dm-devel mailing list dm-devel@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/dm-devel