xref: /linux/drivers/block/zloop.c (revision 9236c5fdd5a8bec2445e834e7e1bbefb2eb62f67)
1eb0570c7SDamien Le Moal // SPDX-License-Identifier: GPL-2.0-only
2eb0570c7SDamien Le Moal /*
3eb0570c7SDamien Le Moal  * Copyright (c) 2025, Christoph Hellwig.
4eb0570c7SDamien Le Moal  * Copyright (c) 2025, Western Digital Corporation or its affiliates.
5eb0570c7SDamien Le Moal  *
6eb0570c7SDamien Le Moal  * Zoned Loop Device driver - exports a zoned block device using one file per
7eb0570c7SDamien Le Moal  * zone as backing storage.
8eb0570c7SDamien Le Moal  */
9eb0570c7SDamien Le Moal #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
10eb0570c7SDamien Le Moal 
11eb0570c7SDamien Le Moal #include <linux/module.h>
12eb0570c7SDamien Le Moal #include <linux/blk-mq.h>
13eb0570c7SDamien Le Moal #include <linux/blkzoned.h>
14eb0570c7SDamien Le Moal #include <linux/pagemap.h>
15eb0570c7SDamien Le Moal #include <linux/miscdevice.h>
16eb0570c7SDamien Le Moal #include <linux/falloc.h>
17eb0570c7SDamien Le Moal #include <linux/mutex.h>
18eb0570c7SDamien Le Moal #include <linux/parser.h>
19eb0570c7SDamien Le Moal #include <linux/seq_file.h>
20eb0570c7SDamien Le Moal 
21eb0570c7SDamien Le Moal /*
22eb0570c7SDamien Le Moal  * Options for adding (and removing) a device.
23eb0570c7SDamien Le Moal  */
24eb0570c7SDamien Le Moal enum {
25eb0570c7SDamien Le Moal 	ZLOOP_OPT_ERR			= 0,
26eb0570c7SDamien Le Moal 	ZLOOP_OPT_ID			= (1 << 0),
27eb0570c7SDamien Le Moal 	ZLOOP_OPT_CAPACITY		= (1 << 1),
28eb0570c7SDamien Le Moal 	ZLOOP_OPT_ZONE_SIZE		= (1 << 2),
29eb0570c7SDamien Le Moal 	ZLOOP_OPT_ZONE_CAPACITY		= (1 << 3),
30eb0570c7SDamien Le Moal 	ZLOOP_OPT_NR_CONV_ZONES		= (1 << 4),
31eb0570c7SDamien Le Moal 	ZLOOP_OPT_BASE_DIR		= (1 << 5),
32eb0570c7SDamien Le Moal 	ZLOOP_OPT_NR_QUEUES		= (1 << 6),
33eb0570c7SDamien Le Moal 	ZLOOP_OPT_QUEUE_DEPTH		= (1 << 7),
34eb0570c7SDamien Le Moal 	ZLOOP_OPT_BUFFERED_IO		= (1 << 8),
35*9236c5fdSDamien Le Moal 	ZLOOP_OPT_ZONE_APPEND		= (1 << 9),
36eb0570c7SDamien Le Moal };
37eb0570c7SDamien Le Moal 
38eb0570c7SDamien Le Moal static const match_table_t zloop_opt_tokens = {
39eb0570c7SDamien Le Moal 	{ ZLOOP_OPT_ID,			"id=%d"	},
40eb0570c7SDamien Le Moal 	{ ZLOOP_OPT_CAPACITY,		"capacity_mb=%u"	},
41eb0570c7SDamien Le Moal 	{ ZLOOP_OPT_ZONE_SIZE,		"zone_size_mb=%u"	},
42eb0570c7SDamien Le Moal 	{ ZLOOP_OPT_ZONE_CAPACITY,	"zone_capacity_mb=%u"	},
43eb0570c7SDamien Le Moal 	{ ZLOOP_OPT_NR_CONV_ZONES,	"conv_zones=%u"		},
44eb0570c7SDamien Le Moal 	{ ZLOOP_OPT_BASE_DIR,		"base_dir=%s"		},
45eb0570c7SDamien Le Moal 	{ ZLOOP_OPT_NR_QUEUES,		"nr_queues=%u"		},
46eb0570c7SDamien Le Moal 	{ ZLOOP_OPT_QUEUE_DEPTH,	"queue_depth=%u"	},
47eb0570c7SDamien Le Moal 	{ ZLOOP_OPT_BUFFERED_IO,	"buffered_io"		},
48*9236c5fdSDamien Le Moal 	{ ZLOOP_OPT_ZONE_APPEND,	"zone_append=%u"	},
49eb0570c7SDamien Le Moal 	{ ZLOOP_OPT_ERR,		NULL			}
50eb0570c7SDamien Le Moal };
51eb0570c7SDamien Le Moal 
52eb0570c7SDamien Le Moal /* Default values for the "add" operation. */
53eb0570c7SDamien Le Moal #define ZLOOP_DEF_ID			-1
54eb0570c7SDamien Le Moal #define ZLOOP_DEF_ZONE_SIZE		((256ULL * SZ_1M) >> SECTOR_SHIFT)
55eb0570c7SDamien Le Moal #define ZLOOP_DEF_NR_ZONES		64
56eb0570c7SDamien Le Moal #define ZLOOP_DEF_NR_CONV_ZONES		8
57eb0570c7SDamien Le Moal #define ZLOOP_DEF_BASE_DIR		"/var/local/zloop"
58eb0570c7SDamien Le Moal #define ZLOOP_DEF_NR_QUEUES		1
59eb0570c7SDamien Le Moal #define ZLOOP_DEF_QUEUE_DEPTH		128
60eb0570c7SDamien Le Moal #define ZLOOP_DEF_BUFFERED_IO		false
61*9236c5fdSDamien Le Moal #define ZLOOP_DEF_ZONE_APPEND		true
62eb0570c7SDamien Le Moal 
63eb0570c7SDamien Le Moal /* Arbitrary limit on the zone size (16GB). */
64eb0570c7SDamien Le Moal #define ZLOOP_MAX_ZONE_SIZE_MB		16384
65eb0570c7SDamien Le Moal 
66eb0570c7SDamien Le Moal struct zloop_options {
67eb0570c7SDamien Le Moal 	unsigned int		mask;
68eb0570c7SDamien Le Moal 	int			id;
69eb0570c7SDamien Le Moal 	sector_t		capacity;
70eb0570c7SDamien Le Moal 	sector_t		zone_size;
71eb0570c7SDamien Le Moal 	sector_t		zone_capacity;
72eb0570c7SDamien Le Moal 	unsigned int		nr_conv_zones;
73eb0570c7SDamien Le Moal 	char			*base_dir;
74eb0570c7SDamien Le Moal 	unsigned int		nr_queues;
75eb0570c7SDamien Le Moal 	unsigned int		queue_depth;
76eb0570c7SDamien Le Moal 	bool			buffered_io;
77*9236c5fdSDamien Le Moal 	bool			zone_append;
78eb0570c7SDamien Le Moal };
79eb0570c7SDamien Le Moal 
80eb0570c7SDamien Le Moal /*
81eb0570c7SDamien Le Moal  * Device states.
82eb0570c7SDamien Le Moal  */
83eb0570c7SDamien Le Moal enum {
84eb0570c7SDamien Le Moal 	Zlo_creating = 0,
85eb0570c7SDamien Le Moal 	Zlo_live,
86eb0570c7SDamien Le Moal 	Zlo_deleting,
87eb0570c7SDamien Le Moal };
88eb0570c7SDamien Le Moal 
89eb0570c7SDamien Le Moal enum zloop_zone_flags {
90eb0570c7SDamien Le Moal 	ZLOOP_ZONE_CONV = 0,
91eb0570c7SDamien Le Moal 	ZLOOP_ZONE_SEQ_ERROR,
92eb0570c7SDamien Le Moal };
93eb0570c7SDamien Le Moal 
94eb0570c7SDamien Le Moal struct zloop_zone {
95eb0570c7SDamien Le Moal 	struct file		*file;
96eb0570c7SDamien Le Moal 
97eb0570c7SDamien Le Moal 	unsigned long		flags;
98eb0570c7SDamien Le Moal 	struct mutex		lock;
99eb0570c7SDamien Le Moal 	enum blk_zone_cond	cond;
100eb0570c7SDamien Le Moal 	sector_t		start;
101eb0570c7SDamien Le Moal 	sector_t		wp;
102eb0570c7SDamien Le Moal 
103eb0570c7SDamien Le Moal 	gfp_t			old_gfp_mask;
104eb0570c7SDamien Le Moal };
105eb0570c7SDamien Le Moal 
106eb0570c7SDamien Le Moal struct zloop_device {
107eb0570c7SDamien Le Moal 	unsigned int		id;
108eb0570c7SDamien Le Moal 	unsigned int		state;
109eb0570c7SDamien Le Moal 
110eb0570c7SDamien Le Moal 	struct blk_mq_tag_set	tag_set;
111eb0570c7SDamien Le Moal 	struct gendisk		*disk;
112eb0570c7SDamien Le Moal 
113eb0570c7SDamien Le Moal 	struct workqueue_struct *workqueue;
114eb0570c7SDamien Le Moal 	bool			buffered_io;
115*9236c5fdSDamien Le Moal 	bool			zone_append;
116eb0570c7SDamien Le Moal 
117eb0570c7SDamien Le Moal 	const char		*base_dir;
118eb0570c7SDamien Le Moal 	struct file		*data_dir;
119eb0570c7SDamien Le Moal 
120eb0570c7SDamien Le Moal 	unsigned int		zone_shift;
121eb0570c7SDamien Le Moal 	sector_t		zone_size;
122eb0570c7SDamien Le Moal 	sector_t		zone_capacity;
123eb0570c7SDamien Le Moal 	unsigned int		nr_zones;
124eb0570c7SDamien Le Moal 	unsigned int		nr_conv_zones;
125eb0570c7SDamien Le Moal 	unsigned int		block_size;
126eb0570c7SDamien Le Moal 
127eb0570c7SDamien Le Moal 	struct zloop_zone	zones[] __counted_by(nr_zones);
128eb0570c7SDamien Le Moal };
129eb0570c7SDamien Le Moal 
130eb0570c7SDamien Le Moal struct zloop_cmd {
131eb0570c7SDamien Le Moal 	struct work_struct	work;
132eb0570c7SDamien Le Moal 	atomic_t		ref;
133eb0570c7SDamien Le Moal 	sector_t		sector;
134eb0570c7SDamien Le Moal 	sector_t		nr_sectors;
135eb0570c7SDamien Le Moal 	long			ret;
136eb0570c7SDamien Le Moal 	struct kiocb		iocb;
137eb0570c7SDamien Le Moal 	struct bio_vec		*bvec;
138eb0570c7SDamien Le Moal };
139eb0570c7SDamien Le Moal 
140eb0570c7SDamien Le Moal static DEFINE_IDR(zloop_index_idr);
141eb0570c7SDamien Le Moal static DEFINE_MUTEX(zloop_ctl_mutex);
142eb0570c7SDamien Le Moal 
143eb0570c7SDamien Le Moal static unsigned int rq_zone_no(struct request *rq)
144eb0570c7SDamien Le Moal {
145eb0570c7SDamien Le Moal 	struct zloop_device *zlo = rq->q->queuedata;
146eb0570c7SDamien Le Moal 
147eb0570c7SDamien Le Moal 	return blk_rq_pos(rq) >> zlo->zone_shift;
148eb0570c7SDamien Le Moal }
149eb0570c7SDamien Le Moal 
150eb0570c7SDamien Le Moal static int zloop_update_seq_zone(struct zloop_device *zlo, unsigned int zone_no)
151eb0570c7SDamien Le Moal {
152eb0570c7SDamien Le Moal 	struct zloop_zone *zone = &zlo->zones[zone_no];
153eb0570c7SDamien Le Moal 	struct kstat stat;
154eb0570c7SDamien Le Moal 	sector_t file_sectors;
155eb0570c7SDamien Le Moal 	int ret;
156eb0570c7SDamien Le Moal 
157eb0570c7SDamien Le Moal 	lockdep_assert_held(&zone->lock);
158eb0570c7SDamien Le Moal 
159eb0570c7SDamien Le Moal 	ret = vfs_getattr(&zone->file->f_path, &stat, STATX_SIZE, 0);
160eb0570c7SDamien Le Moal 	if (ret < 0) {
161eb0570c7SDamien Le Moal 		pr_err("Failed to get zone %u file stat (err=%d)\n",
162eb0570c7SDamien Le Moal 		       zone_no, ret);
163eb0570c7SDamien Le Moal 		set_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags);
164eb0570c7SDamien Le Moal 		return ret;
165eb0570c7SDamien Le Moal 	}
166eb0570c7SDamien Le Moal 
167eb0570c7SDamien Le Moal 	file_sectors = stat.size >> SECTOR_SHIFT;
168eb0570c7SDamien Le Moal 	if (file_sectors > zlo->zone_capacity) {
169eb0570c7SDamien Le Moal 		pr_err("Zone %u file too large (%llu sectors > %llu)\n",
170eb0570c7SDamien Le Moal 		       zone_no, file_sectors, zlo->zone_capacity);
171eb0570c7SDamien Le Moal 		return -EINVAL;
172eb0570c7SDamien Le Moal 	}
173eb0570c7SDamien Le Moal 
174eb0570c7SDamien Le Moal 	if (file_sectors & ((zlo->block_size >> SECTOR_SHIFT) - 1)) {
175eb0570c7SDamien Le Moal 		pr_err("Zone %u file size not aligned to block size %u\n",
176eb0570c7SDamien Le Moal 		       zone_no, zlo->block_size);
177eb0570c7SDamien Le Moal 		return -EINVAL;
178eb0570c7SDamien Le Moal 	}
179eb0570c7SDamien Le Moal 
180eb0570c7SDamien Le Moal 	if (!file_sectors) {
181eb0570c7SDamien Le Moal 		zone->cond = BLK_ZONE_COND_EMPTY;
182eb0570c7SDamien Le Moal 		zone->wp = zone->start;
183eb0570c7SDamien Le Moal 	} else if (file_sectors == zlo->zone_capacity) {
184eb0570c7SDamien Le Moal 		zone->cond = BLK_ZONE_COND_FULL;
185866d6574SDamien Le Moal 		zone->wp = ULLONG_MAX;
186eb0570c7SDamien Le Moal 	} else {
187eb0570c7SDamien Le Moal 		zone->cond = BLK_ZONE_COND_CLOSED;
188eb0570c7SDamien Le Moal 		zone->wp = zone->start + file_sectors;
189eb0570c7SDamien Le Moal 	}
190eb0570c7SDamien Le Moal 
191eb0570c7SDamien Le Moal 	return 0;
192eb0570c7SDamien Le Moal }
193eb0570c7SDamien Le Moal 
194eb0570c7SDamien Le Moal static int zloop_open_zone(struct zloop_device *zlo, unsigned int zone_no)
195eb0570c7SDamien Le Moal {
196eb0570c7SDamien Le Moal 	struct zloop_zone *zone = &zlo->zones[zone_no];
197eb0570c7SDamien Le Moal 	int ret = 0;
198eb0570c7SDamien Le Moal 
199eb0570c7SDamien Le Moal 	if (test_bit(ZLOOP_ZONE_CONV, &zone->flags))
200eb0570c7SDamien Le Moal 		return -EIO;
201eb0570c7SDamien Le Moal 
202eb0570c7SDamien Le Moal 	mutex_lock(&zone->lock);
203eb0570c7SDamien Le Moal 
204eb0570c7SDamien Le Moal 	if (test_and_clear_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags)) {
205eb0570c7SDamien Le Moal 		ret = zloop_update_seq_zone(zlo, zone_no);
206eb0570c7SDamien Le Moal 		if (ret)
207eb0570c7SDamien Le Moal 			goto unlock;
208eb0570c7SDamien Le Moal 	}
209eb0570c7SDamien Le Moal 
210eb0570c7SDamien Le Moal 	switch (zone->cond) {
211eb0570c7SDamien Le Moal 	case BLK_ZONE_COND_EXP_OPEN:
212eb0570c7SDamien Le Moal 		break;
213eb0570c7SDamien Le Moal 	case BLK_ZONE_COND_EMPTY:
214eb0570c7SDamien Le Moal 	case BLK_ZONE_COND_CLOSED:
215eb0570c7SDamien Le Moal 	case BLK_ZONE_COND_IMP_OPEN:
216eb0570c7SDamien Le Moal 		zone->cond = BLK_ZONE_COND_EXP_OPEN;
217eb0570c7SDamien Le Moal 		break;
218eb0570c7SDamien Le Moal 	case BLK_ZONE_COND_FULL:
219eb0570c7SDamien Le Moal 	default:
220eb0570c7SDamien Le Moal 		ret = -EIO;
221eb0570c7SDamien Le Moal 		break;
222eb0570c7SDamien Le Moal 	}
223eb0570c7SDamien Le Moal 
224eb0570c7SDamien Le Moal unlock:
225eb0570c7SDamien Le Moal 	mutex_unlock(&zone->lock);
226eb0570c7SDamien Le Moal 
227eb0570c7SDamien Le Moal 	return ret;
228eb0570c7SDamien Le Moal }
229eb0570c7SDamien Le Moal 
230eb0570c7SDamien Le Moal static int zloop_close_zone(struct zloop_device *zlo, unsigned int zone_no)
231eb0570c7SDamien Le Moal {
232eb0570c7SDamien Le Moal 	struct zloop_zone *zone = &zlo->zones[zone_no];
233eb0570c7SDamien Le Moal 	int ret = 0;
234eb0570c7SDamien Le Moal 
235eb0570c7SDamien Le Moal 	if (test_bit(ZLOOP_ZONE_CONV, &zone->flags))
236eb0570c7SDamien Le Moal 		return -EIO;
237eb0570c7SDamien Le Moal 
238eb0570c7SDamien Le Moal 	mutex_lock(&zone->lock);
239eb0570c7SDamien Le Moal 
240eb0570c7SDamien Le Moal 	if (test_and_clear_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags)) {
241eb0570c7SDamien Le Moal 		ret = zloop_update_seq_zone(zlo, zone_no);
242eb0570c7SDamien Le Moal 		if (ret)
243eb0570c7SDamien Le Moal 			goto unlock;
244eb0570c7SDamien Le Moal 	}
245eb0570c7SDamien Le Moal 
246eb0570c7SDamien Le Moal 	switch (zone->cond) {
247eb0570c7SDamien Le Moal 	case BLK_ZONE_COND_CLOSED:
248eb0570c7SDamien Le Moal 		break;
249eb0570c7SDamien Le Moal 	case BLK_ZONE_COND_IMP_OPEN:
250eb0570c7SDamien Le Moal 	case BLK_ZONE_COND_EXP_OPEN:
251eb0570c7SDamien Le Moal 		if (zone->wp == zone->start)
252eb0570c7SDamien Le Moal 			zone->cond = BLK_ZONE_COND_EMPTY;
253eb0570c7SDamien Le Moal 		else
254eb0570c7SDamien Le Moal 			zone->cond = BLK_ZONE_COND_CLOSED;
255eb0570c7SDamien Le Moal 		break;
256eb0570c7SDamien Le Moal 	case BLK_ZONE_COND_EMPTY:
257eb0570c7SDamien Le Moal 	case BLK_ZONE_COND_FULL:
258eb0570c7SDamien Le Moal 	default:
259eb0570c7SDamien Le Moal 		ret = -EIO;
260eb0570c7SDamien Le Moal 		break;
261eb0570c7SDamien Le Moal 	}
262eb0570c7SDamien Le Moal 
263eb0570c7SDamien Le Moal unlock:
264eb0570c7SDamien Le Moal 	mutex_unlock(&zone->lock);
265eb0570c7SDamien Le Moal 
266eb0570c7SDamien Le Moal 	return ret;
267eb0570c7SDamien Le Moal }
268eb0570c7SDamien Le Moal 
269eb0570c7SDamien Le Moal static int zloop_reset_zone(struct zloop_device *zlo, unsigned int zone_no)
270eb0570c7SDamien Le Moal {
271eb0570c7SDamien Le Moal 	struct zloop_zone *zone = &zlo->zones[zone_no];
272eb0570c7SDamien Le Moal 	int ret = 0;
273eb0570c7SDamien Le Moal 
274eb0570c7SDamien Le Moal 	if (test_bit(ZLOOP_ZONE_CONV, &zone->flags))
275eb0570c7SDamien Le Moal 		return -EIO;
276eb0570c7SDamien Le Moal 
277eb0570c7SDamien Le Moal 	mutex_lock(&zone->lock);
278eb0570c7SDamien Le Moal 
279eb0570c7SDamien Le Moal 	if (!test_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags) &&
280eb0570c7SDamien Le Moal 	    zone->cond == BLK_ZONE_COND_EMPTY)
281eb0570c7SDamien Le Moal 		goto unlock;
282eb0570c7SDamien Le Moal 
283eb0570c7SDamien Le Moal 	if (vfs_truncate(&zone->file->f_path, 0)) {
284eb0570c7SDamien Le Moal 		set_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags);
285eb0570c7SDamien Le Moal 		ret = -EIO;
286eb0570c7SDamien Le Moal 		goto unlock;
287eb0570c7SDamien Le Moal 	}
288eb0570c7SDamien Le Moal 
289eb0570c7SDamien Le Moal 	zone->cond = BLK_ZONE_COND_EMPTY;
290eb0570c7SDamien Le Moal 	zone->wp = zone->start;
291eb0570c7SDamien Le Moal 	clear_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags);
292eb0570c7SDamien Le Moal 
293eb0570c7SDamien Le Moal unlock:
294eb0570c7SDamien Le Moal 	mutex_unlock(&zone->lock);
295eb0570c7SDamien Le Moal 
296eb0570c7SDamien Le Moal 	return ret;
297eb0570c7SDamien Le Moal }
298eb0570c7SDamien Le Moal 
299eb0570c7SDamien Le Moal static int zloop_reset_all_zones(struct zloop_device *zlo)
300eb0570c7SDamien Le Moal {
301eb0570c7SDamien Le Moal 	unsigned int i;
302eb0570c7SDamien Le Moal 	int ret;
303eb0570c7SDamien Le Moal 
304eb0570c7SDamien Le Moal 	for (i = zlo->nr_conv_zones; i < zlo->nr_zones; i++) {
305eb0570c7SDamien Le Moal 		ret = zloop_reset_zone(zlo, i);
306eb0570c7SDamien Le Moal 		if (ret)
307eb0570c7SDamien Le Moal 			return ret;
308eb0570c7SDamien Le Moal 	}
309eb0570c7SDamien Le Moal 
310eb0570c7SDamien Le Moal 	return 0;
311eb0570c7SDamien Le Moal }
312eb0570c7SDamien Le Moal 
313eb0570c7SDamien Le Moal static int zloop_finish_zone(struct zloop_device *zlo, unsigned int zone_no)
314eb0570c7SDamien Le Moal {
315eb0570c7SDamien Le Moal 	struct zloop_zone *zone = &zlo->zones[zone_no];
316eb0570c7SDamien Le Moal 	int ret = 0;
317eb0570c7SDamien Le Moal 
318eb0570c7SDamien Le Moal 	if (test_bit(ZLOOP_ZONE_CONV, &zone->flags))
319eb0570c7SDamien Le Moal 		return -EIO;
320eb0570c7SDamien Le Moal 
321eb0570c7SDamien Le Moal 	mutex_lock(&zone->lock);
322eb0570c7SDamien Le Moal 
323eb0570c7SDamien Le Moal 	if (!test_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags) &&
324eb0570c7SDamien Le Moal 	    zone->cond == BLK_ZONE_COND_FULL)
325eb0570c7SDamien Le Moal 		goto unlock;
326eb0570c7SDamien Le Moal 
327eb0570c7SDamien Le Moal 	if (vfs_truncate(&zone->file->f_path, zlo->zone_size << SECTOR_SHIFT)) {
328eb0570c7SDamien Le Moal 		set_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags);
329eb0570c7SDamien Le Moal 		ret = -EIO;
330eb0570c7SDamien Le Moal 		goto unlock;
331eb0570c7SDamien Le Moal 	}
332eb0570c7SDamien Le Moal 
333eb0570c7SDamien Le Moal 	zone->cond = BLK_ZONE_COND_FULL;
334866d6574SDamien Le Moal 	zone->wp = ULLONG_MAX;
335eb0570c7SDamien Le Moal 	clear_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags);
336eb0570c7SDamien Le Moal 
337eb0570c7SDamien Le Moal  unlock:
338eb0570c7SDamien Le Moal 	mutex_unlock(&zone->lock);
339eb0570c7SDamien Le Moal 
340eb0570c7SDamien Le Moal 	return ret;
341eb0570c7SDamien Le Moal }
342eb0570c7SDamien Le Moal 
343eb0570c7SDamien Le Moal static void zloop_put_cmd(struct zloop_cmd *cmd)
344eb0570c7SDamien Le Moal {
345eb0570c7SDamien Le Moal 	struct request *rq = blk_mq_rq_from_pdu(cmd);
346eb0570c7SDamien Le Moal 
347eb0570c7SDamien Le Moal 	if (!atomic_dec_and_test(&cmd->ref))
348eb0570c7SDamien Le Moal 		return;
349eb0570c7SDamien Le Moal 	kfree(cmd->bvec);
350eb0570c7SDamien Le Moal 	cmd->bvec = NULL;
351eb0570c7SDamien Le Moal 	if (likely(!blk_should_fake_timeout(rq->q)))
352eb0570c7SDamien Le Moal 		blk_mq_complete_request(rq);
353eb0570c7SDamien Le Moal }
354eb0570c7SDamien Le Moal 
355eb0570c7SDamien Le Moal static void zloop_rw_complete(struct kiocb *iocb, long ret)
356eb0570c7SDamien Le Moal {
357eb0570c7SDamien Le Moal 	struct zloop_cmd *cmd = container_of(iocb, struct zloop_cmd, iocb);
358eb0570c7SDamien Le Moal 
359eb0570c7SDamien Le Moal 	cmd->ret = ret;
360eb0570c7SDamien Le Moal 	zloop_put_cmd(cmd);
361eb0570c7SDamien Le Moal }
362eb0570c7SDamien Le Moal 
363eb0570c7SDamien Le Moal static void zloop_rw(struct zloop_cmd *cmd)
364eb0570c7SDamien Le Moal {
365eb0570c7SDamien Le Moal 	struct request *rq = blk_mq_rq_from_pdu(cmd);
366eb0570c7SDamien Le Moal 	struct zloop_device *zlo = rq->q->queuedata;
367eb0570c7SDamien Le Moal 	unsigned int zone_no = rq_zone_no(rq);
368eb0570c7SDamien Le Moal 	sector_t sector = blk_rq_pos(rq);
369eb0570c7SDamien Le Moal 	sector_t nr_sectors = blk_rq_sectors(rq);
370eb0570c7SDamien Le Moal 	bool is_append = req_op(rq) == REQ_OP_ZONE_APPEND;
371eb0570c7SDamien Le Moal 	bool is_write = req_op(rq) == REQ_OP_WRITE || is_append;
372eb0570c7SDamien Le Moal 	int rw = is_write ? ITER_SOURCE : ITER_DEST;
373eb0570c7SDamien Le Moal 	struct req_iterator rq_iter;
374eb0570c7SDamien Le Moal 	struct zloop_zone *zone;
375eb0570c7SDamien Le Moal 	struct iov_iter iter;
376eb0570c7SDamien Le Moal 	struct bio_vec tmp;
377eb0570c7SDamien Le Moal 	sector_t zone_end;
378eb0570c7SDamien Le Moal 	int nr_bvec = 0;
379eb0570c7SDamien Le Moal 	int ret;
380eb0570c7SDamien Le Moal 
381eb0570c7SDamien Le Moal 	atomic_set(&cmd->ref, 2);
382eb0570c7SDamien Le Moal 	cmd->sector = sector;
383eb0570c7SDamien Le Moal 	cmd->nr_sectors = nr_sectors;
384eb0570c7SDamien Le Moal 	cmd->ret = 0;
385eb0570c7SDamien Le Moal 
386*9236c5fdSDamien Le Moal 	if (WARN_ON_ONCE(is_append && !zlo->zone_append)) {
387*9236c5fdSDamien Le Moal 		ret = -EIO;
388*9236c5fdSDamien Le Moal 		goto out;
389*9236c5fdSDamien Le Moal 	}
390*9236c5fdSDamien Le Moal 
391eb0570c7SDamien Le Moal 	/* We should never get an I/O beyond the device capacity. */
392eb0570c7SDamien Le Moal 	if (WARN_ON_ONCE(zone_no >= zlo->nr_zones)) {
393eb0570c7SDamien Le Moal 		ret = -EIO;
394eb0570c7SDamien Le Moal 		goto out;
395eb0570c7SDamien Le Moal 	}
396eb0570c7SDamien Le Moal 	zone = &zlo->zones[zone_no];
397eb0570c7SDamien Le Moal 	zone_end = zone->start + zlo->zone_capacity;
398eb0570c7SDamien Le Moal 
399eb0570c7SDamien Le Moal 	/*
400eb0570c7SDamien Le Moal 	 * The block layer should never send requests that are not fully
401eb0570c7SDamien Le Moal 	 * contained within the zone.
402eb0570c7SDamien Le Moal 	 */
403eb0570c7SDamien Le Moal 	if (WARN_ON_ONCE(sector + nr_sectors > zone->start + zlo->zone_size)) {
404eb0570c7SDamien Le Moal 		ret = -EIO;
405eb0570c7SDamien Le Moal 		goto out;
406eb0570c7SDamien Le Moal 	}
407eb0570c7SDamien Le Moal 
408eb0570c7SDamien Le Moal 	if (test_and_clear_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags)) {
409eb0570c7SDamien Le Moal 		mutex_lock(&zone->lock);
410eb0570c7SDamien Le Moal 		ret = zloop_update_seq_zone(zlo, zone_no);
411eb0570c7SDamien Le Moal 		mutex_unlock(&zone->lock);
412eb0570c7SDamien Le Moal 		if (ret)
413eb0570c7SDamien Le Moal 			goto out;
414eb0570c7SDamien Le Moal 	}
415eb0570c7SDamien Le Moal 
416eb0570c7SDamien Le Moal 	if (!test_bit(ZLOOP_ZONE_CONV, &zone->flags) && is_write) {
417eb0570c7SDamien Le Moal 		mutex_lock(&zone->lock);
418eb0570c7SDamien Le Moal 
419e3a96ca9SDamien Le Moal 		/*
420e3a96ca9SDamien Le Moal 		 * Zone append operations always go at the current write
421e3a96ca9SDamien Le Moal 		 * pointer, but regular write operations must already be
422e3a96ca9SDamien Le Moal 		 * aligned to the write pointer when submitted.
423e3a96ca9SDamien Le Moal 		 */
424eb0570c7SDamien Le Moal 		if (is_append) {
425cf28f6f9SDamien Le Moal 			if (zone->cond == BLK_ZONE_COND_FULL) {
426cf28f6f9SDamien Le Moal 				ret = -EIO;
427cf28f6f9SDamien Le Moal 				goto unlock;
428cf28f6f9SDamien Le Moal 			}
429eb0570c7SDamien Le Moal 			sector = zone->wp;
430eb0570c7SDamien Le Moal 			cmd->sector = sector;
431e3a96ca9SDamien Le Moal 		} else if (sector != zone->wp) {
432eb0570c7SDamien Le Moal 			pr_err("Zone %u: unaligned write: sect %llu, wp %llu\n",
433eb0570c7SDamien Le Moal 			       zone_no, sector, zone->wp);
434eb0570c7SDamien Le Moal 			ret = -EIO;
435eb0570c7SDamien Le Moal 			goto unlock;
436eb0570c7SDamien Le Moal 		}
437eb0570c7SDamien Le Moal 
438eb0570c7SDamien Le Moal 		/* Implicitly open the target zone. */
439eb0570c7SDamien Le Moal 		if (zone->cond == BLK_ZONE_COND_CLOSED ||
440eb0570c7SDamien Le Moal 		    zone->cond == BLK_ZONE_COND_EMPTY)
441eb0570c7SDamien Le Moal 			zone->cond = BLK_ZONE_COND_IMP_OPEN;
442eb0570c7SDamien Le Moal 
443eb0570c7SDamien Le Moal 		/*
444e3a96ca9SDamien Le Moal 		 * Advance the write pointer. If the write fails, the write
445e3a96ca9SDamien Le Moal 		 * pointer position will be corrected when the next I/O starts
446e3a96ca9SDamien Le Moal 		 * execution.
447eb0570c7SDamien Le Moal 		 */
448eb0570c7SDamien Le Moal 		zone->wp += nr_sectors;
449866d6574SDamien Le Moal 		if (zone->wp == zone_end) {
450eb0570c7SDamien Le Moal 			zone->cond = BLK_ZONE_COND_FULL;
451866d6574SDamien Le Moal 			zone->wp = ULLONG_MAX;
452866d6574SDamien Le Moal 		}
453eb0570c7SDamien Le Moal 	}
454eb0570c7SDamien Le Moal 
455eb0570c7SDamien Le Moal 	rq_for_each_bvec(tmp, rq, rq_iter)
456eb0570c7SDamien Le Moal 		nr_bvec++;
457eb0570c7SDamien Le Moal 
458eb0570c7SDamien Le Moal 	if (rq->bio != rq->biotail) {
459eb0570c7SDamien Le Moal 		struct bio_vec *bvec;
460eb0570c7SDamien Le Moal 
461eb0570c7SDamien Le Moal 		cmd->bvec = kmalloc_array(nr_bvec, sizeof(*cmd->bvec), GFP_NOIO);
462eb0570c7SDamien Le Moal 		if (!cmd->bvec) {
463eb0570c7SDamien Le Moal 			ret = -EIO;
464eb0570c7SDamien Le Moal 			goto unlock;
465eb0570c7SDamien Le Moal 		}
466eb0570c7SDamien Le Moal 
467eb0570c7SDamien Le Moal 		/*
468eb0570c7SDamien Le Moal 		 * The bios of the request may be started from the middle of
469eb0570c7SDamien Le Moal 		 * the 'bvec' because of bio splitting, so we can't directly
470eb0570c7SDamien Le Moal 		 * copy bio->bi_iov_vec to new bvec. The rq_for_each_bvec
471eb0570c7SDamien Le Moal 		 * API will take care of all details for us.
472eb0570c7SDamien Le Moal 		 */
473eb0570c7SDamien Le Moal 		bvec = cmd->bvec;
474eb0570c7SDamien Le Moal 		rq_for_each_bvec(tmp, rq, rq_iter) {
475eb0570c7SDamien Le Moal 			*bvec = tmp;
476eb0570c7SDamien Le Moal 			bvec++;
477eb0570c7SDamien Le Moal 		}
478eb0570c7SDamien Le Moal 		iov_iter_bvec(&iter, rw, cmd->bvec, nr_bvec, blk_rq_bytes(rq));
479eb0570c7SDamien Le Moal 	} else {
480eb0570c7SDamien Le Moal 		/*
481eb0570c7SDamien Le Moal 		 * Same here, this bio may be started from the middle of the
482eb0570c7SDamien Le Moal 		 * 'bvec' because of bio splitting, so offset from the bvec
483eb0570c7SDamien Le Moal 		 * must be passed to iov iterator
484eb0570c7SDamien Le Moal 		 */
485eb0570c7SDamien Le Moal 		iov_iter_bvec(&iter, rw,
486eb0570c7SDamien Le Moal 			__bvec_iter_bvec(rq->bio->bi_io_vec, rq->bio->bi_iter),
487eb0570c7SDamien Le Moal 					nr_bvec, blk_rq_bytes(rq));
488eb0570c7SDamien Le Moal 		iter.iov_offset = rq->bio->bi_iter.bi_bvec_done;
489eb0570c7SDamien Le Moal 	}
490eb0570c7SDamien Le Moal 
491eb0570c7SDamien Le Moal 	cmd->iocb.ki_pos = (sector - zone->start) << SECTOR_SHIFT;
492eb0570c7SDamien Le Moal 	cmd->iocb.ki_filp = zone->file;
493eb0570c7SDamien Le Moal 	cmd->iocb.ki_complete = zloop_rw_complete;
494eb0570c7SDamien Le Moal 	if (!zlo->buffered_io)
495eb0570c7SDamien Le Moal 		cmd->iocb.ki_flags = IOCB_DIRECT;
496eb0570c7SDamien Le Moal 	cmd->iocb.ki_ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_NONE, 0);
497eb0570c7SDamien Le Moal 
498eb0570c7SDamien Le Moal 	if (rw == ITER_SOURCE)
499eb0570c7SDamien Le Moal 		ret = zone->file->f_op->write_iter(&cmd->iocb, &iter);
500eb0570c7SDamien Le Moal 	else
501eb0570c7SDamien Le Moal 		ret = zone->file->f_op->read_iter(&cmd->iocb, &iter);
502eb0570c7SDamien Le Moal unlock:
503eb0570c7SDamien Le Moal 	if (!test_bit(ZLOOP_ZONE_CONV, &zone->flags) && is_write)
504eb0570c7SDamien Le Moal 		mutex_unlock(&zone->lock);
505eb0570c7SDamien Le Moal out:
506eb0570c7SDamien Le Moal 	if (ret != -EIOCBQUEUED)
507eb0570c7SDamien Le Moal 		zloop_rw_complete(&cmd->iocb, ret);
508eb0570c7SDamien Le Moal 	zloop_put_cmd(cmd);
509eb0570c7SDamien Le Moal }
510eb0570c7SDamien Le Moal 
511eb0570c7SDamien Le Moal static void zloop_handle_cmd(struct zloop_cmd *cmd)
512eb0570c7SDamien Le Moal {
513eb0570c7SDamien Le Moal 	struct request *rq = blk_mq_rq_from_pdu(cmd);
514eb0570c7SDamien Le Moal 	struct zloop_device *zlo = rq->q->queuedata;
515eb0570c7SDamien Le Moal 
516eb0570c7SDamien Le Moal 	switch (req_op(rq)) {
517eb0570c7SDamien Le Moal 	case REQ_OP_READ:
518eb0570c7SDamien Le Moal 	case REQ_OP_WRITE:
519eb0570c7SDamien Le Moal 	case REQ_OP_ZONE_APPEND:
520eb0570c7SDamien Le Moal 		/*
521eb0570c7SDamien Le Moal 		 * zloop_rw() always executes asynchronously or completes
522eb0570c7SDamien Le Moal 		 * directly.
523eb0570c7SDamien Le Moal 		 */
524eb0570c7SDamien Le Moal 		zloop_rw(cmd);
525eb0570c7SDamien Le Moal 		return;
526eb0570c7SDamien Le Moal 	case REQ_OP_FLUSH:
527eb0570c7SDamien Le Moal 		/*
528eb0570c7SDamien Le Moal 		 * Sync the entire FS containing the zone files instead of
529eb0570c7SDamien Le Moal 		 * walking all files
530eb0570c7SDamien Le Moal 		 */
531eb0570c7SDamien Le Moal 		cmd->ret = sync_filesystem(file_inode(zlo->data_dir)->i_sb);
532eb0570c7SDamien Le Moal 		break;
533eb0570c7SDamien Le Moal 	case REQ_OP_ZONE_RESET:
534eb0570c7SDamien Le Moal 		cmd->ret = zloop_reset_zone(zlo, rq_zone_no(rq));
535eb0570c7SDamien Le Moal 		break;
536eb0570c7SDamien Le Moal 	case REQ_OP_ZONE_RESET_ALL:
537eb0570c7SDamien Le Moal 		cmd->ret = zloop_reset_all_zones(zlo);
538eb0570c7SDamien Le Moal 		break;
539eb0570c7SDamien Le Moal 	case REQ_OP_ZONE_FINISH:
540eb0570c7SDamien Le Moal 		cmd->ret = zloop_finish_zone(zlo, rq_zone_no(rq));
541eb0570c7SDamien Le Moal 		break;
542eb0570c7SDamien Le Moal 	case REQ_OP_ZONE_OPEN:
543eb0570c7SDamien Le Moal 		cmd->ret = zloop_open_zone(zlo, rq_zone_no(rq));
544eb0570c7SDamien Le Moal 		break;
545eb0570c7SDamien Le Moal 	case REQ_OP_ZONE_CLOSE:
546eb0570c7SDamien Le Moal 		cmd->ret = zloop_close_zone(zlo, rq_zone_no(rq));
547eb0570c7SDamien Le Moal 		break;
548eb0570c7SDamien Le Moal 	default:
549eb0570c7SDamien Le Moal 		WARN_ON_ONCE(1);
550eb0570c7SDamien Le Moal 		pr_err("Unsupported operation %d\n", req_op(rq));
551eb0570c7SDamien Le Moal 		cmd->ret = -EOPNOTSUPP;
552eb0570c7SDamien Le Moal 		break;
553eb0570c7SDamien Le Moal 	}
554eb0570c7SDamien Le Moal 
555eb0570c7SDamien Le Moal 	blk_mq_complete_request(rq);
556eb0570c7SDamien Le Moal }
557eb0570c7SDamien Le Moal 
558eb0570c7SDamien Le Moal static void zloop_cmd_workfn(struct work_struct *work)
559eb0570c7SDamien Le Moal {
560eb0570c7SDamien Le Moal 	struct zloop_cmd *cmd = container_of(work, struct zloop_cmd, work);
561eb0570c7SDamien Le Moal 	int orig_flags = current->flags;
562eb0570c7SDamien Le Moal 
563eb0570c7SDamien Le Moal 	current->flags |= PF_LOCAL_THROTTLE | PF_MEMALLOC_NOIO;
564eb0570c7SDamien Le Moal 	zloop_handle_cmd(cmd);
565eb0570c7SDamien Le Moal 	current->flags = orig_flags;
566eb0570c7SDamien Le Moal }
567eb0570c7SDamien Le Moal 
568eb0570c7SDamien Le Moal static void zloop_complete_rq(struct request *rq)
569eb0570c7SDamien Le Moal {
570eb0570c7SDamien Le Moal 	struct zloop_cmd *cmd = blk_mq_rq_to_pdu(rq);
571eb0570c7SDamien Le Moal 	struct zloop_device *zlo = rq->q->queuedata;
572eb0570c7SDamien Le Moal 	unsigned int zone_no = cmd->sector >> zlo->zone_shift;
573eb0570c7SDamien Le Moal 	struct zloop_zone *zone = &zlo->zones[zone_no];
574eb0570c7SDamien Le Moal 	blk_status_t sts = BLK_STS_OK;
575eb0570c7SDamien Le Moal 
576eb0570c7SDamien Le Moal 	switch (req_op(rq)) {
577eb0570c7SDamien Le Moal 	case REQ_OP_READ:
578eb0570c7SDamien Le Moal 		if (cmd->ret < 0)
579eb0570c7SDamien Le Moal 			pr_err("Zone %u: failed read sector %llu, %llu sectors\n",
580eb0570c7SDamien Le Moal 			       zone_no, cmd->sector, cmd->nr_sectors);
581eb0570c7SDamien Le Moal 
582eb0570c7SDamien Le Moal 		if (cmd->ret >= 0 && cmd->ret != blk_rq_bytes(rq)) {
583eb0570c7SDamien Le Moal 			/* short read */
584eb0570c7SDamien Le Moal 			struct bio *bio;
585eb0570c7SDamien Le Moal 
586eb0570c7SDamien Le Moal 			__rq_for_each_bio(bio, rq)
587eb0570c7SDamien Le Moal 				zero_fill_bio(bio);
588eb0570c7SDamien Le Moal 		}
589eb0570c7SDamien Le Moal 		break;
590eb0570c7SDamien Le Moal 	case REQ_OP_WRITE:
591eb0570c7SDamien Le Moal 	case REQ_OP_ZONE_APPEND:
592eb0570c7SDamien Le Moal 		if (cmd->ret < 0)
593eb0570c7SDamien Le Moal 			pr_err("Zone %u: failed %swrite sector %llu, %llu sectors\n",
594eb0570c7SDamien Le Moal 			       zone_no,
595eb0570c7SDamien Le Moal 			       req_op(rq) == REQ_OP_WRITE ? "" : "append ",
596eb0570c7SDamien Le Moal 			       cmd->sector, cmd->nr_sectors);
597eb0570c7SDamien Le Moal 
598eb0570c7SDamien Le Moal 		if (cmd->ret >= 0 && cmd->ret != blk_rq_bytes(rq)) {
599eb0570c7SDamien Le Moal 			pr_err("Zone %u: partial write %ld/%u B\n",
600eb0570c7SDamien Le Moal 			       zone_no, cmd->ret, blk_rq_bytes(rq));
601eb0570c7SDamien Le Moal 			cmd->ret = -EIO;
602eb0570c7SDamien Le Moal 		}
603eb0570c7SDamien Le Moal 
604eb0570c7SDamien Le Moal 		if (cmd->ret < 0 && !test_bit(ZLOOP_ZONE_CONV, &zone->flags)) {
605eb0570c7SDamien Le Moal 			/*
606eb0570c7SDamien Le Moal 			 * A write to a sequential zone file failed: mark the
607eb0570c7SDamien Le Moal 			 * zone as having an error. This will be corrected and
608eb0570c7SDamien Le Moal 			 * cleared when the next IO is submitted.
609eb0570c7SDamien Le Moal 			 */
610eb0570c7SDamien Le Moal 			set_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags);
611eb0570c7SDamien Le Moal 			break;
612eb0570c7SDamien Le Moal 		}
613eb0570c7SDamien Le Moal 		if (req_op(rq) == REQ_OP_ZONE_APPEND)
614eb0570c7SDamien Le Moal 			rq->__sector = cmd->sector;
615eb0570c7SDamien Le Moal 
616eb0570c7SDamien Le Moal 		break;
617eb0570c7SDamien Le Moal 	default:
618eb0570c7SDamien Le Moal 		break;
619eb0570c7SDamien Le Moal 	}
620eb0570c7SDamien Le Moal 
621eb0570c7SDamien Le Moal 	if (cmd->ret < 0)
622eb0570c7SDamien Le Moal 		sts = errno_to_blk_status(cmd->ret);
623eb0570c7SDamien Le Moal 	blk_mq_end_request(rq, sts);
624eb0570c7SDamien Le Moal }
625eb0570c7SDamien Le Moal 
626eb0570c7SDamien Le Moal static blk_status_t zloop_queue_rq(struct blk_mq_hw_ctx *hctx,
627eb0570c7SDamien Le Moal 				   const struct blk_mq_queue_data *bd)
628eb0570c7SDamien Le Moal {
629eb0570c7SDamien Le Moal 	struct request *rq = bd->rq;
630eb0570c7SDamien Le Moal 	struct zloop_cmd *cmd = blk_mq_rq_to_pdu(rq);
631eb0570c7SDamien Le Moal 	struct zloop_device *zlo = rq->q->queuedata;
632eb0570c7SDamien Le Moal 
633eb0570c7SDamien Le Moal 	if (zlo->state == Zlo_deleting)
634eb0570c7SDamien Le Moal 		return BLK_STS_IOERR;
635eb0570c7SDamien Le Moal 
636eb0570c7SDamien Le Moal 	blk_mq_start_request(rq);
637eb0570c7SDamien Le Moal 
638eb0570c7SDamien Le Moal 	INIT_WORK(&cmd->work, zloop_cmd_workfn);
639eb0570c7SDamien Le Moal 	queue_work(zlo->workqueue, &cmd->work);
640eb0570c7SDamien Le Moal 
641eb0570c7SDamien Le Moal 	return BLK_STS_OK;
642eb0570c7SDamien Le Moal }
643eb0570c7SDamien Le Moal 
644eb0570c7SDamien Le Moal static const struct blk_mq_ops zloop_mq_ops = {
645eb0570c7SDamien Le Moal 	.queue_rq       = zloop_queue_rq,
646eb0570c7SDamien Le Moal 	.complete	= zloop_complete_rq,
647eb0570c7SDamien Le Moal };
648eb0570c7SDamien Le Moal 
649eb0570c7SDamien Le Moal static int zloop_open(struct gendisk *disk, blk_mode_t mode)
650eb0570c7SDamien Le Moal {
651eb0570c7SDamien Le Moal 	struct zloop_device *zlo = disk->private_data;
652eb0570c7SDamien Le Moal 	int ret;
653eb0570c7SDamien Le Moal 
654eb0570c7SDamien Le Moal 	ret = mutex_lock_killable(&zloop_ctl_mutex);
655eb0570c7SDamien Le Moal 	if (ret)
656eb0570c7SDamien Le Moal 		return ret;
657eb0570c7SDamien Le Moal 
658eb0570c7SDamien Le Moal 	if (zlo->state != Zlo_live)
659eb0570c7SDamien Le Moal 		ret = -ENXIO;
660eb0570c7SDamien Le Moal 	mutex_unlock(&zloop_ctl_mutex);
661eb0570c7SDamien Le Moal 	return ret;
662eb0570c7SDamien Le Moal }
663eb0570c7SDamien Le Moal 
664eb0570c7SDamien Le Moal static int zloop_report_zones(struct gendisk *disk, sector_t sector,
665fdb9aed8SDamien Le Moal 		unsigned int nr_zones, struct blk_report_zones_args *args)
666eb0570c7SDamien Le Moal {
667eb0570c7SDamien Le Moal 	struct zloop_device *zlo = disk->private_data;
668eb0570c7SDamien Le Moal 	struct blk_zone blkz = {};
669eb0570c7SDamien Le Moal 	unsigned int first, i;
670eb0570c7SDamien Le Moal 	int ret;
671eb0570c7SDamien Le Moal 
672eb0570c7SDamien Le Moal 	first = disk_zone_no(disk, sector);
673eb0570c7SDamien Le Moal 	if (first >= zlo->nr_zones)
674eb0570c7SDamien Le Moal 		return 0;
675eb0570c7SDamien Le Moal 	nr_zones = min(nr_zones, zlo->nr_zones - first);
676eb0570c7SDamien Le Moal 
677eb0570c7SDamien Le Moal 	for (i = 0; i < nr_zones; i++) {
678eb0570c7SDamien Le Moal 		unsigned int zone_no = first + i;
679eb0570c7SDamien Le Moal 		struct zloop_zone *zone = &zlo->zones[zone_no];
680eb0570c7SDamien Le Moal 
681eb0570c7SDamien Le Moal 		mutex_lock(&zone->lock);
682eb0570c7SDamien Le Moal 
683eb0570c7SDamien Le Moal 		if (test_and_clear_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags)) {
684eb0570c7SDamien Le Moal 			ret = zloop_update_seq_zone(zlo, zone_no);
685eb0570c7SDamien Le Moal 			if (ret) {
686eb0570c7SDamien Le Moal 				mutex_unlock(&zone->lock);
687eb0570c7SDamien Le Moal 				return ret;
688eb0570c7SDamien Le Moal 			}
689eb0570c7SDamien Le Moal 		}
690eb0570c7SDamien Le Moal 
691eb0570c7SDamien Le Moal 		blkz.start = zone->start;
692eb0570c7SDamien Le Moal 		blkz.len = zlo->zone_size;
693eb0570c7SDamien Le Moal 		blkz.wp = zone->wp;
694eb0570c7SDamien Le Moal 		blkz.cond = zone->cond;
695eb0570c7SDamien Le Moal 		if (test_bit(ZLOOP_ZONE_CONV, &zone->flags)) {
696eb0570c7SDamien Le Moal 			blkz.type = BLK_ZONE_TYPE_CONVENTIONAL;
697eb0570c7SDamien Le Moal 			blkz.capacity = zlo->zone_size;
698eb0570c7SDamien Le Moal 		} else {
699eb0570c7SDamien Le Moal 			blkz.type = BLK_ZONE_TYPE_SEQWRITE_REQ;
700eb0570c7SDamien Le Moal 			blkz.capacity = zlo->zone_capacity;
701eb0570c7SDamien Le Moal 		}
702eb0570c7SDamien Le Moal 
703eb0570c7SDamien Le Moal 		mutex_unlock(&zone->lock);
704eb0570c7SDamien Le Moal 
705fdb9aed8SDamien Le Moal 		ret = disk_report_zone(disk, &blkz, i, args);
706eb0570c7SDamien Le Moal 		if (ret)
707eb0570c7SDamien Le Moal 			return ret;
708eb0570c7SDamien Le Moal 	}
709eb0570c7SDamien Le Moal 
710eb0570c7SDamien Le Moal 	return nr_zones;
711eb0570c7SDamien Le Moal }
712eb0570c7SDamien Le Moal 
713eb0570c7SDamien Le Moal static void zloop_free_disk(struct gendisk *disk)
714eb0570c7SDamien Le Moal {
715eb0570c7SDamien Le Moal 	struct zloop_device *zlo = disk->private_data;
716eb0570c7SDamien Le Moal 	unsigned int i;
717eb0570c7SDamien Le Moal 
71876576185SShin'ichiro Kawasaki 	blk_mq_free_tag_set(&zlo->tag_set);
71976576185SShin'ichiro Kawasaki 
720eb0570c7SDamien Le Moal 	for (i = 0; i < zlo->nr_zones; i++) {
721eb0570c7SDamien Le Moal 		struct zloop_zone *zone = &zlo->zones[i];
722eb0570c7SDamien Le Moal 
723eb0570c7SDamien Le Moal 		mapping_set_gfp_mask(zone->file->f_mapping,
724eb0570c7SDamien Le Moal 				zone->old_gfp_mask);
725eb0570c7SDamien Le Moal 		fput(zone->file);
726eb0570c7SDamien Le Moal 	}
727eb0570c7SDamien Le Moal 
728eb0570c7SDamien Le Moal 	fput(zlo->data_dir);
729eb0570c7SDamien Le Moal 	destroy_workqueue(zlo->workqueue);
730eb0570c7SDamien Le Moal 	kfree(zlo->base_dir);
731eb0570c7SDamien Le Moal 	kvfree(zlo);
732eb0570c7SDamien Le Moal }
733eb0570c7SDamien Le Moal 
734eb0570c7SDamien Le Moal static const struct block_device_operations zloop_fops = {
735eb0570c7SDamien Le Moal 	.owner			= THIS_MODULE,
736eb0570c7SDamien Le Moal 	.open			= zloop_open,
737eb0570c7SDamien Le Moal 	.report_zones		= zloop_report_zones,
738eb0570c7SDamien Le Moal 	.free_disk		= zloop_free_disk,
739eb0570c7SDamien Le Moal };
740eb0570c7SDamien Le Moal 
741eb0570c7SDamien Le Moal __printf(3, 4)
742eb0570c7SDamien Le Moal static struct file *zloop_filp_open_fmt(int oflags, umode_t mode,
743eb0570c7SDamien Le Moal 		const char *fmt, ...)
744eb0570c7SDamien Le Moal {
745eb0570c7SDamien Le Moal 	struct file *file;
746eb0570c7SDamien Le Moal 	va_list ap;
747eb0570c7SDamien Le Moal 	char *p;
748eb0570c7SDamien Le Moal 
749eb0570c7SDamien Le Moal 	va_start(ap, fmt);
750eb0570c7SDamien Le Moal 	p = kvasprintf(GFP_KERNEL, fmt, ap);
751eb0570c7SDamien Le Moal 	va_end(ap);
752eb0570c7SDamien Le Moal 
753eb0570c7SDamien Le Moal 	if (!p)
754eb0570c7SDamien Le Moal 		return ERR_PTR(-ENOMEM);
755eb0570c7SDamien Le Moal 	file = filp_open(p, oflags, mode);
756eb0570c7SDamien Le Moal 	kfree(p);
757eb0570c7SDamien Le Moal 	return file;
758eb0570c7SDamien Le Moal }
759eb0570c7SDamien Le Moal 
760eb0570c7SDamien Le Moal static int zloop_get_block_size(struct zloop_device *zlo,
761eb0570c7SDamien Le Moal 				struct zloop_zone *zone)
762eb0570c7SDamien Le Moal {
763eb0570c7SDamien Le Moal 	struct block_device *sb_bdev = zone->file->f_mapping->host->i_sb->s_bdev;
764eb0570c7SDamien Le Moal 	struct kstat st;
765eb0570c7SDamien Le Moal 
766eb0570c7SDamien Le Moal 	/*
767eb0570c7SDamien Le Moal 	 * If the FS block size is lower than or equal to 4K, use that as the
768eb0570c7SDamien Le Moal 	 * device block size. Otherwise, fallback to the FS direct IO alignment
769eb0570c7SDamien Le Moal 	 * constraint if that is provided, and to the FS underlying device
770eb0570c7SDamien Le Moal 	 * physical block size if the direct IO alignment is unknown.
771eb0570c7SDamien Le Moal 	 */
772eb0570c7SDamien Le Moal 	if (file_inode(zone->file)->i_sb->s_blocksize <= SZ_4K)
773eb0570c7SDamien Le Moal 		zlo->block_size = file_inode(zone->file)->i_sb->s_blocksize;
774eb0570c7SDamien Le Moal 	else if (!vfs_getattr(&zone->file->f_path, &st, STATX_DIOALIGN, 0) &&
775eb0570c7SDamien Le Moal 		 (st.result_mask & STATX_DIOALIGN))
776eb0570c7SDamien Le Moal 		zlo->block_size = st.dio_offset_align;
777eb0570c7SDamien Le Moal 	else if (sb_bdev)
778eb0570c7SDamien Le Moal 		zlo->block_size = bdev_physical_block_size(sb_bdev);
779eb0570c7SDamien Le Moal 	else
780eb0570c7SDamien Le Moal 		zlo->block_size = SECTOR_SIZE;
781eb0570c7SDamien Le Moal 
782eb0570c7SDamien Le Moal 	if (zlo->zone_capacity & ((zlo->block_size >> SECTOR_SHIFT) - 1)) {
783eb0570c7SDamien Le Moal 		pr_err("Zone capacity is not aligned to block size %u\n",
784eb0570c7SDamien Le Moal 		       zlo->block_size);
785eb0570c7SDamien Le Moal 		return -EINVAL;
786eb0570c7SDamien Le Moal 	}
787eb0570c7SDamien Le Moal 
788eb0570c7SDamien Le Moal 	return 0;
789eb0570c7SDamien Le Moal }
790eb0570c7SDamien Le Moal 
791eb0570c7SDamien Le Moal static int zloop_init_zone(struct zloop_device *zlo, struct zloop_options *opts,
792eb0570c7SDamien Le Moal 			   unsigned int zone_no, bool restore)
793eb0570c7SDamien Le Moal {
794eb0570c7SDamien Le Moal 	struct zloop_zone *zone = &zlo->zones[zone_no];
795eb0570c7SDamien Le Moal 	int oflags = O_RDWR;
796eb0570c7SDamien Le Moal 	struct kstat stat;
797eb0570c7SDamien Le Moal 	sector_t file_sectors;
798eb0570c7SDamien Le Moal 	int ret;
799eb0570c7SDamien Le Moal 
800eb0570c7SDamien Le Moal 	mutex_init(&zone->lock);
801eb0570c7SDamien Le Moal 	zone->start = (sector_t)zone_no << zlo->zone_shift;
802eb0570c7SDamien Le Moal 
803eb0570c7SDamien Le Moal 	if (!restore)
804eb0570c7SDamien Le Moal 		oflags |= O_CREAT;
805eb0570c7SDamien Le Moal 
806eb0570c7SDamien Le Moal 	if (!opts->buffered_io)
807eb0570c7SDamien Le Moal 		oflags |= O_DIRECT;
808eb0570c7SDamien Le Moal 
809eb0570c7SDamien Le Moal 	if (zone_no < zlo->nr_conv_zones) {
810eb0570c7SDamien Le Moal 		/* Conventional zone file. */
811eb0570c7SDamien Le Moal 		set_bit(ZLOOP_ZONE_CONV, &zone->flags);
812eb0570c7SDamien Le Moal 		zone->cond = BLK_ZONE_COND_NOT_WP;
813eb0570c7SDamien Le Moal 		zone->wp = U64_MAX;
814eb0570c7SDamien Le Moal 
815eb0570c7SDamien Le Moal 		zone->file = zloop_filp_open_fmt(oflags, 0600, "%s/%u/cnv-%06u",
816eb0570c7SDamien Le Moal 					zlo->base_dir, zlo->id, zone_no);
817eb0570c7SDamien Le Moal 		if (IS_ERR(zone->file)) {
818eb0570c7SDamien Le Moal 			pr_err("Failed to open zone %u file %s/%u/cnv-%06u (err=%ld)",
819eb0570c7SDamien Le Moal 			       zone_no, zlo->base_dir, zlo->id, zone_no,
820eb0570c7SDamien Le Moal 			       PTR_ERR(zone->file));
821eb0570c7SDamien Le Moal 			return PTR_ERR(zone->file);
822eb0570c7SDamien Le Moal 		}
823eb0570c7SDamien Le Moal 
824eb0570c7SDamien Le Moal 		if (!zlo->block_size) {
825eb0570c7SDamien Le Moal 			ret = zloop_get_block_size(zlo, zone);
826eb0570c7SDamien Le Moal 			if (ret)
827eb0570c7SDamien Le Moal 				return ret;
828eb0570c7SDamien Le Moal 		}
829eb0570c7SDamien Le Moal 
830eb0570c7SDamien Le Moal 		ret = vfs_getattr(&zone->file->f_path, &stat, STATX_SIZE, 0);
831eb0570c7SDamien Le Moal 		if (ret < 0) {
832eb0570c7SDamien Le Moal 			pr_err("Failed to get zone %u file stat\n", zone_no);
833eb0570c7SDamien Le Moal 			return ret;
834eb0570c7SDamien Le Moal 		}
835eb0570c7SDamien Le Moal 		file_sectors = stat.size >> SECTOR_SHIFT;
836eb0570c7SDamien Le Moal 
837eb0570c7SDamien Le Moal 		if (restore && file_sectors != zlo->zone_size) {
838eb0570c7SDamien Le Moal 			pr_err("Invalid conventional zone %u file size (%llu sectors != %llu)\n",
839eb0570c7SDamien Le Moal 			       zone_no, file_sectors, zlo->zone_capacity);
840eb0570c7SDamien Le Moal 			return ret;
841eb0570c7SDamien Le Moal 		}
842eb0570c7SDamien Le Moal 
843eb0570c7SDamien Le Moal 		ret = vfs_truncate(&zone->file->f_path,
844eb0570c7SDamien Le Moal 				   zlo->zone_size << SECTOR_SHIFT);
845eb0570c7SDamien Le Moal 		if (ret < 0) {
846eb0570c7SDamien Le Moal 			pr_err("Failed to truncate zone %u file (err=%d)\n",
847eb0570c7SDamien Le Moal 			       zone_no, ret);
848eb0570c7SDamien Le Moal 			return ret;
849eb0570c7SDamien Le Moal 		}
850eb0570c7SDamien Le Moal 
851eb0570c7SDamien Le Moal 		return 0;
852eb0570c7SDamien Le Moal 	}
853eb0570c7SDamien Le Moal 
854eb0570c7SDamien Le Moal 	/* Sequential zone file. */
855eb0570c7SDamien Le Moal 	zone->file = zloop_filp_open_fmt(oflags, 0600, "%s/%u/seq-%06u",
856eb0570c7SDamien Le Moal 					 zlo->base_dir, zlo->id, zone_no);
857eb0570c7SDamien Le Moal 	if (IS_ERR(zone->file)) {
858eb0570c7SDamien Le Moal 		pr_err("Failed to open zone %u file %s/%u/seq-%06u (err=%ld)",
859eb0570c7SDamien Le Moal 		       zone_no, zlo->base_dir, zlo->id, zone_no,
860eb0570c7SDamien Le Moal 		       PTR_ERR(zone->file));
861eb0570c7SDamien Le Moal 		return PTR_ERR(zone->file);
862eb0570c7SDamien Le Moal 	}
863eb0570c7SDamien Le Moal 
864eb0570c7SDamien Le Moal 	if (!zlo->block_size) {
865eb0570c7SDamien Le Moal 		ret = zloop_get_block_size(zlo, zone);
866eb0570c7SDamien Le Moal 		if (ret)
867eb0570c7SDamien Le Moal 			return ret;
868eb0570c7SDamien Le Moal 	}
869eb0570c7SDamien Le Moal 
870eb0570c7SDamien Le Moal 	zloop_get_block_size(zlo, zone);
871eb0570c7SDamien Le Moal 
872eb0570c7SDamien Le Moal 	mutex_lock(&zone->lock);
873eb0570c7SDamien Le Moal 	ret = zloop_update_seq_zone(zlo, zone_no);
874eb0570c7SDamien Le Moal 	mutex_unlock(&zone->lock);
875eb0570c7SDamien Le Moal 
876eb0570c7SDamien Le Moal 	return ret;
877eb0570c7SDamien Le Moal }
878eb0570c7SDamien Le Moal 
879eb0570c7SDamien Le Moal static bool zloop_dev_exists(struct zloop_device *zlo)
880eb0570c7SDamien Le Moal {
881eb0570c7SDamien Le Moal 	struct file *cnv, *seq;
882eb0570c7SDamien Le Moal 	bool exists;
883eb0570c7SDamien Le Moal 
884eb0570c7SDamien Le Moal 	cnv = zloop_filp_open_fmt(O_RDONLY, 0600, "%s/%u/cnv-%06u",
885eb0570c7SDamien Le Moal 				  zlo->base_dir, zlo->id, 0);
886eb0570c7SDamien Le Moal 	seq = zloop_filp_open_fmt(O_RDONLY, 0600, "%s/%u/seq-%06u",
887eb0570c7SDamien Le Moal 				  zlo->base_dir, zlo->id, 0);
888eb0570c7SDamien Le Moal 	exists = !IS_ERR(cnv) || !IS_ERR(seq);
889eb0570c7SDamien Le Moal 
890eb0570c7SDamien Le Moal 	if (!IS_ERR(cnv))
891eb0570c7SDamien Le Moal 		fput(cnv);
892eb0570c7SDamien Le Moal 	if (!IS_ERR(seq))
893eb0570c7SDamien Le Moal 		fput(seq);
894eb0570c7SDamien Le Moal 
895eb0570c7SDamien Le Moal 	return exists;
896eb0570c7SDamien Le Moal }
897eb0570c7SDamien Le Moal 
898eb0570c7SDamien Le Moal static int zloop_ctl_add(struct zloop_options *opts)
899eb0570c7SDamien Le Moal {
900eb0570c7SDamien Le Moal 	struct queue_limits lim = {
901eb0570c7SDamien Le Moal 		.max_hw_sectors		= SZ_1M >> SECTOR_SHIFT,
902eb0570c7SDamien Le Moal 		.chunk_sectors		= opts->zone_size,
903eb0570c7SDamien Le Moal 		.features		= BLK_FEAT_ZONED,
904eb0570c7SDamien Le Moal 	};
905eb0570c7SDamien Le Moal 	unsigned int nr_zones, i, j;
906eb0570c7SDamien Le Moal 	struct zloop_device *zlo;
907eb0570c7SDamien Le Moal 	int ret = -EINVAL;
908eb0570c7SDamien Le Moal 	bool restore;
909eb0570c7SDamien Le Moal 
910eb0570c7SDamien Le Moal 	__module_get(THIS_MODULE);
911eb0570c7SDamien Le Moal 
912eb0570c7SDamien Le Moal 	nr_zones = opts->capacity >> ilog2(opts->zone_size);
913eb0570c7SDamien Le Moal 	if (opts->nr_conv_zones >= nr_zones) {
914eb0570c7SDamien Le Moal 		pr_err("Invalid number of conventional zones %u\n",
915eb0570c7SDamien Le Moal 		       opts->nr_conv_zones);
916eb0570c7SDamien Le Moal 		goto out;
917eb0570c7SDamien Le Moal 	}
918eb0570c7SDamien Le Moal 
919eb0570c7SDamien Le Moal 	zlo = kvzalloc(struct_size(zlo, zones, nr_zones), GFP_KERNEL);
920eb0570c7SDamien Le Moal 	if (!zlo) {
921eb0570c7SDamien Le Moal 		ret = -ENOMEM;
922eb0570c7SDamien Le Moal 		goto out;
923eb0570c7SDamien Le Moal 	}
924eb0570c7SDamien Le Moal 	zlo->state = Zlo_creating;
925eb0570c7SDamien Le Moal 
926eb0570c7SDamien Le Moal 	ret = mutex_lock_killable(&zloop_ctl_mutex);
927eb0570c7SDamien Le Moal 	if (ret)
928eb0570c7SDamien Le Moal 		goto out_free_dev;
929eb0570c7SDamien Le Moal 
930eb0570c7SDamien Le Moal 	/* Allocate id, if @opts->id >= 0, we're requesting that specific id */
931eb0570c7SDamien Le Moal 	if (opts->id >= 0) {
932eb0570c7SDamien Le Moal 		ret = idr_alloc(&zloop_index_idr, zlo,
933eb0570c7SDamien Le Moal 				  opts->id, opts->id + 1, GFP_KERNEL);
934eb0570c7SDamien Le Moal 		if (ret == -ENOSPC)
935eb0570c7SDamien Le Moal 			ret = -EEXIST;
936eb0570c7SDamien Le Moal 	} else {
937eb0570c7SDamien Le Moal 		ret = idr_alloc(&zloop_index_idr, zlo, 0, 0, GFP_KERNEL);
938eb0570c7SDamien Le Moal 	}
939eb0570c7SDamien Le Moal 	mutex_unlock(&zloop_ctl_mutex);
940eb0570c7SDamien Le Moal 	if (ret < 0)
941eb0570c7SDamien Le Moal 		goto out_free_dev;
942eb0570c7SDamien Le Moal 
943eb0570c7SDamien Le Moal 	zlo->id = ret;
944eb0570c7SDamien Le Moal 	zlo->zone_shift = ilog2(opts->zone_size);
945eb0570c7SDamien Le Moal 	zlo->zone_size = opts->zone_size;
946eb0570c7SDamien Le Moal 	if (opts->zone_capacity)
947eb0570c7SDamien Le Moal 		zlo->zone_capacity = opts->zone_capacity;
948eb0570c7SDamien Le Moal 	else
949eb0570c7SDamien Le Moal 		zlo->zone_capacity = zlo->zone_size;
950eb0570c7SDamien Le Moal 	zlo->nr_zones = nr_zones;
951eb0570c7SDamien Le Moal 	zlo->nr_conv_zones = opts->nr_conv_zones;
952eb0570c7SDamien Le Moal 	zlo->buffered_io = opts->buffered_io;
953*9236c5fdSDamien Le Moal 	zlo->zone_append = opts->zone_append;
954eb0570c7SDamien Le Moal 
955eb0570c7SDamien Le Moal 	zlo->workqueue = alloc_workqueue("zloop%d", WQ_UNBOUND | WQ_FREEZABLE,
956eb0570c7SDamien Le Moal 				opts->nr_queues * opts->queue_depth, zlo->id);
957eb0570c7SDamien Le Moal 	if (!zlo->workqueue) {
958eb0570c7SDamien Le Moal 		ret = -ENOMEM;
959eb0570c7SDamien Le Moal 		goto out_free_idr;
960eb0570c7SDamien Le Moal 	}
961eb0570c7SDamien Le Moal 
962eb0570c7SDamien Le Moal 	if (opts->base_dir)
963eb0570c7SDamien Le Moal 		zlo->base_dir = kstrdup(opts->base_dir, GFP_KERNEL);
964eb0570c7SDamien Le Moal 	else
965eb0570c7SDamien Le Moal 		zlo->base_dir = kstrdup(ZLOOP_DEF_BASE_DIR, GFP_KERNEL);
966eb0570c7SDamien Le Moal 	if (!zlo->base_dir) {
967eb0570c7SDamien Le Moal 		ret = -ENOMEM;
968eb0570c7SDamien Le Moal 		goto out_destroy_workqueue;
969eb0570c7SDamien Le Moal 	}
970eb0570c7SDamien Le Moal 
971eb0570c7SDamien Le Moal 	zlo->data_dir = zloop_filp_open_fmt(O_RDONLY | O_DIRECTORY, 0, "%s/%u",
972eb0570c7SDamien Le Moal 					    zlo->base_dir, zlo->id);
973eb0570c7SDamien Le Moal 	if (IS_ERR(zlo->data_dir)) {
974eb0570c7SDamien Le Moal 		ret = PTR_ERR(zlo->data_dir);
975eb0570c7SDamien Le Moal 		pr_warn("Failed to open directory %s/%u (err=%d)\n",
976eb0570c7SDamien Le Moal 			zlo->base_dir, zlo->id, ret);
977eb0570c7SDamien Le Moal 		goto out_free_base_dir;
978eb0570c7SDamien Le Moal 	}
979eb0570c7SDamien Le Moal 
980eb0570c7SDamien Le Moal 	/*
981eb0570c7SDamien Le Moal 	 * If we already have zone files, we are restoring a device created by a
982eb0570c7SDamien Le Moal 	 * previous add operation. In this case, zloop_init_zone() will check
983eb0570c7SDamien Le Moal 	 * that the zone files are consistent with the zone configuration given.
984eb0570c7SDamien Le Moal 	 */
985eb0570c7SDamien Le Moal 	restore = zloop_dev_exists(zlo);
986eb0570c7SDamien Le Moal 	for (i = 0; i < nr_zones; i++) {
987eb0570c7SDamien Le Moal 		ret = zloop_init_zone(zlo, opts, i, restore);
988eb0570c7SDamien Le Moal 		if (ret)
989eb0570c7SDamien Le Moal 			goto out_close_files;
990eb0570c7SDamien Le Moal 	}
991eb0570c7SDamien Le Moal 
992eb0570c7SDamien Le Moal 	lim.physical_block_size = zlo->block_size;
993eb0570c7SDamien Le Moal 	lim.logical_block_size = zlo->block_size;
994*9236c5fdSDamien Le Moal 	if (zlo->zone_append)
995*9236c5fdSDamien Le Moal 		lim.max_hw_zone_append_sectors = lim.max_hw_sectors;
996eb0570c7SDamien Le Moal 
997eb0570c7SDamien Le Moal 	zlo->tag_set.ops = &zloop_mq_ops;
998eb0570c7SDamien Le Moal 	zlo->tag_set.nr_hw_queues = opts->nr_queues;
999eb0570c7SDamien Le Moal 	zlo->tag_set.queue_depth = opts->queue_depth;
1000eb0570c7SDamien Le Moal 	zlo->tag_set.numa_node = NUMA_NO_NODE;
1001eb0570c7SDamien Le Moal 	zlo->tag_set.cmd_size = sizeof(struct zloop_cmd);
1002eb0570c7SDamien Le Moal 	zlo->tag_set.driver_data = zlo;
1003eb0570c7SDamien Le Moal 
1004eb0570c7SDamien Le Moal 	ret = blk_mq_alloc_tag_set(&zlo->tag_set);
1005eb0570c7SDamien Le Moal 	if (ret) {
1006eb0570c7SDamien Le Moal 		pr_err("blk_mq_alloc_tag_set failed (err=%d)\n", ret);
1007eb0570c7SDamien Le Moal 		goto out_close_files;
1008eb0570c7SDamien Le Moal 	}
1009eb0570c7SDamien Le Moal 
1010eb0570c7SDamien Le Moal 	zlo->disk = blk_mq_alloc_disk(&zlo->tag_set, &lim, zlo);
1011eb0570c7SDamien Le Moal 	if (IS_ERR(zlo->disk)) {
1012eb0570c7SDamien Le Moal 		pr_err("blk_mq_alloc_disk failed (err=%d)\n", ret);
1013eb0570c7SDamien Le Moal 		ret = PTR_ERR(zlo->disk);
1014eb0570c7SDamien Le Moal 		goto out_cleanup_tags;
1015eb0570c7SDamien Le Moal 	}
1016eb0570c7SDamien Le Moal 	zlo->disk->flags = GENHD_FL_NO_PART;
1017eb0570c7SDamien Le Moal 	zlo->disk->fops = &zloop_fops;
1018eb0570c7SDamien Le Moal 	zlo->disk->private_data = zlo;
1019eb0570c7SDamien Le Moal 	sprintf(zlo->disk->disk_name, "zloop%d", zlo->id);
1020eb0570c7SDamien Le Moal 	set_capacity(zlo->disk, (u64)lim.chunk_sectors * zlo->nr_zones);
1021eb0570c7SDamien Le Moal 
1022eb0570c7SDamien Le Moal 	ret = blk_revalidate_disk_zones(zlo->disk);
1023eb0570c7SDamien Le Moal 	if (ret)
1024eb0570c7SDamien Le Moal 		goto out_cleanup_disk;
1025eb0570c7SDamien Le Moal 
1026eb0570c7SDamien Le Moal 	ret = add_disk(zlo->disk);
1027eb0570c7SDamien Le Moal 	if (ret) {
1028eb0570c7SDamien Le Moal 		pr_err("add_disk failed (err=%d)\n", ret);
1029eb0570c7SDamien Le Moal 		goto out_cleanup_disk;
1030eb0570c7SDamien Le Moal 	}
1031eb0570c7SDamien Le Moal 
1032eb0570c7SDamien Le Moal 	mutex_lock(&zloop_ctl_mutex);
1033eb0570c7SDamien Le Moal 	zlo->state = Zlo_live;
1034eb0570c7SDamien Le Moal 	mutex_unlock(&zloop_ctl_mutex);
1035eb0570c7SDamien Le Moal 
1036*9236c5fdSDamien Le Moal 	pr_info("zloop: device %d, %u zones of %llu MiB, %u B block size\n",
1037eb0570c7SDamien Le Moal 		zlo->id, zlo->nr_zones,
1038eb0570c7SDamien Le Moal 		((sector_t)zlo->zone_size << SECTOR_SHIFT) >> 20,
1039eb0570c7SDamien Le Moal 		zlo->block_size);
1040*9236c5fdSDamien Le Moal 	pr_info("zloop%d: using %s zone append\n",
1041*9236c5fdSDamien Le Moal 		zlo->id,
1042*9236c5fdSDamien Le Moal 		zlo->zone_append ? "native" : "emulated");
1043eb0570c7SDamien Le Moal 
1044eb0570c7SDamien Le Moal 	return 0;
1045eb0570c7SDamien Le Moal 
1046eb0570c7SDamien Le Moal out_cleanup_disk:
1047eb0570c7SDamien Le Moal 	put_disk(zlo->disk);
1048eb0570c7SDamien Le Moal out_cleanup_tags:
1049eb0570c7SDamien Le Moal 	blk_mq_free_tag_set(&zlo->tag_set);
1050eb0570c7SDamien Le Moal out_close_files:
1051eb0570c7SDamien Le Moal 	for (j = 0; j < i; j++) {
1052eb0570c7SDamien Le Moal 		struct zloop_zone *zone = &zlo->zones[j];
1053eb0570c7SDamien Le Moal 
1054eb0570c7SDamien Le Moal 		if (!IS_ERR_OR_NULL(zone->file))
1055eb0570c7SDamien Le Moal 			fput(zone->file);
1056eb0570c7SDamien Le Moal 	}
1057eb0570c7SDamien Le Moal 	fput(zlo->data_dir);
1058eb0570c7SDamien Le Moal out_free_base_dir:
1059eb0570c7SDamien Le Moal 	kfree(zlo->base_dir);
1060eb0570c7SDamien Le Moal out_destroy_workqueue:
1061eb0570c7SDamien Le Moal 	destroy_workqueue(zlo->workqueue);
1062eb0570c7SDamien Le Moal out_free_idr:
1063eb0570c7SDamien Le Moal 	mutex_lock(&zloop_ctl_mutex);
1064eb0570c7SDamien Le Moal 	idr_remove(&zloop_index_idr, zlo->id);
1065eb0570c7SDamien Le Moal 	mutex_unlock(&zloop_ctl_mutex);
1066eb0570c7SDamien Le Moal out_free_dev:
1067eb0570c7SDamien Le Moal 	kvfree(zlo);
1068eb0570c7SDamien Le Moal out:
1069eb0570c7SDamien Le Moal 	module_put(THIS_MODULE);
1070eb0570c7SDamien Le Moal 	if (ret == -ENOENT)
1071eb0570c7SDamien Le Moal 		ret = -EINVAL;
1072eb0570c7SDamien Le Moal 	return ret;
1073eb0570c7SDamien Le Moal }
1074eb0570c7SDamien Le Moal 
1075eb0570c7SDamien Le Moal static int zloop_ctl_remove(struct zloop_options *opts)
1076eb0570c7SDamien Le Moal {
1077eb0570c7SDamien Le Moal 	struct zloop_device *zlo;
1078eb0570c7SDamien Le Moal 	int ret;
1079eb0570c7SDamien Le Moal 
1080eb0570c7SDamien Le Moal 	if (!(opts->mask & ZLOOP_OPT_ID)) {
1081eb0570c7SDamien Le Moal 		pr_err("No ID specified\n");
1082eb0570c7SDamien Le Moal 		return -EINVAL;
1083eb0570c7SDamien Le Moal 	}
1084eb0570c7SDamien Le Moal 
1085eb0570c7SDamien Le Moal 	ret = mutex_lock_killable(&zloop_ctl_mutex);
1086eb0570c7SDamien Le Moal 	if (ret)
1087eb0570c7SDamien Le Moal 		return ret;
1088eb0570c7SDamien Le Moal 
1089eb0570c7SDamien Le Moal 	zlo = idr_find(&zloop_index_idr, opts->id);
1090eb0570c7SDamien Le Moal 	if (!zlo || zlo->state == Zlo_creating) {
1091eb0570c7SDamien Le Moal 		ret = -ENODEV;
1092eb0570c7SDamien Le Moal 	} else if (zlo->state == Zlo_deleting) {
1093eb0570c7SDamien Le Moal 		ret = -EINVAL;
1094eb0570c7SDamien Le Moal 	} else {
1095eb0570c7SDamien Le Moal 		idr_remove(&zloop_index_idr, zlo->id);
1096eb0570c7SDamien Le Moal 		zlo->state = Zlo_deleting;
1097eb0570c7SDamien Le Moal 	}
1098eb0570c7SDamien Le Moal 
1099eb0570c7SDamien Le Moal 	mutex_unlock(&zloop_ctl_mutex);
1100eb0570c7SDamien Le Moal 	if (ret)
1101eb0570c7SDamien Le Moal 		return ret;
1102eb0570c7SDamien Le Moal 
1103eb0570c7SDamien Le Moal 	del_gendisk(zlo->disk);
1104eb0570c7SDamien Le Moal 	put_disk(zlo->disk);
1105eb0570c7SDamien Le Moal 
1106eb0570c7SDamien Le Moal 	pr_info("Removed device %d\n", opts->id);
1107eb0570c7SDamien Le Moal 
1108eb0570c7SDamien Le Moal 	module_put(THIS_MODULE);
1109eb0570c7SDamien Le Moal 
1110eb0570c7SDamien Le Moal 	return 0;
1111eb0570c7SDamien Le Moal }
1112eb0570c7SDamien Le Moal 
1113eb0570c7SDamien Le Moal static int zloop_parse_options(struct zloop_options *opts, const char *buf)
1114eb0570c7SDamien Le Moal {
1115eb0570c7SDamien Le Moal 	substring_t args[MAX_OPT_ARGS];
1116eb0570c7SDamien Le Moal 	char *options, *o, *p;
1117eb0570c7SDamien Le Moal 	unsigned int token;
1118eb0570c7SDamien Le Moal 	int ret = 0;
1119eb0570c7SDamien Le Moal 
1120eb0570c7SDamien Le Moal 	/* Set defaults. */
1121eb0570c7SDamien Le Moal 	opts->mask = 0;
1122eb0570c7SDamien Le Moal 	opts->id = ZLOOP_DEF_ID;
1123eb0570c7SDamien Le Moal 	opts->capacity = ZLOOP_DEF_ZONE_SIZE * ZLOOP_DEF_NR_ZONES;
1124eb0570c7SDamien Le Moal 	opts->zone_size = ZLOOP_DEF_ZONE_SIZE;
1125eb0570c7SDamien Le Moal 	opts->nr_conv_zones = ZLOOP_DEF_NR_CONV_ZONES;
1126eb0570c7SDamien Le Moal 	opts->nr_queues = ZLOOP_DEF_NR_QUEUES;
1127eb0570c7SDamien Le Moal 	opts->queue_depth = ZLOOP_DEF_QUEUE_DEPTH;
1128eb0570c7SDamien Le Moal 	opts->buffered_io = ZLOOP_DEF_BUFFERED_IO;
1129*9236c5fdSDamien Le Moal 	opts->zone_append = ZLOOP_DEF_ZONE_APPEND;
1130eb0570c7SDamien Le Moal 
1131eb0570c7SDamien Le Moal 	if (!buf)
1132eb0570c7SDamien Le Moal 		return 0;
1133eb0570c7SDamien Le Moal 
1134eb0570c7SDamien Le Moal 	/* Skip leading spaces before the options. */
1135eb0570c7SDamien Le Moal 	while (isspace(*buf))
1136eb0570c7SDamien Le Moal 		buf++;
1137eb0570c7SDamien Le Moal 
1138eb0570c7SDamien Le Moal 	options = o = kstrdup(buf, GFP_KERNEL);
1139eb0570c7SDamien Le Moal 	if (!options)
1140eb0570c7SDamien Le Moal 		return -ENOMEM;
1141eb0570c7SDamien Le Moal 
1142eb0570c7SDamien Le Moal 	/* Parse the options, doing only some light invalid value checks. */
1143eb0570c7SDamien Le Moal 	while ((p = strsep(&o, ",\n")) != NULL) {
1144eb0570c7SDamien Le Moal 		if (!*p)
1145eb0570c7SDamien Le Moal 			continue;
1146eb0570c7SDamien Le Moal 
1147eb0570c7SDamien Le Moal 		token = match_token(p, zloop_opt_tokens, args);
1148eb0570c7SDamien Le Moal 		opts->mask |= token;
1149eb0570c7SDamien Le Moal 		switch (token) {
1150eb0570c7SDamien Le Moal 		case ZLOOP_OPT_ID:
1151eb0570c7SDamien Le Moal 			if (match_int(args, &opts->id)) {
1152eb0570c7SDamien Le Moal 				ret = -EINVAL;
1153eb0570c7SDamien Le Moal 				goto out;
1154eb0570c7SDamien Le Moal 			}
1155eb0570c7SDamien Le Moal 			break;
1156eb0570c7SDamien Le Moal 		case ZLOOP_OPT_CAPACITY:
1157eb0570c7SDamien Le Moal 			if (match_uint(args, &token)) {
1158eb0570c7SDamien Le Moal 				ret = -EINVAL;
1159eb0570c7SDamien Le Moal 				goto out;
1160eb0570c7SDamien Le Moal 			}
1161eb0570c7SDamien Le Moal 			if (!token) {
1162eb0570c7SDamien Le Moal 				pr_err("Invalid capacity\n");
1163eb0570c7SDamien Le Moal 				ret = -EINVAL;
1164eb0570c7SDamien Le Moal 				goto out;
1165eb0570c7SDamien Le Moal 			}
1166eb0570c7SDamien Le Moal 			opts->capacity =
1167eb0570c7SDamien Le Moal 				((sector_t)token * SZ_1M) >> SECTOR_SHIFT;
1168eb0570c7SDamien Le Moal 			break;
1169eb0570c7SDamien Le Moal 		case ZLOOP_OPT_ZONE_SIZE:
1170eb0570c7SDamien Le Moal 			if (match_uint(args, &token)) {
1171eb0570c7SDamien Le Moal 				ret = -EINVAL;
1172eb0570c7SDamien Le Moal 				goto out;
1173eb0570c7SDamien Le Moal 			}
1174eb0570c7SDamien Le Moal 			if (!token || token > ZLOOP_MAX_ZONE_SIZE_MB ||
1175eb0570c7SDamien Le Moal 			    !is_power_of_2(token)) {
1176eb0570c7SDamien Le Moal 				pr_err("Invalid zone size %u\n", token);
1177eb0570c7SDamien Le Moal 				ret = -EINVAL;
1178eb0570c7SDamien Le Moal 				goto out;
1179eb0570c7SDamien Le Moal 			}
1180eb0570c7SDamien Le Moal 			opts->zone_size =
1181eb0570c7SDamien Le Moal 				((sector_t)token * SZ_1M) >> SECTOR_SHIFT;
1182eb0570c7SDamien Le Moal 			break;
1183eb0570c7SDamien Le Moal 		case ZLOOP_OPT_ZONE_CAPACITY:
1184eb0570c7SDamien Le Moal 			if (match_uint(args, &token)) {
1185eb0570c7SDamien Le Moal 				ret = -EINVAL;
1186eb0570c7SDamien Le Moal 				goto out;
1187eb0570c7SDamien Le Moal 			}
1188eb0570c7SDamien Le Moal 			if (!token) {
1189eb0570c7SDamien Le Moal 				pr_err("Invalid zone capacity\n");
1190eb0570c7SDamien Le Moal 				ret = -EINVAL;
1191eb0570c7SDamien Le Moal 				goto out;
1192eb0570c7SDamien Le Moal 			}
1193eb0570c7SDamien Le Moal 			opts->zone_capacity =
1194eb0570c7SDamien Le Moal 				((sector_t)token * SZ_1M) >> SECTOR_SHIFT;
1195eb0570c7SDamien Le Moal 			break;
1196eb0570c7SDamien Le Moal 		case ZLOOP_OPT_NR_CONV_ZONES:
1197eb0570c7SDamien Le Moal 			if (match_uint(args, &token)) {
1198eb0570c7SDamien Le Moal 				ret = -EINVAL;
1199eb0570c7SDamien Le Moal 				goto out;
1200eb0570c7SDamien Le Moal 			}
1201eb0570c7SDamien Le Moal 			opts->nr_conv_zones = token;
1202eb0570c7SDamien Le Moal 			break;
1203eb0570c7SDamien Le Moal 		case ZLOOP_OPT_BASE_DIR:
1204eb0570c7SDamien Le Moal 			p = match_strdup(args);
1205eb0570c7SDamien Le Moal 			if (!p) {
1206eb0570c7SDamien Le Moal 				ret = -ENOMEM;
1207eb0570c7SDamien Le Moal 				goto out;
1208eb0570c7SDamien Le Moal 			}
1209eb0570c7SDamien Le Moal 			kfree(opts->base_dir);
1210eb0570c7SDamien Le Moal 			opts->base_dir = p;
1211eb0570c7SDamien Le Moal 			break;
1212eb0570c7SDamien Le Moal 		case ZLOOP_OPT_NR_QUEUES:
1213eb0570c7SDamien Le Moal 			if (match_uint(args, &token)) {
1214eb0570c7SDamien Le Moal 				ret = -EINVAL;
1215eb0570c7SDamien Le Moal 				goto out;
1216eb0570c7SDamien Le Moal 			}
1217eb0570c7SDamien Le Moal 			if (!token) {
1218eb0570c7SDamien Le Moal 				pr_err("Invalid number of queues\n");
1219eb0570c7SDamien Le Moal 				ret = -EINVAL;
1220eb0570c7SDamien Le Moal 				goto out;
1221eb0570c7SDamien Le Moal 			}
1222eb0570c7SDamien Le Moal 			opts->nr_queues = min(token, num_online_cpus());
1223eb0570c7SDamien Le Moal 			break;
1224eb0570c7SDamien Le Moal 		case ZLOOP_OPT_QUEUE_DEPTH:
1225eb0570c7SDamien Le Moal 			if (match_uint(args, &token)) {
1226eb0570c7SDamien Le Moal 				ret = -EINVAL;
1227eb0570c7SDamien Le Moal 				goto out;
1228eb0570c7SDamien Le Moal 			}
1229eb0570c7SDamien Le Moal 			if (!token) {
1230eb0570c7SDamien Le Moal 				pr_err("Invalid queue depth\n");
1231eb0570c7SDamien Le Moal 				ret = -EINVAL;
1232eb0570c7SDamien Le Moal 				goto out;
1233eb0570c7SDamien Le Moal 			}
1234eb0570c7SDamien Le Moal 			opts->queue_depth = token;
1235eb0570c7SDamien Le Moal 			break;
1236eb0570c7SDamien Le Moal 		case ZLOOP_OPT_BUFFERED_IO:
1237eb0570c7SDamien Le Moal 			opts->buffered_io = true;
1238eb0570c7SDamien Le Moal 			break;
1239*9236c5fdSDamien Le Moal 		case ZLOOP_OPT_ZONE_APPEND:
1240*9236c5fdSDamien Le Moal 			if (match_uint(args, &token)) {
1241*9236c5fdSDamien Le Moal 				ret = -EINVAL;
1242*9236c5fdSDamien Le Moal 				goto out;
1243*9236c5fdSDamien Le Moal 			}
1244*9236c5fdSDamien Le Moal 			if (token != 0 && token != 1) {
1245*9236c5fdSDamien Le Moal 				pr_err("Invalid zone_append value\n");
1246*9236c5fdSDamien Le Moal 				ret = -EINVAL;
1247*9236c5fdSDamien Le Moal 				goto out;
1248*9236c5fdSDamien Le Moal 			}
1249*9236c5fdSDamien Le Moal 			opts->zone_append = token;
1250*9236c5fdSDamien Le Moal 			break;
1251eb0570c7SDamien Le Moal 		case ZLOOP_OPT_ERR:
1252eb0570c7SDamien Le Moal 		default:
1253eb0570c7SDamien Le Moal 			pr_warn("unknown parameter or missing value '%s'\n", p);
1254eb0570c7SDamien Le Moal 			ret = -EINVAL;
1255eb0570c7SDamien Le Moal 			goto out;
1256eb0570c7SDamien Le Moal 		}
1257eb0570c7SDamien Le Moal 	}
1258eb0570c7SDamien Le Moal 
1259eb0570c7SDamien Le Moal 	ret = -EINVAL;
1260eb0570c7SDamien Le Moal 	if (opts->capacity <= opts->zone_size) {
1261eb0570c7SDamien Le Moal 		pr_err("Invalid capacity\n");
1262eb0570c7SDamien Le Moal 		goto out;
1263eb0570c7SDamien Le Moal 	}
1264eb0570c7SDamien Le Moal 
1265eb0570c7SDamien Le Moal 	if (opts->zone_capacity > opts->zone_size) {
1266eb0570c7SDamien Le Moal 		pr_err("Invalid zone capacity\n");
1267eb0570c7SDamien Le Moal 		goto out;
1268eb0570c7SDamien Le Moal 	}
1269eb0570c7SDamien Le Moal 
1270eb0570c7SDamien Le Moal 	ret = 0;
1271eb0570c7SDamien Le Moal out:
1272eb0570c7SDamien Le Moal 	kfree(options);
1273eb0570c7SDamien Le Moal 	return ret;
1274eb0570c7SDamien Le Moal }
1275eb0570c7SDamien Le Moal 
1276eb0570c7SDamien Le Moal enum {
1277eb0570c7SDamien Le Moal 	ZLOOP_CTL_ADD,
1278eb0570c7SDamien Le Moal 	ZLOOP_CTL_REMOVE,
1279eb0570c7SDamien Le Moal };
1280eb0570c7SDamien Le Moal 
1281eb0570c7SDamien Le Moal static struct zloop_ctl_op {
1282eb0570c7SDamien Le Moal 	int		code;
1283eb0570c7SDamien Le Moal 	const char	*name;
1284eb0570c7SDamien Le Moal } zloop_ctl_ops[] = {
1285eb0570c7SDamien Le Moal 	{ ZLOOP_CTL_ADD,	"add" },
1286eb0570c7SDamien Le Moal 	{ ZLOOP_CTL_REMOVE,	"remove" },
1287eb0570c7SDamien Le Moal 	{ -1,	NULL },
1288eb0570c7SDamien Le Moal };
1289eb0570c7SDamien Le Moal 
1290eb0570c7SDamien Le Moal static ssize_t zloop_ctl_write(struct file *file, const char __user *ubuf,
1291eb0570c7SDamien Le Moal 			       size_t count, loff_t *pos)
1292eb0570c7SDamien Le Moal {
1293eb0570c7SDamien Le Moal 	struct zloop_options opts = { };
1294eb0570c7SDamien Le Moal 	struct zloop_ctl_op *op;
1295eb0570c7SDamien Le Moal 	const char *buf, *opts_buf;
1296eb0570c7SDamien Le Moal 	int i, ret;
1297eb0570c7SDamien Le Moal 
1298eb0570c7SDamien Le Moal 	if (count > PAGE_SIZE)
1299eb0570c7SDamien Le Moal 		return -ENOMEM;
1300eb0570c7SDamien Le Moal 
1301eb0570c7SDamien Le Moal 	buf = memdup_user_nul(ubuf, count);
1302eb0570c7SDamien Le Moal 	if (IS_ERR(buf))
1303eb0570c7SDamien Le Moal 		return PTR_ERR(buf);
1304eb0570c7SDamien Le Moal 
1305eb0570c7SDamien Le Moal 	for (i = 0; i < ARRAY_SIZE(zloop_ctl_ops); i++) {
1306eb0570c7SDamien Le Moal 		op = &zloop_ctl_ops[i];
1307eb0570c7SDamien Le Moal 		if (!op->name) {
1308eb0570c7SDamien Le Moal 			pr_err("Invalid operation\n");
1309eb0570c7SDamien Le Moal 			ret = -EINVAL;
1310eb0570c7SDamien Le Moal 			goto out;
1311eb0570c7SDamien Le Moal 		}
1312eb0570c7SDamien Le Moal 		if (!strncmp(buf, op->name, strlen(op->name)))
1313eb0570c7SDamien Le Moal 			break;
1314eb0570c7SDamien Le Moal 	}
1315eb0570c7SDamien Le Moal 
1316eb0570c7SDamien Le Moal 	if (count <= strlen(op->name))
1317eb0570c7SDamien Le Moal 		opts_buf = NULL;
1318eb0570c7SDamien Le Moal 	else
1319eb0570c7SDamien Le Moal 		opts_buf = buf + strlen(op->name);
1320eb0570c7SDamien Le Moal 
1321eb0570c7SDamien Le Moal 	ret = zloop_parse_options(&opts, opts_buf);
1322eb0570c7SDamien Le Moal 	if (ret) {
1323eb0570c7SDamien Le Moal 		pr_err("Failed to parse options\n");
1324eb0570c7SDamien Le Moal 		goto out;
1325eb0570c7SDamien Le Moal 	}
1326eb0570c7SDamien Le Moal 
1327eb0570c7SDamien Le Moal 	switch (op->code) {
1328eb0570c7SDamien Le Moal 	case ZLOOP_CTL_ADD:
1329eb0570c7SDamien Le Moal 		ret = zloop_ctl_add(&opts);
1330eb0570c7SDamien Le Moal 		break;
1331eb0570c7SDamien Le Moal 	case ZLOOP_CTL_REMOVE:
1332eb0570c7SDamien Le Moal 		ret = zloop_ctl_remove(&opts);
1333eb0570c7SDamien Le Moal 		break;
1334eb0570c7SDamien Le Moal 	default:
1335eb0570c7SDamien Le Moal 		pr_err("Invalid operation\n");
1336eb0570c7SDamien Le Moal 		ret = -EINVAL;
1337eb0570c7SDamien Le Moal 		goto out;
1338eb0570c7SDamien Le Moal 	}
1339eb0570c7SDamien Le Moal 
1340eb0570c7SDamien Le Moal out:
1341eb0570c7SDamien Le Moal 	kfree(opts.base_dir);
1342eb0570c7SDamien Le Moal 	kfree(buf);
1343eb0570c7SDamien Le Moal 	return ret ? ret : count;
1344eb0570c7SDamien Le Moal }
1345eb0570c7SDamien Le Moal 
1346eb0570c7SDamien Le Moal static int zloop_ctl_show(struct seq_file *seq_file, void *private)
1347eb0570c7SDamien Le Moal {
1348eb0570c7SDamien Le Moal 	const struct match_token *tok;
1349eb0570c7SDamien Le Moal 	int i;
1350eb0570c7SDamien Le Moal 
1351eb0570c7SDamien Le Moal 	/* Add operation */
1352eb0570c7SDamien Le Moal 	seq_printf(seq_file, "%s ", zloop_ctl_ops[0].name);
1353eb0570c7SDamien Le Moal 	for (i = 0; i < ARRAY_SIZE(zloop_opt_tokens); i++) {
1354eb0570c7SDamien Le Moal 		tok = &zloop_opt_tokens[i];
1355eb0570c7SDamien Le Moal 		if (!tok->pattern)
1356eb0570c7SDamien Le Moal 			break;
1357eb0570c7SDamien Le Moal 		if (i)
1358eb0570c7SDamien Le Moal 			seq_putc(seq_file, ',');
1359eb0570c7SDamien Le Moal 		seq_puts(seq_file, tok->pattern);
1360eb0570c7SDamien Le Moal 	}
1361eb0570c7SDamien Le Moal 	seq_putc(seq_file, '\n');
1362eb0570c7SDamien Le Moal 
1363eb0570c7SDamien Le Moal 	/* Remove operation */
1364eb0570c7SDamien Le Moal 	seq_puts(seq_file, zloop_ctl_ops[1].name);
1365eb0570c7SDamien Le Moal 	seq_puts(seq_file, " id=%d\n");
1366eb0570c7SDamien Le Moal 
1367eb0570c7SDamien Le Moal 	return 0;
1368eb0570c7SDamien Le Moal }
1369eb0570c7SDamien Le Moal 
1370eb0570c7SDamien Le Moal static int zloop_ctl_open(struct inode *inode, struct file *file)
1371eb0570c7SDamien Le Moal {
1372eb0570c7SDamien Le Moal 	file->private_data = NULL;
1373eb0570c7SDamien Le Moal 	return single_open(file, zloop_ctl_show, NULL);
1374eb0570c7SDamien Le Moal }
1375eb0570c7SDamien Le Moal 
1376eb0570c7SDamien Le Moal static int zloop_ctl_release(struct inode *inode, struct file *file)
1377eb0570c7SDamien Le Moal {
1378eb0570c7SDamien Le Moal 	return single_release(inode, file);
1379eb0570c7SDamien Le Moal }
1380eb0570c7SDamien Le Moal 
1381eb0570c7SDamien Le Moal static const struct file_operations zloop_ctl_fops = {
1382eb0570c7SDamien Le Moal 	.owner		= THIS_MODULE,
1383eb0570c7SDamien Le Moal 	.open		= zloop_ctl_open,
1384eb0570c7SDamien Le Moal 	.release	= zloop_ctl_release,
1385eb0570c7SDamien Le Moal 	.write		= zloop_ctl_write,
1386eb0570c7SDamien Le Moal 	.read		= seq_read,
1387eb0570c7SDamien Le Moal };
1388eb0570c7SDamien Le Moal 
1389eb0570c7SDamien Le Moal static struct miscdevice zloop_misc = {
1390eb0570c7SDamien Le Moal 	.minor		= MISC_DYNAMIC_MINOR,
1391eb0570c7SDamien Le Moal 	.name		= "zloop-control",
1392eb0570c7SDamien Le Moal 	.fops		= &zloop_ctl_fops,
1393eb0570c7SDamien Le Moal };
1394eb0570c7SDamien Le Moal 
1395eb0570c7SDamien Le Moal static int __init zloop_init(void)
1396eb0570c7SDamien Le Moal {
1397eb0570c7SDamien Le Moal 	int ret;
1398eb0570c7SDamien Le Moal 
1399eb0570c7SDamien Le Moal 	ret = misc_register(&zloop_misc);
1400eb0570c7SDamien Le Moal 	if (ret) {
1401eb0570c7SDamien Le Moal 		pr_err("Failed to register misc device: %d\n", ret);
1402eb0570c7SDamien Le Moal 		return ret;
1403eb0570c7SDamien Le Moal 	}
1404eb0570c7SDamien Le Moal 	pr_info("Module loaded\n");
1405eb0570c7SDamien Le Moal 
1406eb0570c7SDamien Le Moal 	return 0;
1407eb0570c7SDamien Le Moal }
1408eb0570c7SDamien Le Moal 
1409eb0570c7SDamien Le Moal static void __exit zloop_exit(void)
1410eb0570c7SDamien Le Moal {
1411eb0570c7SDamien Le Moal 	misc_deregister(&zloop_misc);
1412eb0570c7SDamien Le Moal 	idr_destroy(&zloop_index_idr);
1413eb0570c7SDamien Le Moal }
1414eb0570c7SDamien Le Moal 
1415eb0570c7SDamien Le Moal module_init(zloop_init);
1416eb0570c7SDamien Le Moal module_exit(zloop_exit);
1417eb0570c7SDamien Le Moal 
1418eb0570c7SDamien Le Moal MODULE_DESCRIPTION("Zoned loopback device");
1419eb0570c7SDamien Le Moal MODULE_LICENSE("GPL");
1420