static int clone_ctr()

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;
}