Commit a25eb944 authored by Brian Wood's avatar Brian Wood Committed by Alasdair G Kergon

dm: stripe trigger event on failure

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: default avatarBrian Wood <brian.j.wood@intel.com>
Signed-off-by: default avatarAlasdair G Kergon <agk@redhat.com>
parent fb8b2848
...@@ -14,10 +14,13 @@ ...@@ -14,10 +14,13 @@
#include <linux/log2.h> #include <linux/log2.h>
#define DM_MSG_PREFIX "striped" #define DM_MSG_PREFIX "striped"
#define DM_IO_ERROR_THRESHOLD 15
struct stripe { struct stripe {
struct dm_dev *dev; struct dm_dev *dev;
sector_t physical_start; sector_t physical_start;
atomic_t error_count;
}; };
struct stripe_c { struct stripe_c {
...@@ -30,9 +33,29 @@ struct stripe_c { ...@@ -30,9 +33,29 @@ struct stripe_c {
uint32_t chunk_shift; uint32_t chunk_shift;
sector_t chunk_mask; 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]; 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) static inline struct stripe_c *alloc_context(unsigned int stripes)
{ {
size_t len; size_t len;
...@@ -63,6 +86,7 @@ static int get_stripe(struct dm_target *ti, struct stripe_c *sc, ...@@ -63,6 +86,7 @@ static int get_stripe(struct dm_target *ti, struct stripe_c *sc,
return -ENXIO; return -ENXIO;
sc->stripe[stripe].physical_start = start; sc->stripe[stripe].physical_start = start;
return 0; return 0;
} }
...@@ -135,6 +159,11 @@ static int stripe_ctr(struct dm_target *ti, unsigned int argc, char **argv) ...@@ -135,6 +159,11 @@ static int stripe_ctr(struct dm_target *ti, unsigned int argc, char **argv)
return -ENOMEM; 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->stripes = stripes;
sc->stripe_width = width; sc->stripe_width = width;
ti->split_io = chunk_size; ti->split_io = chunk_size;
...@@ -158,9 +187,11 @@ static int stripe_ctr(struct dm_target *ti, unsigned int argc, char **argv) ...@@ -158,9 +187,11 @@ static int stripe_ctr(struct dm_target *ti, unsigned int argc, char **argv)
kfree(sc); kfree(sc);
return r; return r;
} }
atomic_set(&(sc->stripe[i].error_count), 0);
} }
ti->private = sc; ti->private = sc;
return 0; return 0;
} }
...@@ -172,6 +203,7 @@ static void stripe_dtr(struct dm_target *ti) ...@@ -172,6 +203,7 @@ static void stripe_dtr(struct dm_target *ti)
for (i = 0; i < sc->stripes; i++) for (i = 0; i < sc->stripes; i++)
dm_put_device(ti, sc->stripe[i].dev); dm_put_device(ti, sc->stripe[i].dev);
flush_workqueue(kstriped);
kfree(sc); kfree(sc);
} }
...@@ -213,13 +245,52 @@ static int stripe_status(struct dm_target *ti, ...@@ -213,13 +245,52 @@ static int stripe_status(struct dm_target *ti,
return 0; 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 = { static struct target_type stripe_target = {
.name = "striped", .name = "striped",
.version= {1, 0, 2}, .version = {1, 1, 0},
.module = THIS_MODULE, .module = THIS_MODULE,
.ctr = stripe_ctr, .ctr = stripe_ctr,
.dtr = stripe_dtr, .dtr = stripe_dtr,
.map = stripe_map, .map = stripe_map,
.end_io = stripe_end_io,
.status = stripe_status, .status = stripe_status,
}; };
...@@ -231,6 +302,13 @@ int __init dm_stripe_init(void) ...@@ -231,6 +302,13 @@ int __init dm_stripe_init(void)
if (r < 0) if (r < 0)
DMWARN("target registration failed"); 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; return r;
} }
...@@ -239,5 +317,7 @@ void dm_stripe_exit(void) ...@@ -239,5 +317,7 @@ void dm_stripe_exit(void)
if (dm_unregister_target(&stripe_target)) if (dm_unregister_target(&stripe_target))
DMWARN("target unregistration failed"); DMWARN("target unregistration failed");
destroy_workqueue(kstriped);
return; return;
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment