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