X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fmd%2Fdm.c;h=7f1af5231ef76b17329c98ae66d24a2d64dc53a8;hb=6a77f38946aaee1cd85eeec6cf4229b204c15071;hp=01794ed391a29a2794f4c3d983ad9b4635264567;hpb=9bf4aaab3e101692164d49b7ca357651eb691cb6;p=linux-2.6.git diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 01794ed39..7f1af5231 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2001, 2002 Sistina Software (UK) Limited. + * Copyright (C) 2004 Red Hat, Inc. All rights reserved. * * This file is released under the GPL. */ @@ -15,15 +16,13 @@ #include #include #include +#include static const char *_name = DM_NAME; static unsigned int major = 0; static unsigned int _major = 0; -static int realloc_minor_bits(unsigned long requested_minor); -static void free_minor_bits(void); - /* * One of these is allocated per bio. */ @@ -61,6 +60,8 @@ struct mapped_device { request_queue_t *queue; struct gendisk *disk; + void *interface_ptr; + /* * A list of ios that arrived while we were suspended. */ @@ -113,19 +114,11 @@ static int __init local_init(void) return -ENOMEM; } - r = realloc_minor_bits(1024); - if (r < 0) { - kmem_cache_destroy(_tio_cache); - kmem_cache_destroy(_io_cache); - return r; - } - _major = major; r = register_blkdev(_major, _name); if (r < 0) { kmem_cache_destroy(_tio_cache); kmem_cache_destroy(_io_cache); - free_minor_bits(); return r; } @@ -139,7 +132,6 @@ static void local_exit(void) { kmem_cache_destroy(_tio_cache); kmem_cache_destroy(_io_cache); - free_minor_bits(); if (unregister_blkdev(_major, _name) < 0) DMERR("devfs_unregister_blkdev failed"); @@ -149,23 +141,20 @@ static void local_exit(void) DMINFO("cleaned up"); } -/* - * We have a lot of init/exit functions, so it seems easier to - * store them in an array. The disposable macro 'xx' - * expands a prefix into a pair of function names. - */ -static struct { - int (*init) (void); - void (*exit) (void); - -} _inits[] = { -#define xx(n) {n ## _init, n ## _exit}, - xx(local) - xx(dm_target) - xx(dm_linear) - xx(dm_stripe) - xx(dm_interface) -#undef xx +int (*_inits[])(void) __initdata = { + local_init, + dm_target_init, + dm_linear_init, + dm_stripe_init, + dm_interface_init, +}; + +void (*_exits[])(void) = { + local_exit, + dm_target_exit, + dm_linear_exit, + dm_stripe_exit, + dm_interface_exit, }; static int __init dm_init(void) @@ -175,7 +164,7 @@ static int __init dm_init(void) int r, i; for (i = 0; i < count; i++) { - r = _inits[i].init(); + r = _inits[i](); if (r) goto bad; } @@ -184,17 +173,17 @@ static int __init dm_init(void) bad: while (i--) - _inits[i].exit(); + _exits[i](); return r; } static void __exit dm_exit(void) { - int i = ARRAY_SIZE(_inits); + int i = ARRAY_SIZE(_exits); while (i--) - _inits[i].exit(); + _exits[i](); } /* @@ -342,8 +331,8 @@ static sector_t max_io_len(struct mapped_device *md, */ if (ti->split_io) { sector_t boundary; - boundary = dm_round_up(offset + 1, ti->split_io) - offset; - + boundary = ((offset + ti->split_io) & ~(ti->split_io - 1)) + - offset; if (len > boundary) len = boundary; } @@ -597,6 +586,21 @@ static int dm_request(request_queue_t *q, struct bio *bio) return 0; } +static int dm_flush_all(request_queue_t *q, struct gendisk *disk, + sector_t *error_sector) +{ + struct mapped_device *md = q->queuedata; + struct dm_table *map = dm_get_table(md); + int ret = -ENXIO; + + if (map) { + ret = dm_table_flush_all(md->map); + dm_table_put(map); + } + + return ret; +} + static void dm_unplug_all(request_queue_t *q) { struct mapped_device *md = q->queuedata; @@ -624,109 +628,86 @@ static int dm_any_congested(void *congested_data, int bdi_bits) } /*----------------------------------------------------------------- - * A bitset is used to keep track of allocated minor numbers. + * An IDR is used to keep track of allocated minor numbers. *---------------------------------------------------------------*/ static DECLARE_MUTEX(_minor_lock); -static unsigned long *_minor_bits = NULL; -static unsigned long _max_minors = 0; - -#define MINORS_SIZE(minors) ((minors / BITS_PER_LONG) * sizeof(unsigned long)) - -static int realloc_minor_bits(unsigned long requested_minor) -{ - unsigned long max_minors; - unsigned long *minor_bits, *tmp; - - if (requested_minor < _max_minors) - return -EINVAL; - - /* Round up the requested minor to the next power-of-2. */ - max_minors = 1 << fls(requested_minor - 1); - if (max_minors > (1 << MINORBITS)) - return -EINVAL; - - minor_bits = kmalloc(MINORS_SIZE(max_minors), GFP_KERNEL); - if (!minor_bits) - return -ENOMEM; - memset(minor_bits, 0, MINORS_SIZE(max_minors)); - - /* Copy the existing bit-set to the new one. */ - if (_minor_bits) - memcpy(minor_bits, _minor_bits, MINORS_SIZE(_max_minors)); - - tmp = _minor_bits; - _minor_bits = minor_bits; - _max_minors = max_minors; - if (tmp) - kfree(tmp); - - return 0; -} - -static void free_minor_bits(void) -{ - down(&_minor_lock); - kfree(_minor_bits); - _minor_bits = NULL; - _max_minors = 0; - up(&_minor_lock); -} +static DEFINE_IDR(_minor_idr); static void free_minor(unsigned int minor) { down(&_minor_lock); - if (minor < _max_minors) - clear_bit(minor, _minor_bits); + idr_remove(&_minor_idr, minor); up(&_minor_lock); } /* * See if the device with a specific minor # is free. */ -static int specific_minor(unsigned int minor) +static int specific_minor(struct mapped_device *md, unsigned int minor) { - int r = 0; + int r, m; - if (minor > (1 << MINORBITS)) + if (minor >= (1 << MINORBITS)) return -EINVAL; down(&_minor_lock); - if (minor >= _max_minors) { - r = realloc_minor_bits(minor); - if (r) { - up(&_minor_lock); - return r; - } + + if (idr_find(&_minor_idr, minor)) { + r = -EBUSY; + goto out; + } + + r = idr_pre_get(&_minor_idr, GFP_KERNEL); + if (!r) { + r = -ENOMEM; + goto out; } - if (test_and_set_bit(minor, _minor_bits)) + r = idr_get_new_above(&_minor_idr, md, minor, &m); + if (r) { + goto out; + } + + if (m != minor) { + idr_remove(&_minor_idr, m); r = -EBUSY; - up(&_minor_lock); + goto out; + } +out: + up(&_minor_lock); return r; } -static int next_free_minor(unsigned int *minor) +static int next_free_minor(struct mapped_device *md, unsigned int *minor) { int r; unsigned int m; down(&_minor_lock); - m = find_first_zero_bit(_minor_bits, _max_minors); - if (m >= _max_minors) { - r = realloc_minor_bits(_max_minors * 2); - if (r) { - up(&_minor_lock); - return r; - } - m = find_first_zero_bit(_minor_bits, _max_minors); + + r = idr_pre_get(&_minor_idr, GFP_KERNEL); + if (!r) { + r = -ENOMEM; + goto out; + } + + r = idr_get_new(&_minor_idr, md, &m); + if (r) { + goto out; + } + + if (m >= (1 << MINORBITS)) { + idr_remove(&_minor_idr, m); + r = -ENOSPC; + goto out; } - set_bit(m, _minor_bits); *minor = m; - up(&_minor_lock); - return 0; +out: + up(&_minor_lock); + return r; } static struct block_device_operations dm_blk_dops; @@ -745,7 +726,7 @@ static struct mapped_device *alloc_dev(unsigned int minor, int persistent) } /* get a minor number for the dev */ - r = persistent ? specific_minor(minor) : next_free_minor(&minor); + r = persistent ? specific_minor(md, minor) : next_free_minor(md, &minor); if (r < 0) goto bad1; @@ -764,6 +745,7 @@ static struct mapped_device *alloc_dev(unsigned int minor, int persistent) md->queue->backing_dev_info.congested_data = md; blk_queue_make_request(md->queue, dm_request); md->queue->unplug_fn = dm_unplug_all; + md->queue->issue_flush_fn = dm_flush_all; md->io_pool = mempool_create(MIN_IOS, mempool_alloc_slab, mempool_free_slab, _io_cache); @@ -823,7 +805,7 @@ static void event_callback(void *context) { struct mapped_device *md = (struct mapped_device *) context; - atomic_inc(&md->event_nr);; + atomic_inc(&md->event_nr); wake_up(&md->eventq); } @@ -901,6 +883,32 @@ int dm_create_with_minor(unsigned int minor, struct mapped_device **result) return create_aux(minor, 1, result); } +void *dm_get_mdptr(dev_t dev) +{ + struct mapped_device *md; + void *mdptr = NULL; + unsigned minor = MINOR(dev); + + if (MAJOR(dev) != _major || minor >= (1 << MINORBITS)) + return NULL; + + down(&_minor_lock); + + md = idr_find(&_minor_idr, minor); + + if (md && (dm_disk(md)->first_minor == minor)) + mdptr = md->interface_ptr; + + up(&_minor_lock); + + return mdptr; +} + +void dm_set_mdptr(struct mapped_device *md, void *ptr) +{ + md->interface_ptr = ptr; +} + void dm_get(struct mapped_device *md) { atomic_inc(&md->holders); @@ -911,8 +919,10 @@ void dm_put(struct mapped_device *md) struct dm_table *map = dm_get_table(md); if (atomic_dec_and_test(&md->holders)) { - if (!test_bit(DMF_SUSPENDED, &md->flags) && map) - dm_table_suspend_targets(map); + if (!test_bit(DMF_SUSPENDED, &md->flags) && map) { + dm_table_presuspend_targets(map); + dm_table_postsuspend_targets(map); + } __unbind(md); free_dev(md); } @@ -1024,7 +1034,11 @@ int dm_suspend(struct mapped_device *md) return -EINVAL; } + map = dm_get_table(md); + if (map) + dm_table_presuspend_targets(map); __lock_fs(md); + up_read(&md->lock); /* @@ -1047,7 +1061,6 @@ int dm_suspend(struct mapped_device *md) up_write(&md->lock); /* unplug */ - map = dm_get_table(md); if (map) { dm_table_unplug_all(map); dm_table_put(map); @@ -1082,7 +1095,7 @@ int dm_suspend(struct mapped_device *md) map = dm_get_table(md); if (map) - dm_table_suspend_targets(map); + dm_table_postsuspend_targets(map); dm_table_put(map); up_write(&md->lock); @@ -1160,5 +1173,5 @@ module_exit(dm_exit); module_param(major, uint, 0); MODULE_PARM_DESC(major, "The major number of the device mapper"); MODULE_DESCRIPTION(DM_NAME " driver"); -MODULE_AUTHOR("Joe Thornber "); +MODULE_AUTHOR("Joe Thornber "); MODULE_LICENSE("GPL");