/* * Split the bio into several clones. */ static void __split_bio(struct mapped_device *md, struct bio *bio) { struct clone_info ci; ci.map = dm_get_table(md); if (!ci.map) { bio_io_error(bio, bio->bi_size); return; } ci.md = md; ci.bio = bio; ci.io = alloc_io(md); ci.io->error = 0; atomic_set(&ci.io->io_count, 1); ci.io->bio = bio; ci.io->md = md; ci.sector = bio->bi_sector; ci.sector_count = bio_sectors(bio); ci.idx = bio->bi_idx; atomic_inc(&md->pending); while (ci.sector_count) __clone_and_map(&ci); /* drop the extra reference count */ dec_pending(ci.io, 0); dm_table_put(ci.map); }
/**ltl * 功能: 分割bio请求 * 参数: * 返回值: * 说明: */ static void __split_bio(struct mapped_device *md, struct bio *bio) { struct clone_info ci; ci.map = dm_get_table(md); if (!ci.map) { bio_io_error(bio, bio->bi_size); return; } ci.md = md; ci.bio = bio; ci.io = alloc_io(md); ci.io->error = 0; atomic_set(&ci.io->io_count, 1); ci.io->bio = bio; ci.io->md = md; ci.sector = bio->bi_sector; /* 请求起始扇区 */ /* 从这里可以看出数据长度必定是512的整数倍 */ ci.sector_count = bio_sectors(bio); /* 数据长度(扇区数) */ ci.idx = bio->bi_idx; /* 当前bi_vec数组下标 */ start_io_acct(ci.io); while (ci.sector_count) /* 分发各个请求 */ __clone_and_map(&ci); /* drop the extra reference count */ dec_pending(ci.io, 0); dm_table_put(ci.map); }
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; }
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); } }
/**ltl * 功能: 映射设备的泄流处理函数 * 参数: * 返回值: * 说明: */ 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); } }
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); }
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; }
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(map); dm_table_put(map); } return ret; }
void dm_put(struct mapped_device *md) { struct dm_table *map = dm_get_table(md); if (atomic_dec_and_test(&md->holders)) { if (!dm_suspended(md)) { dm_table_presuspend_targets(map); dm_table_postsuspend_targets(map); } __unbind(md); free_dev(md); } dm_table_put(map); }
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); kobject_uevent(&md->disk->kobj, KOBJ_CHANGE); r = 0; out: dm_table_put(map); up(&md->suspend_lock); return r; }
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); } }
static int dm_blk_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { struct mapped_device *md; struct dm_table *map; struct dm_target *tgt; int r = -ENOTTY; /* We don't really need this lock, but we do need 'inode'. */ unlock_kernel(); md = inode->i_bdev->bd_disk->private_data; map = dm_get_table(md); if (!map || !dm_table_get_size(map)) goto out; /* We only support devices that have a single target */ if (dm_table_get_num_targets(map) != 1) goto out; tgt = dm_table_get_target(map, 0); if (dm_suspended(md)) { r = -EAGAIN; goto out; } if (tgt->type->ioctl) r = tgt->type->ioctl(tgt, inode, file, cmd, arg); out: dm_table_put(map); lock_kernel(); return r; }
int dm_create_error_table(struct dm_table **result, struct mapped_device *md) { struct dm_table *t; sector_t dev_size = 1; int r; /* * Find current size of device. * Default to 1 sector if inactive. */ t = dm_get_table(md); if (t) { dev_size = dm_table_get_size(t); dm_table_put(t); } r = dm_table_create(&t, FMODE_READ, 1, md); if (r) return r; r = dm_table_add_target(t, "error", 0, dev_size, NULL); if (r) goto out; r = dm_table_complete(t); if (r) goto out; *result = t; out: if (r) dm_table_put(t); return r; }
/* * 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; }
/* * 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; }
/* * 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, int do_lockfs) { struct dm_table *map = NULL; DECLARE_WAITQUEUE(wait, current); struct bio *def; int r = -EINVAL; down(&md->suspend_lock); if (dm_suspended(md)) goto out; map = dm_get_table(md); /* This does not get reverted if there's an error later. */ dm_table_presuspend_targets(map); md->suspended_bdev = bdget_disk(md->disk, 0); if (!md->suspended_bdev) { DMWARN("bdget failed in dm_suspend"); r = -ENOMEM; goto out; } /* Flush I/O to the device. */ if (do_lockfs) { 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); /* 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; } up_write(&md->io_lock); dm_table_postsuspend_targets(map); set_bit(DMF_SUSPENDED, &md->flags); r = 0; out: if (r && md->suspended_bdev) { bdput(md->suspended_bdev); md->suspended_bdev = NULL; } dm_table_put(map); up(&md->suspend_lock); return r; }