|
@@ -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 *ti, struct stripe_c *sc,
|
|
|
return -ENXIO;
|
|
|
|
|
|
sc->stripe[stripe].physical_start = start;
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -135,6 +159,11 @@ static int stripe_ctr(struct dm_target *ti, unsigned int argc, char **argv)
|
|
|
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 *ti, unsigned int argc, char **argv)
|
|
|
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 *ti)
|
|
|
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_target *ti,
|
|
|
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;
|
|
|
}
|