]> git.hungrycats.org Git - linux/commitdiff
[PATCH] dm: endio method
authorAndrew Morton <akpm@osdl.org>
Fri, 12 Mar 2004 00:13:40 +0000 (16:13 -0800)
committerLinus Torvalds <torvalds@ppc970.osdl.org>
Fri, 12 Mar 2004 00:13:40 +0000 (16:13 -0800)
From: Joe Thornber <thornber@redhat.com>

Add an endio method to targets.  This method is allowed to request another
shot at failed ios (think multipath).  Context can be passed between the map
method and the endio method.

drivers/md/dm-crypt.c
drivers/md/dm-linear.c
drivers/md/dm-stripe.c
drivers/md/dm-target.c
drivers/md/dm.c
include/linux/device-mapper.h

index 898f4d04762904dd5c318c9da73ae4ff696e9137..5b73745a7d8fc8ae4f8ed1c011ab11ade2658c76 100644 (file)
@@ -621,7 +621,8 @@ crypt_clone(struct crypt_config *cc, struct crypt_io *io, struct bio *bio,
        return clone;
 }
 
-static int crypt_map(struct dm_target *ti, struct bio *bio)
+static int crypt_map(struct dm_target *ti, struct bio *bio,
+                    union map_info *map_context)
 {
        struct crypt_config *cc = (struct crypt_config *) ti->private;
        struct crypt_io *io = mempool_alloc(cc->io_pool, GFP_NOIO);
index cd7b6ee67bab27f4faa89032b44c004a50d0684c..fcb44dca2ff7240d892cffeda861ff2411d701b7 100644 (file)
@@ -65,7 +65,8 @@ static void linear_dtr(struct dm_target *ti)
        kfree(lc);
 }
 
-static int linear_map(struct dm_target *ti, struct bio *bio)
+static int linear_map(struct dm_target *ti, struct bio *bio,
+                     union map_info *map_context)
 {
        struct linear_c *lc = (struct linear_c *) ti->private;
 
index 16df5ce04c4138e413dfccc5f5033a5eb4618a89..047a1580eb69e5f42fd002d7ceb2268bae3b18a9 100644 (file)
@@ -166,7 +166,8 @@ static void stripe_dtr(struct dm_target *ti)
        kfree(sc);
 }
 
-static int stripe_map(struct dm_target *ti, struct bio *bio)
+static int stripe_map(struct dm_target *ti, struct bio *bio,
+                     union map_info *map_context)
 {
        struct stripe_c *sc = (struct stripe_c *) ti->private;
 
index 9c9471c2e3f2a1afee61c4c163922e1ff70cc836..0dac63c84c3afe0c5a48814da43d1dffcd766d9d 100644 (file)
@@ -157,7 +157,8 @@ static void io_err_dtr(struct dm_target *ti)
        /* empty */
 }
 
-static int io_err_map(struct dm_target *ti, struct bio *bio)
+static int io_err_map(struct dm_target *ti, struct bio *bio,
+                     union map_info *map_context)
 {
        return -EIO;
 }
index cfecb376b92756aeedc1f6e03c8edf54de252492..f6f6410448aa3983c1e7581f47e0103d62c915d4 100644 (file)
@@ -21,6 +21,9 @@ static const char *_name = DM_NAME;
 static unsigned int major = 0;
 static unsigned int _major = 0;
 
+/*
+ * One of these is allocated per bio.
+ */
 struct dm_io {
        struct mapped_device *md;
        int error;
@@ -28,6 +31,16 @@ struct dm_io {
        atomic_t io_count;
 };
 
+/*
+ * One of these is allocated per target within a bio.  Hopefully
+ * this will be simplified out one day.
+ */
+struct target_io {
+       struct dm_io *io;
+       struct dm_target *ti;
+       union map_info info;
+};
+
 /*
  * Bits for the md->flags field.
  */
@@ -59,6 +72,7 @@ struct mapped_device {
         * io objects are allocated from here.
         */
        mempool_t *io_pool;
+       mempool_t *tio_pool;
 
        /*
         * Event handling.
@@ -69,6 +83,7 @@ struct mapped_device {
 
 #define MIN_IOS 256
 static kmem_cache_t *_io_cache;
+static kmem_cache_t *_tio_cache;
 
 static __init int local_init(void)
 {
@@ -80,9 +95,18 @@ static __init int local_init(void)
        if (!_io_cache)
                return -ENOMEM;
 
+       /* allocate a slab for the target ios */
+       _tio_cache = kmem_cache_create("dm_tio", sizeof(struct target_io),
+                                      0, 0, NULL, NULL);
+       if (!_tio_cache) {
+               kmem_cache_destroy(_io_cache);
+               return -ENOMEM;
+       }
+
        _major = major;
        r = register_blkdev(_major, _name);
        if (r < 0) {
+               kmem_cache_destroy(_tio_cache);
                kmem_cache_destroy(_io_cache);
                return r;
        }
@@ -95,6 +119,7 @@ static __init int local_init(void)
 
 static void local_exit(void)
 {
+       kmem_cache_destroy(_tio_cache);
        kmem_cache_destroy(_io_cache);
 
        if (unregister_blkdev(_major, _name) < 0)
@@ -184,6 +209,16 @@ static inline void free_io(struct mapped_device *md, struct dm_io *io)
        mempool_free(io, md->io_pool);
 }
 
+static inline struct target_io *alloc_tio(struct mapped_device *md)
+{
+       return mempool_alloc(md->tio_pool, GFP_NOIO);
+}
+
+static inline void free_tio(struct mapped_device *md, struct target_io *tio)
+{
+       mempool_free(tio, md->tio_pool);
+}
+
 /*
  * Add the bio to the list of deferred io.
  */
@@ -232,17 +267,30 @@ static inline void dec_pending(struct dm_io *io, int error)
 
 static int clone_endio(struct bio *bio, unsigned int done, int error)
 {
-       struct dm_io *io = bio->bi_private;
+       int r = 0;
+       struct target_io *tio = bio->bi_private;
+       struct dm_io *io = tio->io;
+       dm_endio_fn endio = tio->ti->type->end_io;
 
        if (bio->bi_size)
                return 1;
 
+       if (endio) {
+               r = endio(tio->ti, bio, error, &tio->info);
+               if (r < 0)
+                       error = r;
+
+               else if (r > 0)
+                       /* the target wants another shot at the io */
+                       return 1;
+       }
+
+       free_tio(io->md, tio);
        dec_pending(io, error);
        bio_put(bio);
-       return 0;
+       return r;
 }
 
-
 static sector_t max_io_len(struct mapped_device *md,
                           sector_t sector, struct dm_target *ti)
 {
@@ -263,7 +311,8 @@ static sector_t max_io_len(struct mapped_device *md,
        return len;
 }
 
-static void __map_bio(struct dm_target *ti, struct bio *clone, struct dm_io *io)
+static void __map_bio(struct dm_target *ti, struct bio *clone,
+                     struct target_io *tio)
 {
        int r;
 
@@ -273,22 +322,25 @@ static void __map_bio(struct dm_target *ti, struct bio *clone, struct dm_io *io)
        BUG_ON(!clone->bi_size);
 
        clone->bi_end_io = clone_endio;
-       clone->bi_private = io;
+       clone->bi_private = tio;
 
        /*
         * Map the clone.  If r == 0 we don't need to do
         * anything, the target has assumed ownership of
         * this io.
         */
-       atomic_inc(&io->io_count);
-       r = ti->type->map(ti, clone);
+       atomic_inc(&tio->io->io_count);
+       r = ti->type->map(ti, clone, &tio->info);
        if (r > 0)
                /* the bio has been remapped so dispatch it */
                generic_make_request(clone);
 
-       else if (r < 0)
+       else if (r < 0) {
                /* error the io and bail out */
+               struct dm_io *io = tio->io;
+               free_tio(tio->io->md, tio);
                dec_pending(io, -EIO);
+       }
 }
 
 struct clone_info {
@@ -348,6 +400,15 @@ static void __clone_and_map(struct clone_info *ci)
        struct bio *clone, *bio = ci->bio;
        struct dm_target *ti = dm_table_find_target(ci->md->map, ci->sector);
        sector_t len = 0, max = max_io_len(ci->md, ci->sector, ti);
+       struct target_io *tio;
+
+       /*
+        * Allocate a target io object.
+        */
+       tio = alloc_tio(ci->md);
+       tio->io = ci->io;
+       tio->ti = ti;
+       memset(&tio->info, 0, sizeof(tio->info));
 
        if (ci->sector_count <= max) {
                /*
@@ -356,7 +417,7 @@ static void __clone_and_map(struct clone_info *ci)
                 */
                clone = clone_bio(bio, ci->sector, ci->idx,
                                  bio->bi_vcnt - ci->idx, ci->sector_count);
-               __map_bio(ti, clone, ci->io);
+               __map_bio(ti, clone, tio);
                ci->sector_count = 0;
 
        } else if (to_sector(bio->bi_io_vec[ci->idx].bv_len) <= max) {
@@ -379,7 +440,7 @@ static void __clone_and_map(struct clone_info *ci)
                }
 
                clone = clone_bio(bio, ci->sector, ci->idx, i - ci->idx, len);
-               __map_bio(ti, clone, ci->io);
+               __map_bio(ti, clone, tio);
 
                ci->sector += len;
                ci->sector_count -= len;
@@ -394,7 +455,7 @@ static void __clone_and_map(struct clone_info *ci)
 
                clone = split_bvec(bio, ci->sector, ci->idx,
                                   bv->bv_offset, max);
-               __map_bio(ti, clone, ci->io);
+               __map_bio(ti, clone, tio);
 
                ci->sector += max;
                ci->sector_count -= max;
@@ -403,7 +464,11 @@ static void __clone_and_map(struct clone_info *ci)
                len = to_sector(bv->bv_len) - max;
                clone = split_bvec(bio, ci->sector, ci->idx,
                                   bv->bv_offset + to_bytes(max), len);
-               __map_bio(ti, clone, ci->io);
+               tio = alloc_tio(ci->md);
+               tio->io = ci->io;
+               tio->ti = ti;
+               memset(&tio->info, 0, sizeof(tio->info));
+               __map_bio(ti, clone, tio);
 
                ci->sector += len;
                ci->sector_count -= len;
@@ -441,6 +506,16 @@ static void __split_bio(struct mapped_device *md, struct bio *bio)
  *---------------------------------------------------------------*/
 
 
+static inline void __dm_request(struct mapped_device *md, struct bio *bio)
+{
+       if (!md->map) {
+               bio_io_error(bio, bio->bi_size);
+               return;
+       }
+
+       __split_bio(md, bio);
+}
+
 /*
  * The request function that just remaps the bio built up by
  * dm_merge_bvec.
@@ -479,12 +554,7 @@ static int dm_request(request_queue_t *q, struct bio *bio)
                down_read(&md->lock);
        }
 
-       if (!md->map) {
-               bio_io_error(bio, bio->bi_size);
-               return 0;
-       }
-
-       __split_bio(md, bio);
+       __dm_request(md, bio);
        up_read(&md->lock);
        return 0;
 }
@@ -574,9 +644,14 @@ static struct mapped_device *alloc_dev(unsigned int minor, int persistent)
        if (!md->io_pool)
                goto bad2;
 
+       md->tio_pool = mempool_create(MIN_IOS, mempool_alloc_slab,
+                                     mempool_free_slab, _tio_cache);
+       if (!md->tio_pool)
+               goto bad3;
+
        md->disk = alloc_disk(1);
        if (!md->disk)
-               goto bad3;
+               goto bad4;
 
        md->disk->major = _major;
        md->disk->first_minor = minor;
@@ -592,7 +667,8 @@ static struct mapped_device *alloc_dev(unsigned int minor, int persistent)
 
        return md;
 
-
+ bad4:
+       mempool_destroy(md->tio_pool);
  bad3:
        mempool_destroy(md->io_pool);
  bad2:
@@ -606,6 +682,7 @@ static struct mapped_device *alloc_dev(unsigned int minor, int persistent)
 static void free_dev(struct mapped_device *md)
 {
        free_minor(md->disk->first_minor);
+       mempool_destroy(md->tio_pool);
        mempool_destroy(md->io_pool);
        del_gendisk(md->disk);
        put_disk(md->disk);
@@ -644,13 +721,13 @@ static int __bind(struct mapped_device *md, struct dm_table *t)
 {
        request_queue_t *q = md->queue;
        sector_t size;
-       md->map = t;
 
        size = dm_table_get_size(t);
        __set_size(md->disk, size);
        if (size == 0)
                return 0;
 
+       md->map = t;
        dm_table_event_callback(md->map, event_callback, md);
 
        dm_table_get(t);
@@ -710,16 +787,16 @@ void dm_put(struct mapped_device *md)
 }
 
 /*
- * Requeue the deferred bios by calling generic_make_request.
+ * Process the deferred bios
  */
-static void flush_deferred_io(struct bio *c)
+static void __flush_deferred_io(struct mapped_device *md, struct bio *c)
 {
        struct bio *n;
 
        while (c) {
                n = c->bi_next;
                c->bi_next = NULL;
-               generic_make_request(c);
+               __dm_request(md, c);
                c = n;
        }
 }
@@ -814,10 +891,11 @@ int dm_resume(struct mapped_device *md)
        dm_table_resume_targets(md->map);
        clear_bit(DMF_SUSPENDED, &md->flags);
        clear_bit(DMF_BLOCK_IO, &md->flags);
+
        def = bio_list_get(&md->deferred);
+       __flush_deferred_io(md, def);
        up_write(&md->lock);
 
-       flush_deferred_io(def);
        blk_run_queues();
 
        return 0;
index 40ea6338e1a1deb0279ccef7034371fcc8aa5fce..a9ecba77fb305822fd85f07116734f5644062f2e 100644 (file)
@@ -13,6 +13,11 @@ struct dm_dev;
 
 typedef enum { STATUSTYPE_INFO, STATUSTYPE_TABLE } status_type_t;
 
+union map_info {
+       void *ptr;
+       unsigned long long ll;
+};
+
 /*
  * In the constructor the target parameter will already have the
  * table, type, begin and len fields filled in.
@@ -32,7 +37,19 @@ typedef void (*dm_dtr_fn) (struct dm_target *ti);
  * = 0: The target will handle the io by resubmitting it later
  * > 0: simple remap complete
  */
-typedef int (*dm_map_fn) (struct dm_target *ti, struct bio *bio);
+typedef int (*dm_map_fn) (struct dm_target *ti, struct bio *bio,
+                         union map_info *map_context);
+
+/*
+ * Returns:
+ * < 0 : error (currently ignored)
+ * 0   : ended successfully
+ * 1   : for some reason the io has still not completed (eg,
+ *       multipath target might want to requeue a failed io).
+ */
+typedef int (*dm_endio_fn) (struct dm_target *ti,
+                           struct bio *bio, int error,
+                           union map_info *map_context);
 
 typedef void (*dm_suspend_fn) (struct dm_target *ti);
 typedef void (*dm_resume_fn) (struct dm_target *ti);
@@ -60,6 +77,7 @@ struct target_type {
        dm_ctr_fn ctr;
        dm_dtr_fn dtr;
        dm_map_fn map;
+       dm_endio_fn end_io;
        dm_suspend_fn suspend;
        dm_resume_fn resume;
        dm_status_fn status;