📄 dm.c
字号:
static void unlock_fs(struct mapped_device *md);static void free_dev(struct mapped_device *md){ int minor = md->disk->first_minor; if (md->suspended_bdev) { unlock_fs(md); bdput(md->suspended_bdev); } mempool_destroy(md->tio_pool); mempool_destroy(md->io_pool); bioset_free(md->bs); del_gendisk(md->disk); free_minor(minor); spin_lock(&_minor_lock); md->disk->private_data = NULL; spin_unlock(&_minor_lock); put_disk(md->disk); blk_cleanup_queue(md->queue); module_put(THIS_MODULE); kfree(md);}/* * Bind a table to the device. */static void event_callback(void *context){ unsigned long flags; LIST_HEAD(uevents); struct mapped_device *md = (struct mapped_device *) context; spin_lock_irqsave(&md->uevent_lock, flags); list_splice_init(&md->uevent_list, &uevents); spin_unlock_irqrestore(&md->uevent_lock, flags); dm_send_uevents(&uevents, &md->disk->kobj); atomic_inc(&md->event_nr); wake_up(&md->eventq);}static void __set_size(struct mapped_device *md, sector_t size){ set_capacity(md->disk, size); mutex_lock(&md->suspended_bdev->bd_inode->i_mutex); i_size_write(md->suspended_bdev->bd_inode, (loff_t)size << SECTOR_SHIFT); mutex_unlock(&md->suspended_bdev->bd_inode->i_mutex);}static int __bind(struct mapped_device *md, struct dm_table *t){ struct request_queue *q = md->queue; sector_t size; size = dm_table_get_size(t); /* * Wipe any geometry if the size of the table changed. */ if (size != get_capacity(md->disk)) memset(&md->geometry, 0, sizeof(md->geometry)); if (md->suspended_bdev) __set_size(md, size); if (size == 0) return 0; dm_table_get(t); dm_table_event_callback(t, event_callback, md); write_lock(&md->map_lock); md->map = t; dm_table_set_restrictions(t, q); write_unlock(&md->map_lock); 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. */int dm_create(int minor, struct mapped_device **result){ struct mapped_device *md; md = alloc_dev(minor); if (!md) return -ENXIO; *result = md; return 0;}static struct mapped_device *dm_find_md(dev_t dev){ struct mapped_device *md; unsigned minor = MINOR(dev); if (MAJOR(dev) != _major || minor >= (1 << MINORBITS)) return NULL; spin_lock(&_minor_lock); md = idr_find(&_minor_idr, minor); if (md && (md == MINOR_ALLOCED || (dm_disk(md)->first_minor != minor) || test_bit(DMF_FREEING, &md->flags))) { md = NULL; goto out; }out: spin_unlock(&_minor_lock); return md;}struct mapped_device *dm_get_md(dev_t dev){ struct mapped_device *md = dm_find_md(dev); if (md) dm_get(md); return md;}void *dm_get_mdptr(struct mapped_device *md){ return md->interface_ptr;}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);}const char *dm_device_name(struct mapped_device *md){ return md->name;}EXPORT_SYMBOL_GPL(dm_device_name);void dm_put(struct mapped_device *md){ struct dm_table *map; BUG_ON(test_bit(DMF_FREEING, &md->flags)); if (atomic_dec_and_lock(&md->holders, &_minor_lock)) { map = dm_get_table(md); idr_replace(&_minor_idr, MINOR_ALLOCED, dm_disk(md)->first_minor); set_bit(DMF_FREEING, &md->flags); spin_unlock(&_minor_lock); if (!dm_suspended(md)) { dm_table_presuspend_targets(map); dm_table_postsuspend_targets(map); } __unbind(md); dm_table_put(map); free_dev(md); }}EXPORT_SYMBOL_GPL(dm_put);/* * 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; if (__split_bio(md, c)) bio_io_error(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 = -EINVAL; down(&md->suspend_lock); /* device must be suspended */ if (!dm_suspended(md)) goto out; /* without bdev, the device size cannot be changed */ if (!md->suspended_bdev) if (get_capacity(md->disk) != dm_table_get_size(table)) goto out; __unbind(md); r = __bind(md, table);out: up(&md->suspend_lock); return r;}/* * Functions to lock and unlock any filesystem running on the * device. */static int lock_fs(struct mapped_device *md){ int r; WARN_ON(md->frozen_sb); md->frozen_sb = freeze_bdev(md->suspended_bdev); if (IS_ERR(md->frozen_sb)) { r = PTR_ERR(md->frozen_sb); md->frozen_sb = NULL; return r; } set_bit(DMF_FROZEN, &md->flags); /* don't bdput right now, we don't want the bdev * to go away while it is locked. */ return 0;}static void unlock_fs(struct mapped_device *md){ if (!test_bit(DMF_FROZEN, &md->flags)) return; thaw_bdev(md->suspended_bdev, md->frozen_sb); md->frozen_sb = NULL; clear_bit(DMF_FROZEN, &md->flags);}/* * 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, unsigned suspend_flags){ struct dm_table *map = NULL; unsigned long flags; DECLARE_WAITQUEUE(wait, current); struct bio *def; int r = -EINVAL; int do_lockfs = suspend_flags & DM_SUSPEND_LOCKFS_FLAG ? 1 : 0; int noflush = suspend_flags & DM_SUSPEND_NOFLUSH_FLAG ? 1 : 0; down(&md->suspend_lock); if (dm_suspended(md)) goto out_unlock; map = dm_get_table(md); /* * DMF_NOFLUSH_SUSPENDING must be set before presuspend. * This flag is cleared before dm_suspend returns. */ if (noflush) set_bit(DMF_NOFLUSH_SUSPENDING, &md->flags); /* This does not get reverted if there's an error later. */ dm_table_presuspend_targets(map); /* bdget() can stall if the pending I/Os are not flushed */ if (!noflush) { md->suspended_bdev = bdget_disk(md->disk, 0); if (!md->suspended_bdev) { DMWARN("bdget failed in dm_suspend"); r = -ENOMEM; goto flush_and_out; } } /* * Flush I/O to the device. * noflush supersedes do_lockfs, because lock_fs() needs to flush I/Os. */ if (do_lockfs && !noflush) { r = lock_fs(md); if (r) goto out; } /* * First we set the BLOCK_IO flag so no more ios will be mapped. */ down_write(&md->io_lock); set_bit(DMF_BLOCK_IO, &md->flags); add_wait_queue(&md->wait, &wait); up_write(&md->io_lock); /* unplug */ if (map) dm_table_unplug_all(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->io_lock); remove_wait_queue(&md->wait, &wait); if (noflush) { spin_lock_irqsave(&md->pushback_lock, flags); clear_bit(DMF_NOFLUSH_SUSPENDING, &md->flags); bio_list_merge_head(&md->deferred, &md->pushback); bio_list_init(&md->pushback); spin_unlock_irqrestore(&md->pushback_lock, flags); } /* were we interrupted ? */ r = -EINTR; if (atomic_read(&md->pending)) { clear_bit(DMF_BLOCK_IO, &md->flags); def = bio_list_get(&md->deferred); __flush_deferred_io(md, def); up_write(&md->io_lock); unlock_fs(md); goto out; /* pushback list is already flushed, so skip flush */ } up_write(&md->io_lock); dm_table_postsuspend_targets(map); set_bit(DMF_SUSPENDED, &md->flags); r = 0;flush_and_out: if (r && noflush) { /* * Because there may be already I/Os in the pushback list, * flush them before return. */ down_write(&md->io_lock); spin_lock_irqsave(&md->pushback_lock, flags); clear_bit(DMF_NOFLUSH_SUSPENDING, &md->flags); bio_list_merge_head(&md->deferred, &md->pushback); bio_list_init(&md->pushback); spin_unlock_irqrestore(&md->pushback_lock, flags); def = bio_list_get(&md->deferred); __flush_deferred_io(md, def); up_write(&md->io_lock); }out: if (r && md->suspended_bdev) { bdput(md->suspended_bdev); md->suspended_bdev = NULL; } dm_table_put(map);out_unlock: up(&md->suspend_lock); return r;}int dm_resume(struct mapped_device *md){ int r = -EINVAL; struct bio *def; struct dm_table *map = NULL; down(&md->suspend_lock); if (!dm_suspended(md)) goto out; map = dm_get_table(md); if (!map || !dm_table_get_size(map)) goto out; r = dm_table_resume_targets(map); if (r) goto out; down_write(&md->io_lock); clear_bit(DMF_BLOCK_IO, &md->flags); def = bio_list_get(&md->deferred); __flush_deferred_io(md, def); up_write(&md->io_lock); unlock_fs(md); if (md->suspended_bdev) { bdput(md->suspended_bdev); md->suspended_bdev = NULL; } clear_bit(DMF_SUSPENDED, &md->flags); dm_table_unplug_all(map); dm_kobject_uevent(md); r = 0;out: dm_table_put(map); up(&md->suspend_lock); return r;}/*----------------------------------------------------------------- * Event notification. *---------------------------------------------------------------*/void dm_kobject_uevent(struct mapped_device *md){ kobject_uevent(&md->disk->kobj, KOBJ_CHANGE);}uint32_t dm_next_uevent_seq(struct mapped_device *md){ return atomic_add_return(1, &md->uevent_seq);}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)));}void dm_uevent_add(struct mapped_device *md, struct list_head *elist){ unsigned long flags; spin_lock_irqsave(&md->uevent_lock, flags); list_add(elist, &md->uevent_list); spin_unlock_irqrestore(&md->uevent_lock, flags);}/* * 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);}int dm_noflush_suspending(struct dm_target *ti){ struct mapped_device *md = dm_table_get_md(ti->table); int r = __noflush_suspending(md); dm_put(md); return r;}EXPORT_SYMBOL_GPL(dm_noflush_suspending);static struct block_device_operations dm_blk_dops = { .open = dm_blk_open, .release = dm_blk_close, .ioctl = dm_blk_ioctl, .getgeo = dm_blk_getgeo, .owner = THIS_MODULE};EXPORT_SYMBOL(dm_get_mapinfo);/* * 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 <dm-devel@redhat.com>");MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -