diff options
-rw-r--r-- | drivers/md/dm-crypt.c | 6 | ||||
-rw-r--r-- | drivers/md/dm-delay.c | 6 | ||||
-rw-r--r-- | drivers/md/dm-ioctl.c | 6 | ||||
-rw-r--r-- | drivers/md/dm-linear.c | 6 | ||||
-rw-r--r-- | drivers/md/dm-log.c | 40 | ||||
-rw-r--r-- | drivers/md/dm-mpath.c | 6 | ||||
-rw-r--r-- | drivers/md/dm-raid1.c | 24 | ||||
-rw-r--r-- | drivers/md/dm-snap.c | 18 | ||||
-rw-r--r-- | drivers/md/dm-stripe.c | 4 | ||||
-rw-r--r-- | drivers/md/dm-table.c | 19 | ||||
-rw-r--r-- | drivers/md/dm-target.c | 15 | ||||
-rw-r--r-- | drivers/md/dm-zero.c | 5 | ||||
-rw-r--r-- | drivers/md/dm.c | 58 | ||||
-rw-r--r-- | drivers/md/dm.h | 1 | ||||
-rw-r--r-- | include/linux/device-mapper.h | 27 |
15 files changed, 151 insertions, 90 deletions
diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index ce26c84af064..dbc03c67fe98 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c @@ -1322,11 +1322,7 @@ static int __init dm_crypt_init(void) static void __exit dm_crypt_exit(void) { - int r = dm_unregister_target(&crypt_target); - - if (r < 0) - DMERR("unregister failed %d", r); - + dm_unregister_target(&crypt_target); kmem_cache_destroy(_crypt_io_pool); } diff --git a/drivers/md/dm-delay.c b/drivers/md/dm-delay.c index 848b381f1173..59ee1b015d2d 100644 --- a/drivers/md/dm-delay.c +++ b/drivers/md/dm-delay.c @@ -364,11 +364,7 @@ bad_queue: static void __exit dm_delay_exit(void) { - int r = dm_unregister_target(&delay_target); - - if (r < 0) - DMERR("unregister failed %d", r); - + dm_unregister_target(&delay_target); kmem_cache_destroy(delayed_cache); destroy_workqueue(kdelayd_wq); } diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index 777c948180f9..8da7a017b4ef 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c @@ -1550,8 +1550,10 @@ int dm_copy_name_and_uuid(struct mapped_device *md, char *name, char *uuid) goto out; } - strcpy(name, hc->name); - strcpy(uuid, hc->uuid ? : ""); + if (name) + strcpy(name, hc->name); + if (uuid) + strcpy(uuid, hc->uuid ? : ""); out: up_read(&_hash_lock); diff --git a/drivers/md/dm-linear.c b/drivers/md/dm-linear.c index 44042becad8a..bfa107f59d96 100644 --- a/drivers/md/dm-linear.c +++ b/drivers/md/dm-linear.c @@ -142,6 +142,7 @@ static struct target_type linear_target = { .status = linear_status, .ioctl = linear_ioctl, .merge = linear_merge, + .features = DM_TARGET_SUPPORTS_BARRIERS, }; int __init dm_linear_init(void) @@ -156,8 +157,5 @@ int __init dm_linear_init(void) void dm_linear_exit(void) { - int r = dm_unregister_target(&linear_target); - - if (r < 0) - DMERR("unregister failed %d", r); + dm_unregister_target(&linear_target); } diff --git a/drivers/md/dm-log.c b/drivers/md/dm-log.c index a8c0fc79ca78..737961f275c1 100644 --- a/drivers/md/dm-log.c +++ b/drivers/md/dm-log.c @@ -326,8 +326,6 @@ static void header_from_disk(struct log_header *core, struct log_header *disk) static int rw_header(struct log_c *lc, int rw) { lc->io_req.bi_rw = rw; - lc->io_req.mem.ptr.vma = lc->disk_header; - lc->io_req.notify.fn = NULL; return dm_io(&lc->io_req, 1, &lc->header_location, NULL); } @@ -362,10 +360,15 @@ static int read_header(struct log_c *log) return 0; } -static inline int write_header(struct log_c *log) +static int _check_region_size(struct dm_target *ti, uint32_t region_size) { - header_to_disk(&log->header, log->disk_header); - return rw_header(log, WRITE); + if (region_size < 2 || region_size > ti->len) + return 0; + + if (!is_power_of_2(region_size)) + return 0; + + return 1; } /*---------------------------------------------------------------- @@ -403,8 +406,9 @@ static int create_log_context(struct dm_dirty_log *log, struct dm_target *ti, } } - if (sscanf(argv[0], "%u", ®ion_size) != 1) { - DMWARN("invalid region size string"); + if (sscanf(argv[0], "%u", ®ion_size) != 1 || + !_check_region_size(ti, region_size)) { + DMWARN("invalid region size %s", argv[0]); return -EINVAL; } @@ -453,8 +457,18 @@ static int create_log_context(struct dm_dirty_log *log, struct dm_target *ti, */ buf_size = dm_round_up((LOG_OFFSET << SECTOR_SHIFT) + bitset_size, ti->limits.hardsect_size); + + if (buf_size > dev->bdev->bd_inode->i_size) { + DMWARN("log device %s too small: need %llu bytes", + dev->name, (unsigned long long)buf_size); + kfree(lc); + return -EINVAL; + } + lc->header_location.count = buf_size >> SECTOR_SHIFT; + lc->io_req.mem.type = DM_IO_VMA; + lc->io_req.notify.fn = NULL; lc->io_req.client = dm_io_client_create(dm_div_up(buf_size, PAGE_SIZE)); if (IS_ERR(lc->io_req.client)) { @@ -467,10 +481,12 @@ static int create_log_context(struct dm_dirty_log *log, struct dm_target *ti, lc->disk_header = vmalloc(buf_size); if (!lc->disk_header) { DMWARN("couldn't allocate disk log buffer"); + dm_io_client_destroy(lc->io_req.client); kfree(lc); return -ENOMEM; } + lc->io_req.mem.ptr.vma = lc->disk_header; lc->clean_bits = (void *)lc->disk_header + (LOG_OFFSET << SECTOR_SHIFT); } @@ -482,6 +498,8 @@ static int create_log_context(struct dm_dirty_log *log, struct dm_target *ti, DMWARN("couldn't allocate sync bitset"); if (!dev) vfree(lc->clean_bits); + else + dm_io_client_destroy(lc->io_req.client); vfree(lc->disk_header); kfree(lc); return -ENOMEM; @@ -495,6 +513,8 @@ static int create_log_context(struct dm_dirty_log *log, struct dm_target *ti, vfree(lc->sync_bits); if (!dev) vfree(lc->clean_bits); + else + dm_io_client_destroy(lc->io_req.client); vfree(lc->disk_header); kfree(lc); return -ENOMEM; @@ -631,8 +651,10 @@ static int disk_resume(struct dm_dirty_log *log) /* set the correct number of regions in the header */ lc->header.nr_regions = lc->region_count; + header_to_disk(&lc->header, lc->disk_header); + /* write the new header */ - r = write_header(lc); + r = rw_header(lc, WRITE); if (r) { DMWARN("%s: Failed to write header on dirty region log device", lc->log_dev->name); @@ -682,7 +704,7 @@ static int disk_flush(struct dm_dirty_log *log) if (!lc->touched) return 0; - r = write_header(lc); + r = rw_header(lc, WRITE); if (r) fail_log_device(lc); else diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c index 3d7f4923cd13..345a26047ae0 100644 --- a/drivers/md/dm-mpath.c +++ b/drivers/md/dm-mpath.c @@ -1495,14 +1495,10 @@ static int __init dm_multipath_init(void) static void __exit dm_multipath_exit(void) { - int r; - destroy_workqueue(kmpath_handlerd); destroy_workqueue(kmultipathd); - r = dm_unregister_target(&multipath_target); - if (r < 0) - DMERR("target unregister failed %d", r); + dm_unregister_target(&multipath_target); kmem_cache_destroy(_mpio_cache); } diff --git a/drivers/md/dm-raid1.c b/drivers/md/dm-raid1.c index ec43f9fa4b2a..4d6bc101962e 100644 --- a/drivers/md/dm-raid1.c +++ b/drivers/md/dm-raid1.c @@ -197,9 +197,6 @@ static void fail_mirror(struct mirror *m, enum dm_raid1_error error_type) struct mirror_set *ms = m->ms; struct mirror *new; - if (!errors_handled(ms)) - return; - /* * error_count is used for nothing more than a * simple way to tell if a device has encountered @@ -210,6 +207,9 @@ static void fail_mirror(struct mirror *m, enum dm_raid1_error error_type) if (test_and_set_bit(error_type, &m->error_type)) return; + if (!errors_handled(ms)) + return; + if (m != get_default_mirror(ms)) goto out; @@ -808,12 +808,6 @@ static void free_context(struct mirror_set *ms, struct dm_target *ti, kfree(ms); } -static inline int _check_region_size(struct dm_target *ti, uint32_t size) -{ - return !(size % (PAGE_SIZE >> 9) || !is_power_of_2(size) || - size > ti->len); -} - static int get_mirror(struct mirror_set *ms, struct dm_target *ti, unsigned int mirror, char **argv) { @@ -872,12 +866,6 @@ static struct dm_dirty_log *create_dirty_log(struct dm_target *ti, return NULL; } - if (!_check_region_size(ti, dl->type->get_region_size(dl))) { - ti->error = "Invalid region size"; - dm_dirty_log_destroy(dl); - return NULL; - } - return dl; } @@ -1300,11 +1288,7 @@ static int __init dm_mirror_init(void) static void __exit dm_mirror_exit(void) { - int r; - - r = dm_unregister_target(&mirror_target); - if (r < 0) - DMERR("unregister failed %d", r); + dm_unregister_target(&mirror_target); } /* Module hooks */ diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c index 6c96db26b87c..a8005b43a06b 100644 --- a/drivers/md/dm-snap.c +++ b/drivers/md/dm-snap.c @@ -9,6 +9,7 @@ #include <linux/blkdev.h> #include <linux/ctype.h> #include <linux/device-mapper.h> +#include <linux/delay.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/kdev_t.h> @@ -735,7 +736,7 @@ static void snapshot_dtr(struct dm_target *ti) unregister_snapshot(s); while (atomic_read(&s->pending_exceptions_count)) - yield(); + msleep(1); /* * Ensure instructions in mempool_destroy aren't reordered * before atomic_read. @@ -888,10 +889,10 @@ static void pending_complete(struct dm_snap_pending_exception *pe, int success) /* * Check for conflicting reads. This is extremely improbable, - * so yield() is sufficient and there is no need for a wait queue. + * so msleep(1) is sufficient and there is no need for a wait queue. */ while (__chunk_is_tracked(s, pe->e.old_chunk)) - yield(); + msleep(1); /* * Add a proper exception, and remove the @@ -1469,17 +1470,10 @@ static int __init dm_snapshot_init(void) static void __exit dm_snapshot_exit(void) { - int r; - destroy_workqueue(ksnapd); - r = dm_unregister_target(&snapshot_target); - if (r) - DMERR("snapshot unregister failed %d", r); - - r = dm_unregister_target(&origin_target); - if (r) - DMERR("origin unregister failed %d", r); + dm_unregister_target(&snapshot_target); + dm_unregister_target(&origin_target); exit_origin_hash(); kmem_cache_destroy(pending_cache); diff --git a/drivers/md/dm-stripe.c b/drivers/md/dm-stripe.c index 9e4ef88d421e..41569bc60abc 100644 --- a/drivers/md/dm-stripe.c +++ b/drivers/md/dm-stripe.c @@ -337,9 +337,7 @@ int __init dm_stripe_init(void) void dm_stripe_exit(void) { - if (dm_unregister_target(&stripe_target)) - DMWARN("target unregistration failed"); - + dm_unregister_target(&stripe_target); destroy_workqueue(kstriped); return; diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c index 04e5fd742c2c..ebaaf72cd822 100644 --- a/drivers/md/dm-table.c +++ b/drivers/md/dm-table.c @@ -38,6 +38,8 @@ struct dm_table { sector_t *highs; struct dm_target *targets; + unsigned barriers_supported:1; + /* * Indicates the rw permissions for the new logical * device. This should be a combination of FMODE_READ @@ -227,6 +229,7 @@ int dm_table_create(struct dm_table **result, fmode_t mode, INIT_LIST_HEAD(&t->devices); atomic_set(&t->holders, 1); + t->barriers_supported = 1; if (!num_targets) num_targets = KEYS_PER_NODE; @@ -728,6 +731,10 @@ int dm_table_add_target(struct dm_table *t, const char *type, /* FIXME: the plan is to combine high here and then have * the merge fn apply the target level restrictions. */ combine_restrictions_low(&t->limits, &tgt->limits); + + if (!(tgt->type->features & DM_TARGET_SUPPORTS_BARRIERS)) + t->barriers_supported = 0; + return 0; bad: @@ -772,6 +779,12 @@ int dm_table_complete(struct dm_table *t) check_for_valid_limits(&t->limits); + /* + * We only support barriers if there is exactly one underlying device. + */ + if (!list_is_singular(&t->devices)) + t->barriers_supported = 0; + /* how many indexes will the btree have ? */ leaf_nodes = dm_div_up(t->num_targets, KEYS_PER_NODE); t->depth = 1 + int_log(leaf_nodes, CHILDREN_PER_NODE); @@ -986,6 +999,12 @@ struct mapped_device *dm_table_get_md(struct dm_table *t) return t->md; } +int dm_table_barrier_ok(struct dm_table *t) +{ + return t->barriers_supported; +} +EXPORT_SYMBOL(dm_table_barrier_ok); + EXPORT_SYMBOL(dm_vcalloc); EXPORT_SYMBOL(dm_get_device); EXPORT_SYMBOL(dm_put_device); diff --git a/drivers/md/dm-target.c b/drivers/md/dm-target.c index 835cf95b857f..7decf10006e4 100644 --- a/drivers/md/dm-target.c +++ b/drivers/md/dm-target.c @@ -130,26 +130,26 @@ int dm_register_target(struct target_type *t) return rv; } -int dm_unregister_target(struct target_type *t) +void dm_unregister_target(struct target_type *t) { struct tt_internal *ti; down_write(&_lock); if (!(ti = __find_target_type(t->name))) { - up_write(&_lock); - return -EINVAL; + DMCRIT("Unregistering unrecognised target: %s", t->name); + BUG(); } if (ti->use) { - up_write(&_lock); - return -ETXTBSY; + DMCRIT("Attempt to unregister target still in use: %s", + t->name); + BUG(); } list_del(&ti->list); kfree(ti); up_write(&_lock); - return 0; } /* @@ -187,8 +187,7 @@ int __init dm_target_init(void) void dm_target_exit(void) { - if (dm_unregister_target(&error_target)) - DMWARN("error target unregistration failed"); + dm_unregister_target(&error_target); } EXPORT_SYMBOL(dm_register_target); diff --git a/drivers/md/dm-zero.c b/drivers/md/dm-zero.c index cdbf126ec106..bbc97030c0c2 100644 --- a/drivers/md/dm-zero.c +++ b/drivers/md/dm-zero.c @@ -69,10 +69,7 @@ static int __init dm_zero_init(void) static void __exit dm_zero_exit(void) { - int r = dm_unregister_target(&zero_target); - - if (r < 0) - DMERR("unregister failed %d", r); + dm_unregister_target(&zero_target); } module_init(dm_zero_init) diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 343094c3feeb..b194bb606dba 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -32,6 +32,7 @@ static unsigned int _major = 0; static DEFINE_SPINLOCK(_minor_lock); /* + * For bio-based dm. * One of these is allocated per bio. */ struct dm_io { @@ -43,6 +44,7 @@ struct dm_io { }; /* + * For bio-based dm. * One of these is allocated per target within a bio. Hopefully * this will be simplified out one day. */ @@ -52,6 +54,27 @@ struct dm_target_io { union map_info info; }; +/* + * For request-based dm. + * One of these is allocated per request. + */ +struct dm_rq_target_io { + struct mapped_device *md; + struct dm_target *ti; + struct request *orig, clone; + int error; + union map_info info; +}; + +/* + * For request-based dm. + * One of these is allocated per bio. + */ +struct dm_rq_clone_bio_info { + struct bio *orig; + struct request *rq; +}; + DEFINE_TRACE(block_bio_complete); union map_info *dm_get_mapinfo(struct bio *bio) @@ -149,6 +172,8 @@ struct mapped_device { #define MIN_IOS 256 static struct kmem_cache *_io_cache; static struct kmem_cache *_tio_cache; +static struct kmem_cache *_rq_tio_cache; +static struct kmem_cache *_rq_bio_info_cache; static int __init local_init(void) { @@ -164,9 +189,17 @@ static int __init local_init(void) if (!_tio_cache) goto out_free_io_cache; + _rq_tio_cache = KMEM_CACHE(dm_rq_target_io, 0); + if (!_rq_tio_cache) + goto out_free_tio_cache; + + _rq_bio_info_cache = KMEM_CACHE(dm_rq_clone_bio_info, 0); + if (!_rq_bio_info_cache) + goto out_free_rq_tio_cache; + r = dm_uevent_init(); if (r) - goto out_free_tio_cache; + goto out_free_rq_bio_info_cache; _major = major; r = register_blkdev(_major, _name); @@ -180,6 +213,10 @@ static int __init local_init(void) out_uevent_exit: dm_uevent_exit(); +out_free_rq_bio_info_cache: + kmem_cache_destroy(_rq_bio_info_cache); +out_free_rq_tio_cache: + kmem_cache_destroy(_rq_tio_cache); out_free_tio_cache: kmem_cache_destroy(_tio_cache); out_free_io_cache: @@ -190,6 +227,8 @@ out_free_io_cache: static void local_exit(void) { + kmem_cache_destroy(_rq_bio_info_cache); + kmem_cache_destroy(_rq_tio_cache); kmem_cache_destroy(_tio_cache); kmem_cache_destroy(_io_cache); unregister_blkdev(_major, _name); @@ -796,7 +835,11 @@ static int __split_bio(struct mapped_device *md, struct bio *bio) ci.map = dm_get_table(md); if (unlikely(!ci.map)) return -EIO; - + if (unlikely(bio_barrier(bio) && !dm_table_barrier_ok(ci.map))) { + dm_table_put(ci.map); + bio_endio(bio, -EOPNOTSUPP); + return 0; + } ci.md = md; ci.bio = bio; ci.io = alloc_io(md); @@ -880,15 +923,6 @@ static int dm_request(struct request_queue *q, struct bio *bio) struct mapped_device *md = q->queuedata; int cpu; - /* - * There is no use in forwarding any barrier request since we can't - * guarantee it is (or can be) handled by the targets correctly. - */ - if (unlikely(bio_barrier(bio))) { - bio_endio(bio, -EOPNOTSUPP); - return 0; - } - down_read(&md->io_lock); cpu = part_stat_lock(); @@ -1330,8 +1364,8 @@ void dm_put(struct mapped_device *md) dm_table_presuspend_targets(map); dm_table_postsuspend_targets(map); } - __unbind(md); dm_table_put(map); + __unbind(md); free_dev(md); } } diff --git a/drivers/md/dm.h b/drivers/md/dm.h index 0ade60cdef42..5b5d08ba9e92 100644 --- a/drivers/md/dm.h +++ b/drivers/md/dm.h @@ -51,6 +51,7 @@ int dm_table_any_congested(struct dm_table *t, int bdi_bits); * To check the return value from dm_table_find_target(). */ #define dm_target_is_valid(t) ((t)->table) +int dm_table_barrier_ok(struct dm_table *t); /*----------------------------------------------------------------- * A registry of target types. diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h index c17fd334e574..79c4000442d4 100644 --- a/include/linux/device-mapper.h +++ b/include/linux/device-mapper.h @@ -45,6 +45,8 @@ typedef void (*dm_dtr_fn) (struct dm_target *ti); */ typedef int (*dm_map_fn) (struct dm_target *ti, struct bio *bio, union map_info *map_context); +typedef int (*dm_map_request_fn) (struct dm_target *ti, struct request *clone, + union map_info *map_context); /* * Returns: @@ -57,6 +59,9 @@ typedef int (*dm_map_fn) (struct dm_target *ti, struct bio *bio, typedef int (*dm_endio_fn) (struct dm_target *ti, struct bio *bio, int error, union map_info *map_context); +typedef int (*dm_request_endio_fn) (struct dm_target *ti, + struct request *clone, int error, + union map_info *map_context); typedef void (*dm_flush_fn) (struct dm_target *ti); typedef void (*dm_presuspend_fn) (struct dm_target *ti); @@ -75,6 +80,13 @@ typedef int (*dm_ioctl_fn) (struct dm_target *ti, unsigned int cmd, typedef int (*dm_merge_fn) (struct dm_target *ti, struct bvec_merge_data *bvm, struct bio_vec *biovec, int max_size); +/* + * Returns: + * 0: The target can handle the next I/O immediately. + * 1: The target can't handle the next I/O immediately. + */ +typedef int (*dm_busy_fn) (struct dm_target *ti); + void dm_error(const char *message); /* @@ -100,14 +112,23 @@ void dm_put_device(struct dm_target *ti, struct dm_dev *d); /* * Information about a target type */ + +/* + * Target features + */ +#define DM_TARGET_SUPPORTS_BARRIERS 0x00000001 + struct target_type { + uint64_t features; const char *name; struct module *module; unsigned version[3]; dm_ctr_fn ctr; dm_dtr_fn dtr; dm_map_fn map; + dm_map_request_fn map_rq; dm_endio_fn end_io; + dm_request_endio_fn rq_end_io; dm_flush_fn flush; dm_presuspend_fn presuspend; dm_postsuspend_fn postsuspend; @@ -117,6 +138,7 @@ struct target_type { dm_message_fn message; dm_ioctl_fn ioctl; dm_merge_fn merge; + dm_busy_fn busy; }; struct io_restrictions { @@ -157,7 +179,7 @@ struct dm_target { }; int dm_register_target(struct target_type *t); -int dm_unregister_target(struct target_type *t); +void dm_unregister_target(struct target_type *t); /*----------------------------------------------------------------- @@ -276,6 +298,9 @@ void *dm_vcalloc(unsigned long nmemb, unsigned long elem_size); *---------------------------------------------------------------*/ #define DM_NAME "device-mapper" +#define DMCRIT(f, arg...) \ + printk(KERN_CRIT DM_NAME ": " DM_MSG_PREFIX ": " f "\n", ## arg) + #define DMERR(f, arg...) \ printk(KERN_ERR DM_NAME ": " DM_MSG_PREFIX ": " f "\n", ## arg) #define DMERR_LIMIT(f, arg...) \ |