📄 dm.c
字号:
return 0; /* deferred successfully */ /* * We're in a while loop, because someone could suspend * before we get to the following read lock. */ down_read(&md->lock); } __split_bio(md, bio); up_read(&md->lock); 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; struct dm_table *map = dm_get_table(md); if (map) { dm_table_unplug_all(map); dm_table_put(map); }}static int dm_any_congested(void *congested_data, int bdi_bits){ int r; struct mapped_device *md = (struct mapped_device *) congested_data; struct dm_table *map = dm_get_table(md); if (!map || test_bit(DMF_BLOCK_IO, &md->flags)) r = bdi_bits; else r = dm_table_any_congested(map, bdi_bits); dm_table_put(map); return r;}/*----------------------------------------------------------------- * An IDR is used to keep track of allocated minor numbers. *---------------------------------------------------------------*/static DECLARE_MUTEX(_minor_lock);static DEFINE_IDR(_minor_idr);static void free_minor(unsigned int minor){ 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){ int r, m; if (minor >= (1 << MINORBITS)) return -EINVAL; down(&_minor_lock); if (idr_find(&_minor_idr, minor)) { r = -EBUSY; goto out; } r = idr_pre_get(&_minor_idr, GFP_KERNEL); if (!r) { r = -ENOMEM; goto out; } r = idr_get_new_above(&_minor_idr, specific_minor, 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){ int r; unsigned int m; down(&_minor_lock); r = idr_pre_get(&_minor_idr, GFP_KERNEL); if (!r) { r = -ENOMEM; goto out; } r = idr_get_new(&_minor_idr, next_free_minor, &m); if (r) { goto out; } 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. */static struct mapped_device *alloc_dev(unsigned int minor, int persistent){ int r; struct mapped_device *md = kmalloc(sizeof(*md), GFP_KERNEL); if (!md) { DMWARN("unable to allocate device, out of memory."); return NULL; } /* get a minor number for the dev */ r = persistent ? specific_minor(minor) : next_free_minor(&minor); if (r < 0) goto bad1; memset(md, 0, sizeof(*md)); 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) goto bad1; md->queue->queuedata = md; md->queue->backing_dev_info.congested_fn = dm_any_congested; 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); 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 bad4; md->disk->major = _major; md->disk->first_minor = minor; md->disk->fops = &dm_blk_dops; md->disk->queue = md->queue; md->disk->private_data = md; sprintf(md->disk->disk_name, "dm-%d", minor); add_disk(md->disk); atomic_set(&md->pending, 0); init_waitqueue_head(&md->wait); init_waitqueue_head(&md->eventq); return md; bad4: mempool_destroy(md->tio_pool); bad3: mempool_destroy(md->io_pool); bad2: blk_put_queue(md->queue); free_minor(minor); bad1: kfree(md); return NULL;}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); blk_put_queue(md->queue); kfree(md);}/* * Bind a table to the device. */static void event_callback(void *context){ struct mapped_device *md = (struct mapped_device *) context; atomic_inc(&md->event_nr);; wake_up(&md->eventq);}static void __set_size(struct gendisk *disk, sector_t size){ struct block_device *bdev; set_capacity(disk, size); bdev = bdget_disk(disk, 0); if (bdev) { down(&bdev->bd_inode->i_sem); i_size_write(bdev->bd_inode, (loff_t)size << SECTOR_SHIFT); up(&bdev->bd_inode->i_sem); bdput(bdev); }}static int __bind(struct mapped_device *md, struct dm_table *t){ request_queue_t *q = md->queue; sector_t size; size = dm_table_get_size(t); __set_size(md->disk, size); if (size == 0) return 0; write_lock(&md->map_lock); md->map = t; write_unlock(&md->map_lock); dm_table_get(t); dm_table_event_callback(md->map, event_callback, md); dm_table_set_restrictions(t, q); return 0;}static void __unbind(struct mapped_device *md){ struct dm_table *map = md->map; if (!map) return; dm_table_event_callback(map, NULL, NULL); write_lock(&md->map_lock); md->map = NULL; write_unlock(&md->map_lock); dm_table_put(map);}/* * Constructor for a new device. */static int create_aux(unsigned int minor, int persistent, struct mapped_device **result){ struct mapped_device *md; md = alloc_dev(minor, persistent); if (!md) return -ENXIO; *result = md; return 0;}int dm_create(struct mapped_device **result){ return create_aux(0, 0, result);}int dm_create_with_minor(unsigned int minor, struct mapped_device **result){ return create_aux(minor, 1, result);}void dm_get(struct mapped_device *md){ atomic_inc(&md->holders);}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); __unbind(md); free_dev(md); } dm_table_put(map);}/* * Process the deferred bios */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; __split_bio(md, c); c = n; }}/* * Swap in a new table (destroying old one). */int dm_swap_table(struct mapped_device *md, struct dm_table *table){ int r; down_write(&md->lock); /* device must be suspended */ if (!test_bit(DMF_SUSPENDED, &md->flags)) { up_write(&md->lock); return -EPERM; } __unbind(md); r = __bind(md, table); if (r) return r; up_write(&md->lock); return 0;}/* * Functions to lock and unlock any filesystem running on the * device. */static int __lock_fs(struct mapped_device *md){ struct block_device *bdev; if (test_and_set_bit(DMF_FS_LOCKED, &md->flags)) return 0; bdev = bdget_disk(md->disk, 0); if (!bdev) { DMWARN("bdget failed in __lock_fs"); return -ENOMEM; } WARN_ON(md->frozen_sb); md->frozen_sb = freeze_bdev(bdev); /* don't bdput right now, we don't want the bdev * to go away while it is locked. We'll bdput * in __unlock_fs */ return 0;}static int __unlock_fs(struct mapped_device *md){ struct block_device *bdev; if (!test_and_clear_bit(DMF_FS_LOCKED, &md->flags)) return 0; bdev = bdget_disk(md->disk, 0); if (!bdev) { DMWARN("bdget failed in __unlock_fs"); return -ENOMEM; } thaw_bdev(bdev, md->frozen_sb); md->frozen_sb = NULL; bdput(bdev); bdput(bdev); return 0;}/* * We need to be able to change a mapping table under a mounted * filesystem. For example we might want to move some data in * the background. Before the table can be swapped with * dm_bind_table, dm_suspend must be called to flush any in * flight bios and ensure that any further io gets deferred. */int dm_suspend(struct mapped_device *md){ struct dm_table *map; DECLARE_WAITQUEUE(wait, current); /* Flush I/O to the device. */ down_read(&md->lock); if (test_bit(DMF_BLOCK_IO, &md->flags)) { up_read(&md->lock); return -EINVAL; } __lock_fs(md); up_read(&md->lock); /* * First we set the BLOCK_IO flag so no more ios will be * mapped. */ down_write(&md->lock); if (test_bit(DMF_BLOCK_IO, &md->flags)) { /* * If we get here we know another thread is * trying to suspend as well, so we leave the fs * locked for this thread. */ up_write(&md->lock); return -EINVAL; } set_bit(DMF_BLOCK_IO, &md->flags); add_wait_queue(&md->wait, &wait); up_write(&md->lock); /* unplug */ map = dm_get_table(md); if (map) { dm_table_unplug_all(map); dm_table_put(map); } /* * Then we wait for the already mapped ios to * complete. */ while (1) { set_current_state(TASK_INTERRUPTIBLE); if (!atomic_read(&md->pending) || signal_pending(current)) break; io_schedule(); } set_current_state(TASK_RUNNING); down_write(&md->lock); remove_wait_queue(&md->wait, &wait); /* were we interrupted ? */ if (atomic_read(&md->pending)) { __unlock_fs(md); clear_bit(DMF_BLOCK_IO, &md->flags); up_write(&md->lock); return -EINTR; } set_bit(DMF_SUSPENDED, &md->flags); map = dm_get_table(md); if (map) dm_table_suspend_targets(map); dm_table_put(map); up_write(&md->lock); return 0;}int dm_resume(struct mapped_device *md){ struct bio *def; struct dm_table *map = dm_get_table(md); down_write(&md->lock); if (!map || !test_bit(DMF_SUSPENDED, &md->flags) || !dm_table_get_size(map)) { up_write(&md->lock); dm_table_put(map); return -EINVAL; } dm_table_resume_targets(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); __unlock_fs(md); dm_table_unplug_all(map); dm_table_put(map); return 0;}/*----------------------------------------------------------------- * Event notification. *---------------------------------------------------------------*/uint32_t dm_get_event_nr(struct mapped_device *md){ return atomic_read(&md->event_nr);}int dm_wait_event(struct mapped_device *md, int event_nr){ return wait_event_interruptible(md->eventq, (event_nr != atomic_read(&md->event_nr)));}/* * The gendisk is only valid as long as you have a reference * count on 'md'. */struct gendisk *dm_disk(struct mapped_device *md){ return md->disk;}int dm_suspended(struct mapped_device *md){ return test_bit(DMF_SUSPENDED, &md->flags);}static struct block_device_operations dm_blk_dops = { .open = dm_blk_open, .release = dm_blk_close, .owner = THIS_MODULE};/* * module hooks */module_init(dm_init);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 <thornber@sistina.com>");MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -