X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fmd%2Fdm.c;h=7f1af5231ef76b17329c98ae66d24a2d64dc53a8;hb=6a77f38946aaee1cd85eeec6cf4229b204c15071;hp=bdfcbe09d4d5cf96a3450a66c6df6b1b45d71ee8;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/drivers/md/dm.c b/drivers/md/dm.c index bdfcbe09d..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,9 +16,9 @@ #include #include #include +#include static const char *_name = DM_NAME; -#define MAX_DEVICES 1024 static unsigned int major = 0; static unsigned int _major = 0; @@ -59,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. */ @@ -80,7 +83,7 @@ struct mapped_device { /* * Event handling. */ - uint32_t event_nr; + atomic_t event_nr; wait_queue_head_t eventq; /* @@ -93,7 +96,7 @@ struct mapped_device { static kmem_cache_t *_io_cache; static kmem_cache_t *_tio_cache; -static __init int local_init(void) +static int __init local_init(void) { int r; @@ -138,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) @@ -164,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; } @@ -173,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](); } /* @@ -331,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; } @@ -369,6 +369,7 @@ static void __map_bio(struct dm_target *ti, struct bio *clone, struct dm_io *io = tio->io; free_tio(tio->io->md, tio); dec_pending(io, -EIO); + bio_put(clone); } } @@ -393,7 +394,7 @@ static struct bio *split_bvec(struct bio *bio, sector_t sector, struct bio_vec *bv = bio->bi_io_vec + idx; clone = bio_alloc(GFP_NOIO, 1); - memcpy(clone->bi_io_vec, bv, sizeof(*bv)); + *clone->bi_io_vec = *bv; clone->bi_sector = sector; clone->bi_bdev = bio->bi_bdev; @@ -585,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; @@ -612,56 +628,90 @@ 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 spinlock_t _minor_lock = SPIN_LOCK_UNLOCKED; -static unsigned long _minor_bits[MAX_DEVICES / BITS_PER_LONG]; +static DECLARE_MUTEX(_minor_lock); +static DEFINE_IDR(_minor_idr); static void free_minor(unsigned int minor) { - spin_lock(&_minor_lock); - clear_bit(minor, _minor_bits); - spin_unlock(&_minor_lock); + down(&_minor_lock); + 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 = -EBUSY; + int r, m; - if (minor >= MAX_DEVICES) { - DMWARN("request for a mapped_device beyond MAX_DEVICES (%d)", - MAX_DEVICES); + if (minor >= (1 << MINORBITS)) return -EINVAL; + + down(&_minor_lock); + + if (idr_find(&_minor_idr, minor)) { + r = -EBUSY; + goto out; } - spin_lock(&_minor_lock); - if (!test_and_set_bit(minor, _minor_bits)) - r = 0; - spin_unlock(&_minor_lock); + r = idr_pre_get(&_minor_idr, GFP_KERNEL); + if (!r) { + r = -ENOMEM; + goto out; + } + + r = idr_get_new_above(&_minor_idr, md, minor, &m); + if (r) { + goto out; + } + + if (m != minor) { + idr_remove(&_minor_idr, m); + r = -EBUSY; + 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 = -EBUSY; + int r; unsigned int m; - spin_lock(&_minor_lock); - m = find_first_zero_bit(_minor_bits, MAX_DEVICES); - if (m != MAX_DEVICES) { - set_bit(m, _minor_bits); - *minor = m; - r = 0; + down(&_minor_lock); + + 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; } - spin_unlock(&_minor_lock); + if (m >= (1 << MINORBITS)) { + idr_remove(&_minor_idr, m); + r = -ENOSPC; + goto out; + } + + *minor = m; + +out: + up(&_minor_lock); return r; } +static struct block_device_operations dm_blk_dops; + /* * Allocate and initialise a blank device with a given minor. */ @@ -676,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; @@ -684,6 +734,7 @@ static struct mapped_device *alloc_dev(unsigned int minor, int persistent) init_rwsem(&md->lock); rwlock_init(&md->map_lock); atomic_set(&md->holders, 1); + atomic_set(&md->event_nr, 0); md->queue = blk_alloc_queue(GFP_KERNEL); if (!md->queue) @@ -694,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); @@ -753,10 +805,8 @@ static void event_callback(void *context) { struct mapped_device *md = (struct mapped_device *) context; - down_write(&md->lock); - md->event_nr++; + atomic_inc(&md->event_nr); wake_up(&md->eventq); - up_write(&md->lock); } static void __set_size(struct gendisk *disk, sector_t size) @@ -833,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); @@ -843,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); } @@ -956,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); /* @@ -979,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); @@ -1014,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); @@ -1054,35 +1135,13 @@ int dm_resume(struct mapped_device *md) *---------------------------------------------------------------*/ uint32_t dm_get_event_nr(struct mapped_device *md) { - uint32_t r; - - down_read(&md->lock); - r = md->event_nr; - up_read(&md->lock); - - return r; + return atomic_read(&md->event_nr); } -int dm_add_wait_queue(struct mapped_device *md, wait_queue_t *wq, - uint32_t event_nr) +int dm_wait_event(struct mapped_device *md, int event_nr) { - down_write(&md->lock); - if (event_nr != md->event_nr) { - up_write(&md->lock); - return 1; - } - - add_wait_queue(&md->eventq, wq); - up_write(&md->lock); - - return 0; -} - -void dm_remove_wait_queue(struct mapped_device *md, wait_queue_t *wq) -{ - down_write(&md->lock); - remove_wait_queue(&md->eventq, wq); - up_write(&md->lock); + return wait_event_interruptible(md->eventq, + (event_nr != atomic_read(&md->event_nr))); } /* @@ -1099,7 +1158,7 @@ int dm_suspended(struct mapped_device *md) return test_bit(DMF_SUSPENDED, &md->flags); } -struct block_device_operations dm_blk_dops = { +static struct block_device_operations dm_blk_dops = { .open = dm_blk_open, .release = dm_blk_close, .owner = THIS_MODULE @@ -1114,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");