18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright (C) 2005-2007 Red Hat GmbH
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * A target that delays reads and/or writes and can send
58c2ecf20Sopenharmony_ci * them to different devices.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * This file is released under the GPL.
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci#include <linux/init.h>
128c2ecf20Sopenharmony_ci#include <linux/blkdev.h>
138c2ecf20Sopenharmony_ci#include <linux/bio.h>
148c2ecf20Sopenharmony_ci#include <linux/slab.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <linux/device-mapper.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#define DM_MSG_PREFIX "delay"
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistruct delay_class {
218c2ecf20Sopenharmony_ci	struct dm_dev *dev;
228c2ecf20Sopenharmony_ci	sector_t start;
238c2ecf20Sopenharmony_ci	unsigned delay;
248c2ecf20Sopenharmony_ci	unsigned ops;
258c2ecf20Sopenharmony_ci};
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistruct delay_c {
288c2ecf20Sopenharmony_ci	struct timer_list delay_timer;
298c2ecf20Sopenharmony_ci	struct mutex timer_lock;
308c2ecf20Sopenharmony_ci	struct workqueue_struct *kdelayd_wq;
318c2ecf20Sopenharmony_ci	struct work_struct flush_expired_bios;
328c2ecf20Sopenharmony_ci	struct list_head delayed_bios;
338c2ecf20Sopenharmony_ci	bool may_delay;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	struct delay_class read;
368c2ecf20Sopenharmony_ci	struct delay_class write;
378c2ecf20Sopenharmony_ci	struct delay_class flush;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	int argc;
408c2ecf20Sopenharmony_ci};
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistruct dm_delay_info {
438c2ecf20Sopenharmony_ci	struct delay_c *context;
448c2ecf20Sopenharmony_ci	struct delay_class *class;
458c2ecf20Sopenharmony_ci	struct list_head list;
468c2ecf20Sopenharmony_ci	unsigned long expires;
478c2ecf20Sopenharmony_ci};
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(delayed_bios_lock);
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic void handle_delayed_timer(struct timer_list *t)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	struct delay_c *dc = from_timer(dc, t, delay_timer);
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	queue_work(dc->kdelayd_wq, &dc->flush_expired_bios);
568c2ecf20Sopenharmony_ci}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic void queue_timeout(struct delay_c *dc, unsigned long expires)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	mutex_lock(&dc->timer_lock);
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	if (!timer_pending(&dc->delay_timer) || expires < dc->delay_timer.expires)
638c2ecf20Sopenharmony_ci		mod_timer(&dc->delay_timer, expires);
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	mutex_unlock(&dc->timer_lock);
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic void flush_bios(struct bio *bio)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	struct bio *n;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	while (bio) {
738c2ecf20Sopenharmony_ci		n = bio->bi_next;
748c2ecf20Sopenharmony_ci		bio->bi_next = NULL;
758c2ecf20Sopenharmony_ci		submit_bio_noacct(bio);
768c2ecf20Sopenharmony_ci		bio = n;
778c2ecf20Sopenharmony_ci	}
788c2ecf20Sopenharmony_ci}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistatic struct bio *flush_delayed_bios(struct delay_c *dc, int flush_all)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	struct dm_delay_info *delayed, *next;
838c2ecf20Sopenharmony_ci	unsigned long next_expires = 0;
848c2ecf20Sopenharmony_ci	unsigned long start_timer = 0;
858c2ecf20Sopenharmony_ci	struct bio_list flush_bios = { };
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	mutex_lock(&delayed_bios_lock);
888c2ecf20Sopenharmony_ci	list_for_each_entry_safe(delayed, next, &dc->delayed_bios, list) {
898c2ecf20Sopenharmony_ci		if (flush_all || time_after_eq(jiffies, delayed->expires)) {
908c2ecf20Sopenharmony_ci			struct bio *bio = dm_bio_from_per_bio_data(delayed,
918c2ecf20Sopenharmony_ci						sizeof(struct dm_delay_info));
928c2ecf20Sopenharmony_ci			list_del(&delayed->list);
938c2ecf20Sopenharmony_ci			bio_list_add(&flush_bios, bio);
948c2ecf20Sopenharmony_ci			delayed->class->ops--;
958c2ecf20Sopenharmony_ci			continue;
968c2ecf20Sopenharmony_ci		}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci		if (!start_timer) {
998c2ecf20Sopenharmony_ci			start_timer = 1;
1008c2ecf20Sopenharmony_ci			next_expires = delayed->expires;
1018c2ecf20Sopenharmony_ci		} else
1028c2ecf20Sopenharmony_ci			next_expires = min(next_expires, delayed->expires);
1038c2ecf20Sopenharmony_ci	}
1048c2ecf20Sopenharmony_ci	mutex_unlock(&delayed_bios_lock);
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	if (start_timer)
1078c2ecf20Sopenharmony_ci		queue_timeout(dc, next_expires);
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	return bio_list_get(&flush_bios);
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistatic void flush_expired_bios(struct work_struct *work)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	struct delay_c *dc;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	dc = container_of(work, struct delay_c, flush_expired_bios);
1178c2ecf20Sopenharmony_ci	flush_bios(flush_delayed_bios(dc, 0));
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic void delay_dtr(struct dm_target *ti)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	struct delay_c *dc = ti->private;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	if (dc->kdelayd_wq)
1258c2ecf20Sopenharmony_ci		destroy_workqueue(dc->kdelayd_wq);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	if (dc->read.dev)
1288c2ecf20Sopenharmony_ci		dm_put_device(ti, dc->read.dev);
1298c2ecf20Sopenharmony_ci	if (dc->write.dev)
1308c2ecf20Sopenharmony_ci		dm_put_device(ti, dc->write.dev);
1318c2ecf20Sopenharmony_ci	if (dc->flush.dev)
1328c2ecf20Sopenharmony_ci		dm_put_device(ti, dc->flush.dev);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	mutex_destroy(&dc->timer_lock);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	kfree(dc);
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_cistatic int delay_class_ctr(struct dm_target *ti, struct delay_class *c, char **argv)
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	int ret;
1428c2ecf20Sopenharmony_ci	unsigned long long tmpll;
1438c2ecf20Sopenharmony_ci	char dummy;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	if (sscanf(argv[1], "%llu%c", &tmpll, &dummy) != 1 || tmpll != (sector_t)tmpll) {
1468c2ecf20Sopenharmony_ci		ti->error = "Invalid device sector";
1478c2ecf20Sopenharmony_ci		return -EINVAL;
1488c2ecf20Sopenharmony_ci	}
1498c2ecf20Sopenharmony_ci	c->start = tmpll;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	if (sscanf(argv[2], "%u%c", &c->delay, &dummy) != 1) {
1528c2ecf20Sopenharmony_ci		ti->error = "Invalid delay";
1538c2ecf20Sopenharmony_ci		return -EINVAL;
1548c2ecf20Sopenharmony_ci	}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	ret = dm_get_device(ti, argv[0], dm_table_get_mode(ti->table), &c->dev);
1578c2ecf20Sopenharmony_ci	if (ret) {
1588c2ecf20Sopenharmony_ci		ti->error = "Device lookup failed";
1598c2ecf20Sopenharmony_ci		return ret;
1608c2ecf20Sopenharmony_ci	}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	return 0;
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci/*
1668c2ecf20Sopenharmony_ci * Mapping parameters:
1678c2ecf20Sopenharmony_ci *    <device> <offset> <delay> [<write_device> <write_offset> <write_delay>]
1688c2ecf20Sopenharmony_ci *
1698c2ecf20Sopenharmony_ci * With separate write parameters, the first set is only used for reads.
1708c2ecf20Sopenharmony_ci * Offsets are specified in sectors.
1718c2ecf20Sopenharmony_ci * Delays are specified in milliseconds.
1728c2ecf20Sopenharmony_ci */
1738c2ecf20Sopenharmony_cistatic int delay_ctr(struct dm_target *ti, unsigned int argc, char **argv)
1748c2ecf20Sopenharmony_ci{
1758c2ecf20Sopenharmony_ci	struct delay_c *dc;
1768c2ecf20Sopenharmony_ci	int ret;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	if (argc != 3 && argc != 6 && argc != 9) {
1798c2ecf20Sopenharmony_ci		ti->error = "Requires exactly 3, 6 or 9 arguments";
1808c2ecf20Sopenharmony_ci		return -EINVAL;
1818c2ecf20Sopenharmony_ci	}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	dc = kzalloc(sizeof(*dc), GFP_KERNEL);
1848c2ecf20Sopenharmony_ci	if (!dc) {
1858c2ecf20Sopenharmony_ci		ti->error = "Cannot allocate context";
1868c2ecf20Sopenharmony_ci		return -ENOMEM;
1878c2ecf20Sopenharmony_ci	}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	ti->private = dc;
1908c2ecf20Sopenharmony_ci	timer_setup(&dc->delay_timer, handle_delayed_timer, 0);
1918c2ecf20Sopenharmony_ci	INIT_WORK(&dc->flush_expired_bios, flush_expired_bios);
1928c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&dc->delayed_bios);
1938c2ecf20Sopenharmony_ci	mutex_init(&dc->timer_lock);
1948c2ecf20Sopenharmony_ci	dc->may_delay = true;
1958c2ecf20Sopenharmony_ci	dc->argc = argc;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	ret = delay_class_ctr(ti, &dc->read, argv);
1988c2ecf20Sopenharmony_ci	if (ret)
1998c2ecf20Sopenharmony_ci		goto bad;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	if (argc == 3) {
2028c2ecf20Sopenharmony_ci		ret = delay_class_ctr(ti, &dc->write, argv);
2038c2ecf20Sopenharmony_ci		if (ret)
2048c2ecf20Sopenharmony_ci			goto bad;
2058c2ecf20Sopenharmony_ci		ret = delay_class_ctr(ti, &dc->flush, argv);
2068c2ecf20Sopenharmony_ci		if (ret)
2078c2ecf20Sopenharmony_ci			goto bad;
2088c2ecf20Sopenharmony_ci		goto out;
2098c2ecf20Sopenharmony_ci	}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	ret = delay_class_ctr(ti, &dc->write, argv + 3);
2128c2ecf20Sopenharmony_ci	if (ret)
2138c2ecf20Sopenharmony_ci		goto bad;
2148c2ecf20Sopenharmony_ci	if (argc == 6) {
2158c2ecf20Sopenharmony_ci		ret = delay_class_ctr(ti, &dc->flush, argv + 3);
2168c2ecf20Sopenharmony_ci		if (ret)
2178c2ecf20Sopenharmony_ci			goto bad;
2188c2ecf20Sopenharmony_ci		goto out;
2198c2ecf20Sopenharmony_ci	}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	ret = delay_class_ctr(ti, &dc->flush, argv + 6);
2228c2ecf20Sopenharmony_ci	if (ret)
2238c2ecf20Sopenharmony_ci		goto bad;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ciout:
2268c2ecf20Sopenharmony_ci	dc->kdelayd_wq = alloc_workqueue("kdelayd", WQ_MEM_RECLAIM, 0);
2278c2ecf20Sopenharmony_ci	if (!dc->kdelayd_wq) {
2288c2ecf20Sopenharmony_ci		ret = -EINVAL;
2298c2ecf20Sopenharmony_ci		DMERR("Couldn't start kdelayd");
2308c2ecf20Sopenharmony_ci		goto bad;
2318c2ecf20Sopenharmony_ci	}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	ti->num_flush_bios = 1;
2348c2ecf20Sopenharmony_ci	ti->num_discard_bios = 1;
2358c2ecf20Sopenharmony_ci	ti->per_io_data_size = sizeof(struct dm_delay_info);
2368c2ecf20Sopenharmony_ci	return 0;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_cibad:
2398c2ecf20Sopenharmony_ci	delay_dtr(ti);
2408c2ecf20Sopenharmony_ci	return ret;
2418c2ecf20Sopenharmony_ci}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_cistatic int delay_bio(struct delay_c *dc, struct delay_class *c, struct bio *bio)
2448c2ecf20Sopenharmony_ci{
2458c2ecf20Sopenharmony_ci	struct dm_delay_info *delayed;
2468c2ecf20Sopenharmony_ci	unsigned long expires = 0;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	if (!c->delay)
2498c2ecf20Sopenharmony_ci		return DM_MAPIO_REMAPPED;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	delayed = dm_per_bio_data(bio, sizeof(struct dm_delay_info));
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	delayed->context = dc;
2548c2ecf20Sopenharmony_ci	delayed->expires = expires = jiffies + msecs_to_jiffies(c->delay);
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	mutex_lock(&delayed_bios_lock);
2578c2ecf20Sopenharmony_ci	if (unlikely(!dc->may_delay)) {
2588c2ecf20Sopenharmony_ci		mutex_unlock(&delayed_bios_lock);
2598c2ecf20Sopenharmony_ci		return DM_MAPIO_REMAPPED;
2608c2ecf20Sopenharmony_ci	}
2618c2ecf20Sopenharmony_ci	c->ops++;
2628c2ecf20Sopenharmony_ci	list_add_tail(&delayed->list, &dc->delayed_bios);
2638c2ecf20Sopenharmony_ci	mutex_unlock(&delayed_bios_lock);
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	queue_timeout(dc, expires);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	return DM_MAPIO_SUBMITTED;
2688c2ecf20Sopenharmony_ci}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_cistatic void delay_presuspend(struct dm_target *ti)
2718c2ecf20Sopenharmony_ci{
2728c2ecf20Sopenharmony_ci	struct delay_c *dc = ti->private;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	mutex_lock(&delayed_bios_lock);
2758c2ecf20Sopenharmony_ci	dc->may_delay = false;
2768c2ecf20Sopenharmony_ci	mutex_unlock(&delayed_bios_lock);
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	del_timer_sync(&dc->delay_timer);
2798c2ecf20Sopenharmony_ci	flush_bios(flush_delayed_bios(dc, 1));
2808c2ecf20Sopenharmony_ci}
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_cistatic void delay_resume(struct dm_target *ti)
2838c2ecf20Sopenharmony_ci{
2848c2ecf20Sopenharmony_ci	struct delay_c *dc = ti->private;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	dc->may_delay = true;
2878c2ecf20Sopenharmony_ci}
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_cistatic int delay_map(struct dm_target *ti, struct bio *bio)
2908c2ecf20Sopenharmony_ci{
2918c2ecf20Sopenharmony_ci	struct delay_c *dc = ti->private;
2928c2ecf20Sopenharmony_ci	struct delay_class *c;
2938c2ecf20Sopenharmony_ci	struct dm_delay_info *delayed = dm_per_bio_data(bio, sizeof(struct dm_delay_info));
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	if (bio_data_dir(bio) == WRITE) {
2968c2ecf20Sopenharmony_ci		if (unlikely(bio->bi_opf & REQ_PREFLUSH))
2978c2ecf20Sopenharmony_ci			c = &dc->flush;
2988c2ecf20Sopenharmony_ci		else
2998c2ecf20Sopenharmony_ci			c = &dc->write;
3008c2ecf20Sopenharmony_ci	} else {
3018c2ecf20Sopenharmony_ci		c = &dc->read;
3028c2ecf20Sopenharmony_ci	}
3038c2ecf20Sopenharmony_ci	delayed->class = c;
3048c2ecf20Sopenharmony_ci	bio_set_dev(bio, c->dev->bdev);
3058c2ecf20Sopenharmony_ci	if (bio_sectors(bio))
3068c2ecf20Sopenharmony_ci		bio->bi_iter.bi_sector = c->start + dm_target_offset(ti, bio->bi_iter.bi_sector);
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	return delay_bio(dc, c, bio);
3098c2ecf20Sopenharmony_ci}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci#define DMEMIT_DELAY_CLASS(c) \
3128c2ecf20Sopenharmony_ci	DMEMIT("%s %llu %u", (c)->dev->name, (unsigned long long)(c)->start, (c)->delay)
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_cistatic void delay_status(struct dm_target *ti, status_type_t type,
3158c2ecf20Sopenharmony_ci			 unsigned status_flags, char *result, unsigned maxlen)
3168c2ecf20Sopenharmony_ci{
3178c2ecf20Sopenharmony_ci	struct delay_c *dc = ti->private;
3188c2ecf20Sopenharmony_ci	int sz = 0;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	switch (type) {
3218c2ecf20Sopenharmony_ci	case STATUSTYPE_INFO:
3228c2ecf20Sopenharmony_ci		DMEMIT("%u %u %u", dc->read.ops, dc->write.ops, dc->flush.ops);
3238c2ecf20Sopenharmony_ci		break;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	case STATUSTYPE_TABLE:
3268c2ecf20Sopenharmony_ci		DMEMIT_DELAY_CLASS(&dc->read);
3278c2ecf20Sopenharmony_ci		if (dc->argc >= 6) {
3288c2ecf20Sopenharmony_ci			DMEMIT(" ");
3298c2ecf20Sopenharmony_ci			DMEMIT_DELAY_CLASS(&dc->write);
3308c2ecf20Sopenharmony_ci		}
3318c2ecf20Sopenharmony_ci		if (dc->argc >= 9) {
3328c2ecf20Sopenharmony_ci			DMEMIT(" ");
3338c2ecf20Sopenharmony_ci			DMEMIT_DELAY_CLASS(&dc->flush);
3348c2ecf20Sopenharmony_ci		}
3358c2ecf20Sopenharmony_ci		break;
3368c2ecf20Sopenharmony_ci	}
3378c2ecf20Sopenharmony_ci}
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_cistatic int delay_iterate_devices(struct dm_target *ti,
3408c2ecf20Sopenharmony_ci				 iterate_devices_callout_fn fn, void *data)
3418c2ecf20Sopenharmony_ci{
3428c2ecf20Sopenharmony_ci	struct delay_c *dc = ti->private;
3438c2ecf20Sopenharmony_ci	int ret = 0;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	ret = fn(ti, dc->read.dev, dc->read.start, ti->len, data);
3468c2ecf20Sopenharmony_ci	if (ret)
3478c2ecf20Sopenharmony_ci		goto out;
3488c2ecf20Sopenharmony_ci	ret = fn(ti, dc->write.dev, dc->write.start, ti->len, data);
3498c2ecf20Sopenharmony_ci	if (ret)
3508c2ecf20Sopenharmony_ci		goto out;
3518c2ecf20Sopenharmony_ci	ret = fn(ti, dc->flush.dev, dc->flush.start, ti->len, data);
3528c2ecf20Sopenharmony_ci	if (ret)
3538c2ecf20Sopenharmony_ci		goto out;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ciout:
3568c2ecf20Sopenharmony_ci	return ret;
3578c2ecf20Sopenharmony_ci}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_cistatic struct target_type delay_target = {
3608c2ecf20Sopenharmony_ci	.name	     = "delay",
3618c2ecf20Sopenharmony_ci	.version     = {1, 2, 1},
3628c2ecf20Sopenharmony_ci	.features    = DM_TARGET_PASSES_INTEGRITY,
3638c2ecf20Sopenharmony_ci	.module      = THIS_MODULE,
3648c2ecf20Sopenharmony_ci	.ctr	     = delay_ctr,
3658c2ecf20Sopenharmony_ci	.dtr	     = delay_dtr,
3668c2ecf20Sopenharmony_ci	.map	     = delay_map,
3678c2ecf20Sopenharmony_ci	.presuspend  = delay_presuspend,
3688c2ecf20Sopenharmony_ci	.resume	     = delay_resume,
3698c2ecf20Sopenharmony_ci	.status	     = delay_status,
3708c2ecf20Sopenharmony_ci	.iterate_devices = delay_iterate_devices,
3718c2ecf20Sopenharmony_ci};
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_cistatic int __init dm_delay_init(void)
3748c2ecf20Sopenharmony_ci{
3758c2ecf20Sopenharmony_ci	int r;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	r = dm_register_target(&delay_target);
3788c2ecf20Sopenharmony_ci	if (r < 0) {
3798c2ecf20Sopenharmony_ci		DMERR("register failed %d", r);
3808c2ecf20Sopenharmony_ci		goto bad_register;
3818c2ecf20Sopenharmony_ci	}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	return 0;
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_cibad_register:
3868c2ecf20Sopenharmony_ci	return r;
3878c2ecf20Sopenharmony_ci}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_cistatic void __exit dm_delay_exit(void)
3908c2ecf20Sopenharmony_ci{
3918c2ecf20Sopenharmony_ci	dm_unregister_target(&delay_target);
3928c2ecf20Sopenharmony_ci}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci/* Module hooks */
3958c2ecf20Sopenharmony_cimodule_init(dm_delay_init);
3968c2ecf20Sopenharmony_cimodule_exit(dm_delay_exit);
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DM_NAME " delay target");
3998c2ecf20Sopenharmony_ciMODULE_AUTHOR("Heinz Mauelshagen <mauelshagen@redhat.com>");
4008c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
401