in dm-clone-target.c [1775:1947]
static int clone_ctr(struct dm_target *ti, unsigned int argc, char **argv)
{
int r;
sector_t nr_regions;
struct clone *clone;
struct dm_arg_set as;
if (argc < 4) {
ti->error = "Invalid number of arguments";
return -EINVAL;
}
as.argc = argc;
as.argv = argv;
clone = kzalloc(sizeof(*clone), GFP_KERNEL);
if (!clone) {
ti->error = "Failed to allocate clone structure";
return -ENOMEM;
}
clone->ti = ti;
/* Initialize dm-clone flags */
__set_bit(DM_CLONE_HYDRATION_ENABLED, &clone->flags);
__set_bit(DM_CLONE_HYDRATION_SUSPENDED, &clone->flags);
__set_bit(DM_CLONE_DISCARD_PASSDOWN, &clone->flags);
r = parse_metadata_dev(clone, &as, &ti->error);
if (r)
goto out_with_clone;
r = parse_dest_dev(clone, &as, &ti->error);
if (r)
goto out_with_meta_dev;
r = parse_source_dev(clone, &as, &ti->error);
if (r)
goto out_with_dest_dev;
r = parse_region_size(clone, &as, &ti->error);
if (r)
goto out_with_source_dev;
clone->region_shift = __ffs(clone->region_size);
nr_regions = dm_sector_div_up(ti->len, clone->region_size);
/* Check for overflow */
if (nr_regions != (unsigned long)nr_regions) {
ti->error = "Too many regions. Consider increasing the region size";
r = -EOVERFLOW;
goto out_with_source_dev;
}
clone->nr_regions = nr_regions;
r = validate_nr_regions(clone->nr_regions, &ti->error);
if (r)
goto out_with_source_dev;
r = dm_set_target_max_io_len(ti, clone->region_size);
if (r) {
ti->error = "Failed to set max io len";
goto out_with_source_dev;
}
r = parse_feature_args(&as, clone);
if (r)
goto out_with_source_dev;
r = parse_core_args(&as, clone);
if (r)
goto out_with_source_dev;
/* Load metadata */
clone->cmd = dm_clone_metadata_open(clone->metadata_dev->bdev, ti->len,
clone->region_size);
if (IS_ERR(clone->cmd)) {
ti->error = "Failed to load metadata";
r = PTR_ERR(clone->cmd);
goto out_with_source_dev;
}
__set_clone_mode(clone, CM_WRITE);
if (get_clone_mode(clone) != CM_WRITE) {
ti->error = "Unable to get write access to metadata, please check/repair metadata";
r = -EPERM;
goto out_with_metadata;
}
clone->last_commit_jiffies = jiffies;
/* Allocate hydration hash table */
r = hash_table_init(clone);
if (r) {
ti->error = "Failed to allocate hydration hash table";
goto out_with_metadata;
}
atomic_set(&clone->ios_in_flight, 0);
init_waitqueue_head(&clone->hydration_stopped);
spin_lock_init(&clone->lock);
bio_list_init(&clone->deferred_bios);
bio_list_init(&clone->deferred_discard_bios);
bio_list_init(&clone->deferred_flush_bios);
bio_list_init(&clone->deferred_flush_completions);
clone->hydration_offset = 0;
atomic_set(&clone->hydrations_in_flight, 0);
clone->wq = alloc_workqueue("dm-" DM_MSG_PREFIX, WQ_MEM_RECLAIM, 0);
if (!clone->wq) {
ti->error = "Failed to allocate workqueue";
r = -ENOMEM;
goto out_with_ht;
}
INIT_WORK(&clone->worker, do_worker);
INIT_DELAYED_WORK(&clone->waker, do_waker);
clone->kcopyd_client = dm_kcopyd_client_create(&dm_kcopyd_throttle);
if (IS_ERR(clone->kcopyd_client)) {
r = PTR_ERR(clone->kcopyd_client);
goto out_with_wq;
}
r = mempool_init_slab_pool(&clone->hydration_pool, MIN_HYDRATIONS,
_hydration_cache);
if (r) {
ti->error = "Failed to create dm_clone_region_hydration memory pool";
goto out_with_kcopyd;
}
/* Save a copy of the table line */
r = copy_ctr_args(clone, argc - 3, (const char **)argv + 3, &ti->error);
if (r)
goto out_with_mempool;
mutex_init(&clone->commit_lock);
/* Enable flushes */
ti->num_flush_bios = 1;
ti->flush_supported = true;
/* Enable discards */
ti->discards_supported = true;
ti->num_discard_bios = 1;
ti->private = clone;
return 0;
out_with_mempool:
mempool_exit(&clone->hydration_pool);
out_with_kcopyd:
dm_kcopyd_client_destroy(clone->kcopyd_client);
out_with_wq:
destroy_workqueue(clone->wq);
out_with_ht:
hash_table_exit(clone);
out_with_metadata:
dm_clone_metadata_close(clone->cmd);
out_with_source_dev:
dm_put_device(ti, clone->source_dev);
out_with_dest_dev:
dm_put_device(ti, clone->dest_dev);
out_with_meta_dev:
dm_put_device(ti, clone->metadata_dev);
out_with_clone:
kfree(clone);
return r;
}