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>
20829def1eSChristoph Hellwig #include <linux/xattr.h>
21eb0570c7SDamien Le Moal
22eb0570c7SDamien Le Moal /*
23eb0570c7SDamien Le Moal * Options for adding (and removing) a device.
24eb0570c7SDamien Le Moal */
25eb0570c7SDamien Le Moal enum {
26eb0570c7SDamien Le Moal ZLOOP_OPT_ERR = 0,
27eb0570c7SDamien Le Moal ZLOOP_OPT_ID = (1 << 0),
28eb0570c7SDamien Le Moal ZLOOP_OPT_CAPACITY = (1 << 1),
29eb0570c7SDamien Le Moal ZLOOP_OPT_ZONE_SIZE = (1 << 2),
30eb0570c7SDamien Le Moal ZLOOP_OPT_ZONE_CAPACITY = (1 << 3),
31eb0570c7SDamien Le Moal ZLOOP_OPT_NR_CONV_ZONES = (1 << 4),
32eb0570c7SDamien Le Moal ZLOOP_OPT_BASE_DIR = (1 << 5),
33eb0570c7SDamien Le Moal ZLOOP_OPT_NR_QUEUES = (1 << 6),
34eb0570c7SDamien Le Moal ZLOOP_OPT_QUEUE_DEPTH = (1 << 7),
35eb0570c7SDamien Le Moal ZLOOP_OPT_BUFFERED_IO = (1 << 8),
369236c5fdSDamien Le Moal ZLOOP_OPT_ZONE_APPEND = (1 << 9),
37fcc6eaa3SDamien Le Moal ZLOOP_OPT_ORDERED_ZONE_APPEND = (1 << 10),
38829def1eSChristoph Hellwig ZLOOP_OPT_DISCARD_WRITE_CACHE = (1 << 11),
39b2a78fecSDamien Le Moal ZLOOP_OPT_MAX_OPEN_ZONES = (1 << 12),
40eb0570c7SDamien Le Moal };
41eb0570c7SDamien Le Moal
42eb0570c7SDamien Le Moal static const match_table_t zloop_opt_tokens = {
43eb0570c7SDamien Le Moal { ZLOOP_OPT_ID, "id=%d" },
44eb0570c7SDamien Le Moal { ZLOOP_OPT_CAPACITY, "capacity_mb=%u" },
45eb0570c7SDamien Le Moal { ZLOOP_OPT_ZONE_SIZE, "zone_size_mb=%u" },
46eb0570c7SDamien Le Moal { ZLOOP_OPT_ZONE_CAPACITY, "zone_capacity_mb=%u" },
47eb0570c7SDamien Le Moal { ZLOOP_OPT_NR_CONV_ZONES, "conv_zones=%u" },
48eb0570c7SDamien Le Moal { ZLOOP_OPT_BASE_DIR, "base_dir=%s" },
49eb0570c7SDamien Le Moal { ZLOOP_OPT_NR_QUEUES, "nr_queues=%u" },
50eb0570c7SDamien Le Moal { ZLOOP_OPT_QUEUE_DEPTH, "queue_depth=%u" },
51eb0570c7SDamien Le Moal { ZLOOP_OPT_BUFFERED_IO, "buffered_io" },
529236c5fdSDamien Le Moal { ZLOOP_OPT_ZONE_APPEND, "zone_append=%u" },
53fcc6eaa3SDamien Le Moal { ZLOOP_OPT_ORDERED_ZONE_APPEND, "ordered_zone_append" },
54829def1eSChristoph Hellwig { ZLOOP_OPT_DISCARD_WRITE_CACHE, "discard_write_cache" },
55b2a78fecSDamien Le Moal { ZLOOP_OPT_MAX_OPEN_ZONES, "max_open_zones=%u" },
56eb0570c7SDamien Le Moal { ZLOOP_OPT_ERR, NULL }
57eb0570c7SDamien Le Moal };
58eb0570c7SDamien Le Moal
59eb0570c7SDamien Le Moal /* Default values for the "add" operation. */
60eb0570c7SDamien Le Moal #define ZLOOP_DEF_ID -1
61eb0570c7SDamien Le Moal #define ZLOOP_DEF_ZONE_SIZE ((256ULL * SZ_1M) >> SECTOR_SHIFT)
62eb0570c7SDamien Le Moal #define ZLOOP_DEF_NR_ZONES 64
63eb0570c7SDamien Le Moal #define ZLOOP_DEF_NR_CONV_ZONES 8
64b2a78fecSDamien Le Moal #define ZLOOP_DEF_MAX_OPEN_ZONES 0
65eb0570c7SDamien Le Moal #define ZLOOP_DEF_BASE_DIR "/var/local/zloop"
66eb0570c7SDamien Le Moal #define ZLOOP_DEF_NR_QUEUES 1
67eb0570c7SDamien Le Moal #define ZLOOP_DEF_QUEUE_DEPTH 128
68eb0570c7SDamien Le Moal #define ZLOOP_DEF_BUFFERED_IO false
699236c5fdSDamien Le Moal #define ZLOOP_DEF_ZONE_APPEND true
70fcc6eaa3SDamien Le Moal #define ZLOOP_DEF_ORDERED_ZONE_APPEND false
71eb0570c7SDamien Le Moal
72eb0570c7SDamien Le Moal /* Arbitrary limit on the zone size (16GB). */
73eb0570c7SDamien Le Moal #define ZLOOP_MAX_ZONE_SIZE_MB 16384
74eb0570c7SDamien Le Moal
75eb0570c7SDamien Le Moal struct zloop_options {
76eb0570c7SDamien Le Moal unsigned int mask;
77eb0570c7SDamien Le Moal int id;
78eb0570c7SDamien Le Moal sector_t capacity;
79eb0570c7SDamien Le Moal sector_t zone_size;
80eb0570c7SDamien Le Moal sector_t zone_capacity;
81eb0570c7SDamien Le Moal unsigned int nr_conv_zones;
82b2a78fecSDamien Le Moal unsigned int max_open_zones;
83eb0570c7SDamien Le Moal char *base_dir;
84eb0570c7SDamien Le Moal unsigned int nr_queues;
85eb0570c7SDamien Le Moal unsigned int queue_depth;
86eb0570c7SDamien Le Moal bool buffered_io;
879236c5fdSDamien Le Moal bool zone_append;
88fcc6eaa3SDamien Le Moal bool ordered_zone_append;
89829def1eSChristoph Hellwig bool discard_write_cache;
90eb0570c7SDamien Le Moal };
91eb0570c7SDamien Le Moal
92eb0570c7SDamien Le Moal /*
93eb0570c7SDamien Le Moal * Device states.
94eb0570c7SDamien Le Moal */
95eb0570c7SDamien Le Moal enum {
96eb0570c7SDamien Le Moal Zlo_creating = 0,
97eb0570c7SDamien Le Moal Zlo_live,
98eb0570c7SDamien Le Moal Zlo_deleting,
99eb0570c7SDamien Le Moal };
100eb0570c7SDamien Le Moal
101eb0570c7SDamien Le Moal enum zloop_zone_flags {
102eb0570c7SDamien Le Moal ZLOOP_ZONE_CONV = 0,
103eb0570c7SDamien Le Moal ZLOOP_ZONE_SEQ_ERROR,
104eb0570c7SDamien Le Moal };
105eb0570c7SDamien Le Moal
106b2a78fecSDamien Le Moal /*
107b2a78fecSDamien Le Moal * Zone descriptor.
108b2a78fecSDamien Le Moal * Locking order: z.lock -> z.wp_lock -> zlo.open_zones_lock
109b2a78fecSDamien Le Moal */
110eb0570c7SDamien Le Moal struct zloop_zone {
111b2a78fecSDamien Le Moal struct list_head open_zone_entry;
112eb0570c7SDamien Le Moal struct file *file;
113eb0570c7SDamien Le Moal
114eb0570c7SDamien Le Moal unsigned long flags;
115eb0570c7SDamien Le Moal struct mutex lock;
116fcc6eaa3SDamien Le Moal spinlock_t wp_lock;
117eb0570c7SDamien Le Moal enum blk_zone_cond cond;
118eb0570c7SDamien Le Moal sector_t start;
119eb0570c7SDamien Le Moal sector_t wp;
120eb0570c7SDamien Le Moal
121eb0570c7SDamien Le Moal gfp_t old_gfp_mask;
122eb0570c7SDamien Le Moal };
123eb0570c7SDamien Le Moal
124eb0570c7SDamien Le Moal struct zloop_device {
125eb0570c7SDamien Le Moal unsigned int id;
126eb0570c7SDamien Le Moal unsigned int state;
127eb0570c7SDamien Le Moal
128eb0570c7SDamien Le Moal struct blk_mq_tag_set tag_set;
129eb0570c7SDamien Le Moal struct gendisk *disk;
130eb0570c7SDamien Le Moal
131eb0570c7SDamien Le Moal struct workqueue_struct *workqueue;
132eb0570c7SDamien Le Moal bool buffered_io;
1339236c5fdSDamien Le Moal bool zone_append;
134fcc6eaa3SDamien Le Moal bool ordered_zone_append;
135829def1eSChristoph Hellwig bool discard_write_cache;
136eb0570c7SDamien Le Moal
137eb0570c7SDamien Le Moal const char *base_dir;
138eb0570c7SDamien Le Moal struct file *data_dir;
139eb0570c7SDamien Le Moal
140eb0570c7SDamien Le Moal unsigned int zone_shift;
141eb0570c7SDamien Le Moal sector_t zone_size;
142eb0570c7SDamien Le Moal sector_t zone_capacity;
143eb0570c7SDamien Le Moal unsigned int nr_zones;
144eb0570c7SDamien Le Moal unsigned int nr_conv_zones;
145b2a78fecSDamien Le Moal unsigned int max_open_zones;
146eb0570c7SDamien Le Moal unsigned int block_size;
147eb0570c7SDamien Le Moal
148b2a78fecSDamien Le Moal spinlock_t open_zones_lock;
149b2a78fecSDamien Le Moal struct list_head open_zones_lru_list;
150b2a78fecSDamien Le Moal unsigned int nr_open_zones;
151b2a78fecSDamien Le Moal
152eb0570c7SDamien Le Moal struct zloop_zone zones[] __counted_by(nr_zones);
153eb0570c7SDamien Le Moal };
154eb0570c7SDamien Le Moal
155eb0570c7SDamien Le Moal struct zloop_cmd {
156eb0570c7SDamien Le Moal struct work_struct work;
157eb0570c7SDamien Le Moal atomic_t ref;
158eb0570c7SDamien Le Moal sector_t sector;
159eb0570c7SDamien Le Moal sector_t nr_sectors;
160eb0570c7SDamien Le Moal long ret;
161eb0570c7SDamien Le Moal struct kiocb iocb;
162eb0570c7SDamien Le Moal struct bio_vec *bvec;
163eb0570c7SDamien Le Moal };
164eb0570c7SDamien Le Moal
165eb0570c7SDamien Le Moal static DEFINE_IDR(zloop_index_idr);
166eb0570c7SDamien Le Moal static DEFINE_MUTEX(zloop_ctl_mutex);
167eb0570c7SDamien Le Moal
rq_zone_no(struct request * rq)168eb0570c7SDamien Le Moal static unsigned int rq_zone_no(struct request *rq)
169eb0570c7SDamien Le Moal {
170eb0570c7SDamien Le Moal struct zloop_device *zlo = rq->q->queuedata;
171eb0570c7SDamien Le Moal
172eb0570c7SDamien Le Moal return blk_rq_pos(rq) >> zlo->zone_shift;
173eb0570c7SDamien Le Moal }
174eb0570c7SDamien Le Moal
175b2a78fecSDamien Le Moal /*
176b2a78fecSDamien Le Moal * Open an already open zone. This is mostly a no-op, except for the imp open ->
177b2a78fecSDamien Le Moal * exp open condition change that may happen. We also move a zone at the tail of
178b2a78fecSDamien Le Moal * the list of open zones so that if we need to
179b2a78fecSDamien Le Moal * implicitly close one open zone, we can do so in LRU order.
180b2a78fecSDamien Le Moal */
zloop_lru_rotate_open_zone(struct zloop_device * zlo,struct zloop_zone * zone)181b2a78fecSDamien Le Moal static inline void zloop_lru_rotate_open_zone(struct zloop_device *zlo,
182b2a78fecSDamien Le Moal struct zloop_zone *zone)
183b2a78fecSDamien Le Moal {
184b2a78fecSDamien Le Moal if (zlo->max_open_zones) {
185b2a78fecSDamien Le Moal spin_lock(&zlo->open_zones_lock);
186b2a78fecSDamien Le Moal list_move_tail(&zone->open_zone_entry,
187b2a78fecSDamien Le Moal &zlo->open_zones_lru_list);
188b2a78fecSDamien Le Moal spin_unlock(&zlo->open_zones_lock);
189b2a78fecSDamien Le Moal }
190b2a78fecSDamien Le Moal }
191b2a78fecSDamien Le Moal
zloop_lru_remove_open_zone(struct zloop_device * zlo,struct zloop_zone * zone)192b2a78fecSDamien Le Moal static inline void zloop_lru_remove_open_zone(struct zloop_device *zlo,
193b2a78fecSDamien Le Moal struct zloop_zone *zone)
194b2a78fecSDamien Le Moal {
195b2a78fecSDamien Le Moal if (zone->cond == BLK_ZONE_COND_IMP_OPEN ||
196b2a78fecSDamien Le Moal zone->cond == BLK_ZONE_COND_EXP_OPEN) {
197b2a78fecSDamien Le Moal spin_lock(&zlo->open_zones_lock);
198b2a78fecSDamien Le Moal list_del_init(&zone->open_zone_entry);
199b2a78fecSDamien Le Moal zlo->nr_open_zones--;
200b2a78fecSDamien Le Moal spin_unlock(&zlo->open_zones_lock);
201b2a78fecSDamien Le Moal }
202b2a78fecSDamien Le Moal }
203b2a78fecSDamien Le Moal
zloop_can_open_zone(struct zloop_device * zlo)204b2a78fecSDamien Le Moal static inline bool zloop_can_open_zone(struct zloop_device *zlo)
205b2a78fecSDamien Le Moal {
206b2a78fecSDamien Le Moal return !zlo->max_open_zones || zlo->nr_open_zones < zlo->max_open_zones;
207b2a78fecSDamien Le Moal }
208b2a78fecSDamien Le Moal
209b2a78fecSDamien Le Moal /*
210b2a78fecSDamien Le Moal * If we have reached the maximum open zones limit, attempt to close an
211b2a78fecSDamien Le Moal * implicitly open zone (if we have any) so that we can implicitly open another
212b2a78fecSDamien Le Moal * zone without exceeding the maximum number of open zones.
213b2a78fecSDamien Le Moal */
zloop_close_imp_open_zone(struct zloop_device * zlo)214b2a78fecSDamien Le Moal static bool zloop_close_imp_open_zone(struct zloop_device *zlo)
215b2a78fecSDamien Le Moal {
216b2a78fecSDamien Le Moal struct zloop_zone *zone;
217b2a78fecSDamien Le Moal
218b2a78fecSDamien Le Moal lockdep_assert_held(&zlo->open_zones_lock);
219b2a78fecSDamien Le Moal
220b2a78fecSDamien Le Moal if (zloop_can_open_zone(zlo))
221b2a78fecSDamien Le Moal return true;
222b2a78fecSDamien Le Moal
223b2a78fecSDamien Le Moal list_for_each_entry(zone, &zlo->open_zones_lru_list, open_zone_entry) {
224b2a78fecSDamien Le Moal if (zone->cond == BLK_ZONE_COND_IMP_OPEN) {
225b2a78fecSDamien Le Moal zone->cond = BLK_ZONE_COND_CLOSED;
226b2a78fecSDamien Le Moal list_del_init(&zone->open_zone_entry);
227b2a78fecSDamien Le Moal zlo->nr_open_zones--;
228b2a78fecSDamien Le Moal return true;
229b2a78fecSDamien Le Moal }
230b2a78fecSDamien Le Moal }
231b2a78fecSDamien Le Moal
232b2a78fecSDamien Le Moal return false;
233b2a78fecSDamien Le Moal }
234b2a78fecSDamien Le Moal
zloop_open_closed_or_empty_zone(struct zloop_device * zlo,struct zloop_zone * zone,bool explicit)235b2a78fecSDamien Le Moal static bool zloop_open_closed_or_empty_zone(struct zloop_device *zlo,
236b2a78fecSDamien Le Moal struct zloop_zone *zone,
237b2a78fecSDamien Le Moal bool explicit)
238b2a78fecSDamien Le Moal {
239b2a78fecSDamien Le Moal spin_lock(&zlo->open_zones_lock);
240b2a78fecSDamien Le Moal
241b2a78fecSDamien Le Moal if (explicit) {
242b2a78fecSDamien Le Moal /*
243b2a78fecSDamien Le Moal * Explicit open: we cannot allow this if we have reached the
244b2a78fecSDamien Le Moal * maximum open zones limit.
245b2a78fecSDamien Le Moal */
246b2a78fecSDamien Le Moal if (!zloop_can_open_zone(zlo))
247b2a78fecSDamien Le Moal goto fail;
248b2a78fecSDamien Le Moal zone->cond = BLK_ZONE_COND_EXP_OPEN;
249b2a78fecSDamien Le Moal } else {
250b2a78fecSDamien Le Moal /*
251b2a78fecSDamien Le Moal * Implicit open case: if we have reached the maximum open zones
252b2a78fecSDamien Le Moal * limit, try to close an implicitly open zone first.
253b2a78fecSDamien Le Moal */
254b2a78fecSDamien Le Moal if (!zloop_close_imp_open_zone(zlo))
255b2a78fecSDamien Le Moal goto fail;
256b2a78fecSDamien Le Moal zone->cond = BLK_ZONE_COND_IMP_OPEN;
257b2a78fecSDamien Le Moal }
258b2a78fecSDamien Le Moal
259b2a78fecSDamien Le Moal zlo->nr_open_zones++;
260b2a78fecSDamien Le Moal list_add_tail(&zone->open_zone_entry,
261b2a78fecSDamien Le Moal &zlo->open_zones_lru_list);
262b2a78fecSDamien Le Moal
263b2a78fecSDamien Le Moal spin_unlock(&zlo->open_zones_lock);
264b2a78fecSDamien Le Moal
265b2a78fecSDamien Le Moal return true;
266b2a78fecSDamien Le Moal
267b2a78fecSDamien Le Moal fail:
268b2a78fecSDamien Le Moal spin_unlock(&zlo->open_zones_lock);
269b2a78fecSDamien Le Moal
270b2a78fecSDamien Le Moal return false;
271b2a78fecSDamien Le Moal }
272b2a78fecSDamien Le Moal
zloop_do_open_zone(struct zloop_device * zlo,struct zloop_zone * zone,bool explicit)273b2a78fecSDamien Le Moal static bool zloop_do_open_zone(struct zloop_device *zlo,
274b2a78fecSDamien Le Moal struct zloop_zone *zone, bool explicit)
275b2a78fecSDamien Le Moal {
276b2a78fecSDamien Le Moal switch (zone->cond) {
277b2a78fecSDamien Le Moal case BLK_ZONE_COND_IMP_OPEN:
278b2a78fecSDamien Le Moal case BLK_ZONE_COND_EXP_OPEN:
279b2a78fecSDamien Le Moal if (explicit)
280b2a78fecSDamien Le Moal zone->cond = BLK_ZONE_COND_EXP_OPEN;
281b2a78fecSDamien Le Moal zloop_lru_rotate_open_zone(zlo, zone);
282b2a78fecSDamien Le Moal return true;
283b2a78fecSDamien Le Moal case BLK_ZONE_COND_EMPTY:
284b2a78fecSDamien Le Moal case BLK_ZONE_COND_CLOSED:
285b2a78fecSDamien Le Moal return zloop_open_closed_or_empty_zone(zlo, zone, explicit);
286b2a78fecSDamien Le Moal default:
287b2a78fecSDamien Le Moal return false;
288b2a78fecSDamien Le Moal }
289b2a78fecSDamien Le Moal }
290b2a78fecSDamien Le Moal
zloop_mark_full(struct zloop_device * zlo,struct zloop_zone * zone)291ec5c045fSChristoph Hellwig static void zloop_mark_full(struct zloop_device *zlo, struct zloop_zone *zone)
292ec5c045fSChristoph Hellwig {
293ec5c045fSChristoph Hellwig lockdep_assert_held(&zone->wp_lock);
294ec5c045fSChristoph Hellwig
295ec5c045fSChristoph Hellwig zloop_lru_remove_open_zone(zlo, zone);
296ec5c045fSChristoph Hellwig zone->cond = BLK_ZONE_COND_FULL;
297ec5c045fSChristoph Hellwig zone->wp = ULLONG_MAX;
298ec5c045fSChristoph Hellwig }
299ec5c045fSChristoph Hellwig
zloop_mark_empty(struct zloop_device * zlo,struct zloop_zone * zone)300ec5c045fSChristoph Hellwig static void zloop_mark_empty(struct zloop_device *zlo, struct zloop_zone *zone)
301ec5c045fSChristoph Hellwig {
302ec5c045fSChristoph Hellwig lockdep_assert_held(&zone->wp_lock);
303ec5c045fSChristoph Hellwig
304ec5c045fSChristoph Hellwig zloop_lru_remove_open_zone(zlo, zone);
305ec5c045fSChristoph Hellwig zone->cond = BLK_ZONE_COND_EMPTY;
306ec5c045fSChristoph Hellwig zone->wp = zone->start;
307ec5c045fSChristoph Hellwig }
308ec5c045fSChristoph Hellwig
zloop_update_seq_zone(struct zloop_device * zlo,unsigned int zone_no)309eb0570c7SDamien Le Moal static int zloop_update_seq_zone(struct zloop_device *zlo, unsigned int zone_no)
310eb0570c7SDamien Le Moal {
311eb0570c7SDamien Le Moal struct zloop_zone *zone = &zlo->zones[zone_no];
312eb0570c7SDamien Le Moal struct kstat stat;
313eb0570c7SDamien Le Moal sector_t file_sectors;
314eb0570c7SDamien Le Moal int ret;
315eb0570c7SDamien Le Moal
316eb0570c7SDamien Le Moal lockdep_assert_held(&zone->lock);
317eb0570c7SDamien Le Moal
318eb0570c7SDamien Le Moal ret = vfs_getattr(&zone->file->f_path, &stat, STATX_SIZE, 0);
319eb0570c7SDamien Le Moal if (ret < 0) {
320eb0570c7SDamien Le Moal pr_err("Failed to get zone %u file stat (err=%d)\n",
321eb0570c7SDamien Le Moal zone_no, ret);
322eb0570c7SDamien Le Moal set_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags);
323eb0570c7SDamien Le Moal return ret;
324eb0570c7SDamien Le Moal }
325eb0570c7SDamien Le Moal
326eb0570c7SDamien Le Moal file_sectors = stat.size >> SECTOR_SHIFT;
327eb0570c7SDamien Le Moal if (file_sectors > zlo->zone_capacity) {
328eb0570c7SDamien Le Moal pr_err("Zone %u file too large (%llu sectors > %llu)\n",
329eb0570c7SDamien Le Moal zone_no, file_sectors, zlo->zone_capacity);
330eb0570c7SDamien Le Moal return -EINVAL;
331eb0570c7SDamien Le Moal }
332eb0570c7SDamien Le Moal
3336466b211SChristoph Hellwig if (!IS_ALIGNED(stat.size, zlo->block_size)) {
3346466b211SChristoph Hellwig pr_err("Zone %u file size (%llu) not aligned to block size %u\n",
3356466b211SChristoph Hellwig zone_no, stat.size, zlo->block_size);
336eb0570c7SDamien Le Moal return -EINVAL;
337eb0570c7SDamien Le Moal }
338eb0570c7SDamien Le Moal
339*64b437c4SChristoph Hellwig spin_lock(&zone->wp_lock);
340eb0570c7SDamien Le Moal if (!file_sectors) {
341ec5c045fSChristoph Hellwig zloop_mark_empty(zlo, zone);
342eb0570c7SDamien Le Moal } else if (file_sectors == zlo->zone_capacity) {
343ec5c045fSChristoph Hellwig zloop_mark_full(zlo, zone);
344eb0570c7SDamien Le Moal } else {
345b2a78fecSDamien Le Moal if (zone->cond != BLK_ZONE_COND_IMP_OPEN &&
346b2a78fecSDamien Le Moal zone->cond != BLK_ZONE_COND_EXP_OPEN)
347eb0570c7SDamien Le Moal zone->cond = BLK_ZONE_COND_CLOSED;
348eb0570c7SDamien Le Moal zone->wp = zone->start + file_sectors;
349eb0570c7SDamien Le Moal }
350*64b437c4SChristoph Hellwig spin_unlock(&zone->wp_lock);
351eb0570c7SDamien Le Moal
352eb0570c7SDamien Le Moal return 0;
353eb0570c7SDamien Le Moal }
354eb0570c7SDamien Le Moal
zloop_open_zone(struct zloop_device * zlo,unsigned int zone_no)355eb0570c7SDamien Le Moal static int zloop_open_zone(struct zloop_device *zlo, unsigned int zone_no)
356eb0570c7SDamien Le Moal {
357eb0570c7SDamien Le Moal struct zloop_zone *zone = &zlo->zones[zone_no];
358eb0570c7SDamien Le Moal int ret = 0;
359eb0570c7SDamien Le Moal
360eb0570c7SDamien Le Moal if (test_bit(ZLOOP_ZONE_CONV, &zone->flags))
361eb0570c7SDamien Le Moal return -EIO;
362eb0570c7SDamien Le Moal
363eb0570c7SDamien Le Moal mutex_lock(&zone->lock);
364eb0570c7SDamien Le Moal
365eb0570c7SDamien Le Moal if (test_and_clear_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags)) {
366eb0570c7SDamien Le Moal ret = zloop_update_seq_zone(zlo, zone_no);
367eb0570c7SDamien Le Moal if (ret)
368eb0570c7SDamien Le Moal goto unlock;
369eb0570c7SDamien Le Moal }
370eb0570c7SDamien Le Moal
371b2a78fecSDamien Le Moal if (!zloop_do_open_zone(zlo, zone, true))
372eb0570c7SDamien Le Moal ret = -EIO;
373eb0570c7SDamien Le Moal
374eb0570c7SDamien Le Moal unlock:
375eb0570c7SDamien Le Moal mutex_unlock(&zone->lock);
376eb0570c7SDamien Le Moal
377eb0570c7SDamien Le Moal return ret;
378eb0570c7SDamien Le Moal }
379eb0570c7SDamien Le Moal
zloop_close_zone(struct zloop_device * zlo,unsigned int zone_no)380eb0570c7SDamien Le Moal static int zloop_close_zone(struct zloop_device *zlo, unsigned int zone_no)
381eb0570c7SDamien Le Moal {
382eb0570c7SDamien Le Moal struct zloop_zone *zone = &zlo->zones[zone_no];
383eb0570c7SDamien Le Moal int ret = 0;
384eb0570c7SDamien Le Moal
385eb0570c7SDamien Le Moal if (test_bit(ZLOOP_ZONE_CONV, &zone->flags))
386eb0570c7SDamien Le Moal return -EIO;
387eb0570c7SDamien Le Moal
388eb0570c7SDamien Le Moal mutex_lock(&zone->lock);
389eb0570c7SDamien Le Moal
390eb0570c7SDamien Le Moal if (test_and_clear_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags)) {
391eb0570c7SDamien Le Moal ret = zloop_update_seq_zone(zlo, zone_no);
392eb0570c7SDamien Le Moal if (ret)
393eb0570c7SDamien Le Moal goto unlock;
394eb0570c7SDamien Le Moal }
395eb0570c7SDamien Le Moal
396eb0570c7SDamien Le Moal switch (zone->cond) {
397eb0570c7SDamien Le Moal case BLK_ZONE_COND_CLOSED:
398eb0570c7SDamien Le Moal break;
399eb0570c7SDamien Le Moal case BLK_ZONE_COND_IMP_OPEN:
400eb0570c7SDamien Le Moal case BLK_ZONE_COND_EXP_OPEN:
401*64b437c4SChristoph Hellwig spin_lock(&zone->wp_lock);
402b2a78fecSDamien Le Moal zloop_lru_remove_open_zone(zlo, zone);
403eb0570c7SDamien Le Moal if (zone->wp == zone->start)
404eb0570c7SDamien Le Moal zone->cond = BLK_ZONE_COND_EMPTY;
405eb0570c7SDamien Le Moal else
406eb0570c7SDamien Le Moal zone->cond = BLK_ZONE_COND_CLOSED;
407*64b437c4SChristoph Hellwig spin_unlock(&zone->wp_lock);
408eb0570c7SDamien Le Moal break;
409eb0570c7SDamien Le Moal case BLK_ZONE_COND_EMPTY:
410eb0570c7SDamien Le Moal case BLK_ZONE_COND_FULL:
411eb0570c7SDamien Le Moal default:
412eb0570c7SDamien Le Moal ret = -EIO;
413eb0570c7SDamien Le Moal break;
414eb0570c7SDamien Le Moal }
415eb0570c7SDamien Le Moal
416eb0570c7SDamien Le Moal unlock:
417eb0570c7SDamien Le Moal mutex_unlock(&zone->lock);
418eb0570c7SDamien Le Moal
419eb0570c7SDamien Le Moal return ret;
420eb0570c7SDamien Le Moal }
421eb0570c7SDamien Le Moal
zloop_reset_zone(struct zloop_device * zlo,unsigned int zone_no)422eb0570c7SDamien Le Moal static int zloop_reset_zone(struct zloop_device *zlo, unsigned int zone_no)
423eb0570c7SDamien Le Moal {
424eb0570c7SDamien Le Moal struct zloop_zone *zone = &zlo->zones[zone_no];
425eb0570c7SDamien Le Moal int ret = 0;
426eb0570c7SDamien Le Moal
427eb0570c7SDamien Le Moal if (test_bit(ZLOOP_ZONE_CONV, &zone->flags))
428eb0570c7SDamien Le Moal return -EIO;
429eb0570c7SDamien Le Moal
430eb0570c7SDamien Le Moal mutex_lock(&zone->lock);
431eb0570c7SDamien Le Moal
432eb0570c7SDamien Le Moal if (!test_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags) &&
433eb0570c7SDamien Le Moal zone->cond == BLK_ZONE_COND_EMPTY)
434eb0570c7SDamien Le Moal goto unlock;
435eb0570c7SDamien Le Moal
436eb0570c7SDamien Le Moal if (vfs_truncate(&zone->file->f_path, 0)) {
437eb0570c7SDamien Le Moal set_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags);
438eb0570c7SDamien Le Moal ret = -EIO;
439eb0570c7SDamien Le Moal goto unlock;
440eb0570c7SDamien Le Moal }
441eb0570c7SDamien Le Moal
442*64b437c4SChristoph Hellwig spin_lock(&zone->wp_lock);
443ec5c045fSChristoph Hellwig zloop_mark_empty(zlo, zone);
444eb0570c7SDamien Le Moal clear_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags);
445*64b437c4SChristoph Hellwig spin_unlock(&zone->wp_lock);
446eb0570c7SDamien Le Moal
447eb0570c7SDamien Le Moal unlock:
448eb0570c7SDamien Le Moal mutex_unlock(&zone->lock);
449eb0570c7SDamien Le Moal
450eb0570c7SDamien Le Moal return ret;
451eb0570c7SDamien Le Moal }
452eb0570c7SDamien Le Moal
zloop_reset_all_zones(struct zloop_device * zlo)453eb0570c7SDamien Le Moal static int zloop_reset_all_zones(struct zloop_device *zlo)
454eb0570c7SDamien Le Moal {
455eb0570c7SDamien Le Moal unsigned int i;
456eb0570c7SDamien Le Moal int ret;
457eb0570c7SDamien Le Moal
458eb0570c7SDamien Le Moal for (i = zlo->nr_conv_zones; i < zlo->nr_zones; i++) {
459eb0570c7SDamien Le Moal ret = zloop_reset_zone(zlo, i);
460eb0570c7SDamien Le Moal if (ret)
461eb0570c7SDamien Le Moal return ret;
462eb0570c7SDamien Le Moal }
463eb0570c7SDamien Le Moal
464eb0570c7SDamien Le Moal return 0;
465eb0570c7SDamien Le Moal }
466eb0570c7SDamien Le Moal
zloop_finish_zone(struct zloop_device * zlo,unsigned int zone_no)467eb0570c7SDamien Le Moal static int zloop_finish_zone(struct zloop_device *zlo, unsigned int zone_no)
468eb0570c7SDamien Le Moal {
469eb0570c7SDamien Le Moal struct zloop_zone *zone = &zlo->zones[zone_no];
470eb0570c7SDamien Le Moal int ret = 0;
471eb0570c7SDamien Le Moal
472eb0570c7SDamien Le Moal if (test_bit(ZLOOP_ZONE_CONV, &zone->flags))
473eb0570c7SDamien Le Moal return -EIO;
474eb0570c7SDamien Le Moal
475eb0570c7SDamien Le Moal mutex_lock(&zone->lock);
476eb0570c7SDamien Le Moal
477eb0570c7SDamien Le Moal if (!test_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags) &&
478eb0570c7SDamien Le Moal zone->cond == BLK_ZONE_COND_FULL)
479eb0570c7SDamien Le Moal goto unlock;
480eb0570c7SDamien Le Moal
481eb0570c7SDamien Le Moal if (vfs_truncate(&zone->file->f_path, zlo->zone_size << SECTOR_SHIFT)) {
482eb0570c7SDamien Le Moal set_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags);
483eb0570c7SDamien Le Moal ret = -EIO;
484eb0570c7SDamien Le Moal goto unlock;
485eb0570c7SDamien Le Moal }
486eb0570c7SDamien Le Moal
487*64b437c4SChristoph Hellwig spin_lock(&zone->wp_lock);
488ec5c045fSChristoph Hellwig zloop_mark_full(zlo, zone);
489eb0570c7SDamien Le Moal clear_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags);
490*64b437c4SChristoph Hellwig spin_unlock(&zone->wp_lock);
491eb0570c7SDamien Le Moal
492eb0570c7SDamien Le Moal unlock:
493eb0570c7SDamien Le Moal mutex_unlock(&zone->lock);
494eb0570c7SDamien Le Moal
495eb0570c7SDamien Le Moal return ret;
496eb0570c7SDamien Le Moal }
497eb0570c7SDamien Le Moal
zloop_put_cmd(struct zloop_cmd * cmd)498eb0570c7SDamien Le Moal static void zloop_put_cmd(struct zloop_cmd *cmd)
499eb0570c7SDamien Le Moal {
500eb0570c7SDamien Le Moal struct request *rq = blk_mq_rq_from_pdu(cmd);
501eb0570c7SDamien Le Moal
502eb0570c7SDamien Le Moal if (!atomic_dec_and_test(&cmd->ref))
503eb0570c7SDamien Le Moal return;
504eb0570c7SDamien Le Moal kfree(cmd->bvec);
505eb0570c7SDamien Le Moal cmd->bvec = NULL;
506eb0570c7SDamien Le Moal if (likely(!blk_should_fake_timeout(rq->q)))
507eb0570c7SDamien Le Moal blk_mq_complete_request(rq);
508eb0570c7SDamien Le Moal }
509eb0570c7SDamien Le Moal
zloop_rw_complete(struct kiocb * iocb,long ret)510eb0570c7SDamien Le Moal static void zloop_rw_complete(struct kiocb *iocb, long ret)
511eb0570c7SDamien Le Moal {
512eb0570c7SDamien Le Moal struct zloop_cmd *cmd = container_of(iocb, struct zloop_cmd, iocb);
513eb0570c7SDamien Le Moal
514eb0570c7SDamien Le Moal cmd->ret = ret;
515eb0570c7SDamien Le Moal zloop_put_cmd(cmd);
516eb0570c7SDamien Le Moal }
517eb0570c7SDamien Le Moal
zloop_do_rw(struct zloop_cmd * cmd)518eff8d165SChristoph Hellwig static int zloop_do_rw(struct zloop_cmd *cmd)
519eb0570c7SDamien Le Moal {
520eb0570c7SDamien Le Moal struct request *rq = blk_mq_rq_from_pdu(cmd);
521eff8d165SChristoph Hellwig int rw = req_op(rq) == REQ_OP_READ ? ITER_DEST : ITER_SOURCE;
522eff8d165SChristoph Hellwig unsigned int nr_bvec = blk_rq_nr_bvec(rq);
523eb0570c7SDamien Le Moal struct zloop_device *zlo = rq->q->queuedata;
524eff8d165SChristoph Hellwig struct zloop_zone *zone = &zlo->zones[rq_zone_no(rq)];
525eb0570c7SDamien Le Moal struct req_iterator rq_iter;
526eb0570c7SDamien Le Moal struct iov_iter iter;
527eb0570c7SDamien Le Moal
528eb0570c7SDamien Le Moal if (rq->bio != rq->biotail) {
529eff8d165SChristoph Hellwig struct bio_vec tmp, *bvec;
530eb0570c7SDamien Le Moal
53169050f8dSKees Cook cmd->bvec = kmalloc_objs(*cmd->bvec, nr_bvec, GFP_NOIO);
532eff8d165SChristoph Hellwig if (!cmd->bvec)
533eff8d165SChristoph Hellwig return -EIO;
534eb0570c7SDamien Le Moal
535eb0570c7SDamien Le Moal /*
536eb0570c7SDamien Le Moal * The bios of the request may be started from the middle of
537eb0570c7SDamien Le Moal * the 'bvec' because of bio splitting, so we can't directly
538eb0570c7SDamien Le Moal * copy bio->bi_iov_vec to new bvec. The rq_for_each_bvec
539eb0570c7SDamien Le Moal * API will take care of all details for us.
540eb0570c7SDamien Le Moal */
541eb0570c7SDamien Le Moal bvec = cmd->bvec;
542eb0570c7SDamien Le Moal rq_for_each_bvec(tmp, rq, rq_iter) {
543eb0570c7SDamien Le Moal *bvec = tmp;
544eb0570c7SDamien Le Moal bvec++;
545eb0570c7SDamien Le Moal }
546eb0570c7SDamien Le Moal iov_iter_bvec(&iter, rw, cmd->bvec, nr_bvec, blk_rq_bytes(rq));
547eb0570c7SDamien Le Moal } else {
548eb0570c7SDamien Le Moal /*
549eb0570c7SDamien Le Moal * Same here, this bio may be started from the middle of the
550eb0570c7SDamien Le Moal * 'bvec' because of bio splitting, so offset from the bvec
551eb0570c7SDamien Le Moal * must be passed to iov iterator
552eb0570c7SDamien Le Moal */
553eb0570c7SDamien Le Moal iov_iter_bvec(&iter, rw,
554eb0570c7SDamien Le Moal __bvec_iter_bvec(rq->bio->bi_io_vec, rq->bio->bi_iter),
555eb0570c7SDamien Le Moal nr_bvec, blk_rq_bytes(rq));
556eb0570c7SDamien Le Moal iter.iov_offset = rq->bio->bi_iter.bi_bvec_done;
557eb0570c7SDamien Le Moal }
558eb0570c7SDamien Le Moal
559eff8d165SChristoph Hellwig cmd->iocb.ki_pos = (cmd->sector - zone->start) << SECTOR_SHIFT;
560eb0570c7SDamien Le Moal cmd->iocb.ki_filp = zone->file;
561eb0570c7SDamien Le Moal cmd->iocb.ki_complete = zloop_rw_complete;
562eb0570c7SDamien Le Moal if (!zlo->buffered_io)
563eb0570c7SDamien Le Moal cmd->iocb.ki_flags = IOCB_DIRECT;
564eb0570c7SDamien Le Moal cmd->iocb.ki_ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_NONE, 0);
565eb0570c7SDamien Le Moal
566eb0570c7SDamien Le Moal if (rw == ITER_SOURCE)
567eff8d165SChristoph Hellwig return zone->file->f_op->write_iter(&cmd->iocb, &iter);
568eff8d165SChristoph Hellwig return zone->file->f_op->read_iter(&cmd->iocb, &iter);
569eff8d165SChristoph Hellwig }
570eff8d165SChristoph Hellwig
zloop_seq_write_prep(struct zloop_cmd * cmd)571eff8d165SChristoph Hellwig static int zloop_seq_write_prep(struct zloop_cmd *cmd)
572eff8d165SChristoph Hellwig {
573eff8d165SChristoph Hellwig struct request *rq = blk_mq_rq_from_pdu(cmd);
574eff8d165SChristoph Hellwig struct zloop_device *zlo = rq->q->queuedata;
575eff8d165SChristoph Hellwig unsigned int zone_no = rq_zone_no(rq);
576eff8d165SChristoph Hellwig sector_t nr_sectors = blk_rq_sectors(rq);
577eff8d165SChristoph Hellwig bool is_append = req_op(rq) == REQ_OP_ZONE_APPEND;
578eff8d165SChristoph Hellwig struct zloop_zone *zone = &zlo->zones[zone_no];
579eff8d165SChristoph Hellwig sector_t zone_end = zone->start + zlo->zone_capacity;
580eff8d165SChristoph Hellwig int ret = 0;
581eff8d165SChristoph Hellwig
582*64b437c4SChristoph Hellwig spin_lock(&zone->wp_lock);
583eff8d165SChristoph Hellwig
584eff8d165SChristoph Hellwig /*
585eff8d165SChristoph Hellwig * Zone append operations always go at the current write pointer, but
586eff8d165SChristoph Hellwig * regular write operations must already be aligned to the write pointer
587eff8d165SChristoph Hellwig * when submitted.
588eff8d165SChristoph Hellwig */
589eff8d165SChristoph Hellwig if (is_append) {
590eff8d165SChristoph Hellwig /*
591eff8d165SChristoph Hellwig * If ordered zone append is in use, we already checked and set
592eff8d165SChristoph Hellwig * the target sector in zloop_queue_rq().
593eff8d165SChristoph Hellwig */
594eff8d165SChristoph Hellwig if (!zlo->ordered_zone_append) {
595eff8d165SChristoph Hellwig if (zone->cond == BLK_ZONE_COND_FULL ||
596eff8d165SChristoph Hellwig zone->wp + nr_sectors > zone_end) {
597eff8d165SChristoph Hellwig ret = -EIO;
598eff8d165SChristoph Hellwig goto out_unlock;
599eff8d165SChristoph Hellwig }
600eff8d165SChristoph Hellwig cmd->sector = zone->wp;
601eff8d165SChristoph Hellwig }
602eff8d165SChristoph Hellwig } else {
603eff8d165SChristoph Hellwig if (cmd->sector != zone->wp) {
604eff8d165SChristoph Hellwig pr_err("Zone %u: unaligned write: sect %llu, wp %llu\n",
605eff8d165SChristoph Hellwig zone_no, cmd->sector, zone->wp);
606eff8d165SChristoph Hellwig ret = -EIO;
607eff8d165SChristoph Hellwig goto out_unlock;
608eff8d165SChristoph Hellwig }
609eff8d165SChristoph Hellwig }
610eff8d165SChristoph Hellwig
611eff8d165SChristoph Hellwig /* Implicitly open the target zone. */
612b2a78fecSDamien Le Moal if (!zloop_do_open_zone(zlo, zone, false)) {
613b2a78fecSDamien Le Moal ret = -EIO;
614b2a78fecSDamien Le Moal goto out_unlock;
615b2a78fecSDamien Le Moal }
616eff8d165SChristoph Hellwig
617eff8d165SChristoph Hellwig /*
618eff8d165SChristoph Hellwig * Advance the write pointer, unless ordered zone append is in use. If
619eff8d165SChristoph Hellwig * the write fails, the write pointer position will be corrected when
620eff8d165SChristoph Hellwig * the next I/O starts execution.
621eff8d165SChristoph Hellwig */
622eff8d165SChristoph Hellwig if (!is_append || !zlo->ordered_zone_append) {
623eff8d165SChristoph Hellwig zone->wp += nr_sectors;
624ec5c045fSChristoph Hellwig if (zone->wp == zone_end)
625ec5c045fSChristoph Hellwig zloop_mark_full(zlo, zone);
626eff8d165SChristoph Hellwig }
627eff8d165SChristoph Hellwig out_unlock:
628*64b437c4SChristoph Hellwig spin_unlock(&zone->wp_lock);
629eff8d165SChristoph Hellwig return ret;
630eff8d165SChristoph Hellwig }
631eff8d165SChristoph Hellwig
zloop_rw(struct zloop_cmd * cmd)632eff8d165SChristoph Hellwig static void zloop_rw(struct zloop_cmd *cmd)
633eff8d165SChristoph Hellwig {
634eff8d165SChristoph Hellwig struct request *rq = blk_mq_rq_from_pdu(cmd);
635eff8d165SChristoph Hellwig struct zloop_device *zlo = rq->q->queuedata;
636eff8d165SChristoph Hellwig unsigned int zone_no = rq_zone_no(rq);
637eff8d165SChristoph Hellwig sector_t nr_sectors = blk_rq_sectors(rq);
638eff8d165SChristoph Hellwig bool is_append = req_op(rq) == REQ_OP_ZONE_APPEND;
639eff8d165SChristoph Hellwig bool is_write = req_op(rq) == REQ_OP_WRITE || is_append;
640eff8d165SChristoph Hellwig struct zloop_zone *zone;
641eff8d165SChristoph Hellwig int ret = -EIO;
642eff8d165SChristoph Hellwig
643eff8d165SChristoph Hellwig atomic_set(&cmd->ref, 2);
644eff8d165SChristoph Hellwig cmd->sector = blk_rq_pos(rq);
645eff8d165SChristoph Hellwig cmd->nr_sectors = nr_sectors;
646eff8d165SChristoph Hellwig cmd->ret = 0;
647eff8d165SChristoph Hellwig
648eff8d165SChristoph Hellwig if (WARN_ON_ONCE(is_append && !zlo->zone_append))
649eff8d165SChristoph Hellwig goto out;
650eff8d165SChristoph Hellwig
651eff8d165SChristoph Hellwig /* We should never get an I/O beyond the device capacity. */
652eff8d165SChristoph Hellwig if (WARN_ON_ONCE(zone_no >= zlo->nr_zones))
653eff8d165SChristoph Hellwig goto out;
654eff8d165SChristoph Hellwig
655eff8d165SChristoph Hellwig zone = &zlo->zones[zone_no];
656eff8d165SChristoph Hellwig
657eff8d165SChristoph Hellwig /*
658eff8d165SChristoph Hellwig * The block layer should never send requests that are not fully
659eff8d165SChristoph Hellwig * contained within the zone.
660eff8d165SChristoph Hellwig */
661eff8d165SChristoph Hellwig if (WARN_ON_ONCE(cmd->sector + nr_sectors >
662eff8d165SChristoph Hellwig zone->start + zlo->zone_size))
663eff8d165SChristoph Hellwig goto out;
664eff8d165SChristoph Hellwig
665eff8d165SChristoph Hellwig if (test_and_clear_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags)) {
666eff8d165SChristoph Hellwig mutex_lock(&zone->lock);
667eff8d165SChristoph Hellwig ret = zloop_update_seq_zone(zlo, zone_no);
668eb0570c7SDamien Le Moal mutex_unlock(&zone->lock);
669eff8d165SChristoph Hellwig if (ret)
670eff8d165SChristoph Hellwig goto out;
671eff8d165SChristoph Hellwig }
672eff8d165SChristoph Hellwig
673eff8d165SChristoph Hellwig if (!test_bit(ZLOOP_ZONE_CONV, &zone->flags) && is_write) {
674eff8d165SChristoph Hellwig mutex_lock(&zone->lock);
675eff8d165SChristoph Hellwig ret = zloop_seq_write_prep(cmd);
676eff8d165SChristoph Hellwig if (!ret)
677eff8d165SChristoph Hellwig ret = zloop_do_rw(cmd);
678eff8d165SChristoph Hellwig mutex_unlock(&zone->lock);
679eff8d165SChristoph Hellwig } else {
680eff8d165SChristoph Hellwig ret = zloop_do_rw(cmd);
681eff8d165SChristoph Hellwig }
682eb0570c7SDamien Le Moal out:
683eb0570c7SDamien Le Moal if (ret != -EIOCBQUEUED)
684eb0570c7SDamien Le Moal zloop_rw_complete(&cmd->iocb, ret);
685eb0570c7SDamien Le Moal zloop_put_cmd(cmd);
686eb0570c7SDamien Le Moal }
687eb0570c7SDamien Le Moal
zloop_zone_is_active(struct zloop_zone * zone)688829def1eSChristoph Hellwig static inline bool zloop_zone_is_active(struct zloop_zone *zone)
689829def1eSChristoph Hellwig {
690829def1eSChristoph Hellwig switch (zone->cond) {
691829def1eSChristoph Hellwig case BLK_ZONE_COND_EXP_OPEN:
692829def1eSChristoph Hellwig case BLK_ZONE_COND_IMP_OPEN:
693829def1eSChristoph Hellwig case BLK_ZONE_COND_CLOSED:
694829def1eSChristoph Hellwig return true;
695829def1eSChristoph Hellwig default:
696829def1eSChristoph Hellwig return false;
697829def1eSChristoph Hellwig }
698829def1eSChristoph Hellwig }
699829def1eSChristoph Hellwig
zloop_record_safe_wps(struct zloop_device * zlo)700829def1eSChristoph Hellwig static int zloop_record_safe_wps(struct zloop_device *zlo)
701829def1eSChristoph Hellwig {
702829def1eSChristoph Hellwig unsigned int i;
703829def1eSChristoph Hellwig int ret;
704829def1eSChristoph Hellwig
705829def1eSChristoph Hellwig for (i = 0; i < zlo->nr_zones; i++) {
706829def1eSChristoph Hellwig struct zloop_zone *zone = &zlo->zones[i];
707829def1eSChristoph Hellwig struct file *file = zone->file;
708829def1eSChristoph Hellwig
709829def1eSChristoph Hellwig if (!zloop_zone_is_active(zone))
710829def1eSChristoph Hellwig continue;
711829def1eSChristoph Hellwig ret = vfs_setxattr(file_mnt_idmap(file), file_dentry(file),
712829def1eSChristoph Hellwig "user.zloop.wp", &zone->wp, sizeof(zone->wp), 0);
713829def1eSChristoph Hellwig if (ret) {
714829def1eSChristoph Hellwig pr_err("%pg: failed to record write pointer (%d)\n",
715829def1eSChristoph Hellwig zlo->disk->part0, ret);
716829def1eSChristoph Hellwig return ret;
717829def1eSChristoph Hellwig }
718829def1eSChristoph Hellwig }
719829def1eSChristoph Hellwig
720829def1eSChristoph Hellwig return 0;
721829def1eSChristoph Hellwig }
722829def1eSChristoph Hellwig
7236acf7860SChristoph Hellwig /*
7246acf7860SChristoph Hellwig * Sync the entire FS containing the zone files instead of walking all files.
7256acf7860SChristoph Hellwig */
zloop_flush(struct zloop_device * zlo)7266acf7860SChristoph Hellwig static int zloop_flush(struct zloop_device *zlo)
7276acf7860SChristoph Hellwig {
7286acf7860SChristoph Hellwig struct super_block *sb = file_inode(zlo->data_dir)->i_sb;
7296acf7860SChristoph Hellwig int ret;
7306acf7860SChristoph Hellwig
731829def1eSChristoph Hellwig if (zlo->discard_write_cache) {
732829def1eSChristoph Hellwig ret = zloop_record_safe_wps(zlo);
733829def1eSChristoph Hellwig if (ret)
734829def1eSChristoph Hellwig return ret;
735829def1eSChristoph Hellwig }
736829def1eSChristoph Hellwig
7376acf7860SChristoph Hellwig down_read(&sb->s_umount);
7386acf7860SChristoph Hellwig ret = sync_filesystem(sb);
7396acf7860SChristoph Hellwig up_read(&sb->s_umount);
7406acf7860SChristoph Hellwig
7416acf7860SChristoph Hellwig return ret;
7426acf7860SChristoph Hellwig }
7436acf7860SChristoph Hellwig
zloop_handle_cmd(struct zloop_cmd * cmd)744eb0570c7SDamien Le Moal static void zloop_handle_cmd(struct zloop_cmd *cmd)
745eb0570c7SDamien Le Moal {
746eb0570c7SDamien Le Moal struct request *rq = blk_mq_rq_from_pdu(cmd);
747eb0570c7SDamien Le Moal struct zloop_device *zlo = rq->q->queuedata;
748eb0570c7SDamien Le Moal
749e8f0abddSChaitanya Kulkarni /* We can block in this context, so ignore REQ_NOWAIT. */
750e8f0abddSChaitanya Kulkarni if (rq->cmd_flags & REQ_NOWAIT)
751e8f0abddSChaitanya Kulkarni rq->cmd_flags &= ~REQ_NOWAIT;
752e8f0abddSChaitanya Kulkarni
753eb0570c7SDamien Le Moal switch (req_op(rq)) {
754eb0570c7SDamien Le Moal case REQ_OP_READ:
755eb0570c7SDamien Le Moal case REQ_OP_WRITE:
756eb0570c7SDamien Le Moal case REQ_OP_ZONE_APPEND:
757eb0570c7SDamien Le Moal /*
758eb0570c7SDamien Le Moal * zloop_rw() always executes asynchronously or completes
759eb0570c7SDamien Le Moal * directly.
760eb0570c7SDamien Le Moal */
761eb0570c7SDamien Le Moal zloop_rw(cmd);
762eb0570c7SDamien Le Moal return;
763eb0570c7SDamien Le Moal case REQ_OP_FLUSH:
7646acf7860SChristoph Hellwig cmd->ret = zloop_flush(zlo);
765eb0570c7SDamien Le Moal break;
766eb0570c7SDamien Le Moal case REQ_OP_ZONE_RESET:
767eb0570c7SDamien Le Moal cmd->ret = zloop_reset_zone(zlo, rq_zone_no(rq));
768eb0570c7SDamien Le Moal break;
769eb0570c7SDamien Le Moal case REQ_OP_ZONE_RESET_ALL:
770eb0570c7SDamien Le Moal cmd->ret = zloop_reset_all_zones(zlo);
771eb0570c7SDamien Le Moal break;
772eb0570c7SDamien Le Moal case REQ_OP_ZONE_FINISH:
773eb0570c7SDamien Le Moal cmd->ret = zloop_finish_zone(zlo, rq_zone_no(rq));
774eb0570c7SDamien Le Moal break;
775eb0570c7SDamien Le Moal case REQ_OP_ZONE_OPEN:
776eb0570c7SDamien Le Moal cmd->ret = zloop_open_zone(zlo, rq_zone_no(rq));
777eb0570c7SDamien Le Moal break;
778eb0570c7SDamien Le Moal case REQ_OP_ZONE_CLOSE:
779eb0570c7SDamien Le Moal cmd->ret = zloop_close_zone(zlo, rq_zone_no(rq));
780eb0570c7SDamien Le Moal break;
781eb0570c7SDamien Le Moal default:
782eb0570c7SDamien Le Moal WARN_ON_ONCE(1);
783eb0570c7SDamien Le Moal pr_err("Unsupported operation %d\n", req_op(rq));
784eb0570c7SDamien Le Moal cmd->ret = -EOPNOTSUPP;
785eb0570c7SDamien Le Moal break;
786eb0570c7SDamien Le Moal }
787eb0570c7SDamien Le Moal
788eb0570c7SDamien Le Moal blk_mq_complete_request(rq);
789eb0570c7SDamien Le Moal }
790eb0570c7SDamien Le Moal
zloop_cmd_workfn(struct work_struct * work)791eb0570c7SDamien Le Moal static void zloop_cmd_workfn(struct work_struct *work)
792eb0570c7SDamien Le Moal {
793eb0570c7SDamien Le Moal struct zloop_cmd *cmd = container_of(work, struct zloop_cmd, work);
794eb0570c7SDamien Le Moal int orig_flags = current->flags;
795eb0570c7SDamien Le Moal
796eb0570c7SDamien Le Moal current->flags |= PF_LOCAL_THROTTLE | PF_MEMALLOC_NOIO;
797eb0570c7SDamien Le Moal zloop_handle_cmd(cmd);
798eb0570c7SDamien Le Moal current->flags = orig_flags;
799eb0570c7SDamien Le Moal }
800eb0570c7SDamien Le Moal
zloop_complete_rq(struct request * rq)801eb0570c7SDamien Le Moal static void zloop_complete_rq(struct request *rq)
802eb0570c7SDamien Le Moal {
803eb0570c7SDamien Le Moal struct zloop_cmd *cmd = blk_mq_rq_to_pdu(rq);
804eb0570c7SDamien Le Moal struct zloop_device *zlo = rq->q->queuedata;
805eb0570c7SDamien Le Moal unsigned int zone_no = cmd->sector >> zlo->zone_shift;
806eb0570c7SDamien Le Moal struct zloop_zone *zone = &zlo->zones[zone_no];
807eb0570c7SDamien Le Moal blk_status_t sts = BLK_STS_OK;
808eb0570c7SDamien Le Moal
809eb0570c7SDamien Le Moal switch (req_op(rq)) {
810eb0570c7SDamien Le Moal case REQ_OP_READ:
811eb0570c7SDamien Le Moal if (cmd->ret < 0)
812eb0570c7SDamien Le Moal pr_err("Zone %u: failed read sector %llu, %llu sectors\n",
813eb0570c7SDamien Le Moal zone_no, cmd->sector, cmd->nr_sectors);
814eb0570c7SDamien Le Moal
815eb0570c7SDamien Le Moal if (cmd->ret >= 0 && cmd->ret != blk_rq_bytes(rq)) {
816eb0570c7SDamien Le Moal /* short read */
817eb0570c7SDamien Le Moal struct bio *bio;
818eb0570c7SDamien Le Moal
819eb0570c7SDamien Le Moal __rq_for_each_bio(bio, rq)
820eb0570c7SDamien Le Moal zero_fill_bio(bio);
821eb0570c7SDamien Le Moal }
822eb0570c7SDamien Le Moal break;
823eb0570c7SDamien Le Moal case REQ_OP_WRITE:
824eb0570c7SDamien Le Moal case REQ_OP_ZONE_APPEND:
825eb0570c7SDamien Le Moal if (cmd->ret < 0)
826eb0570c7SDamien Le Moal pr_err("Zone %u: failed %swrite sector %llu, %llu sectors\n",
827eb0570c7SDamien Le Moal zone_no,
828eb0570c7SDamien Le Moal req_op(rq) == REQ_OP_WRITE ? "" : "append ",
829eb0570c7SDamien Le Moal cmd->sector, cmd->nr_sectors);
830eb0570c7SDamien Le Moal
831eb0570c7SDamien Le Moal if (cmd->ret >= 0 && cmd->ret != blk_rq_bytes(rq)) {
832eb0570c7SDamien Le Moal pr_err("Zone %u: partial write %ld/%u B\n",
833eb0570c7SDamien Le Moal zone_no, cmd->ret, blk_rq_bytes(rq));
834eb0570c7SDamien Le Moal cmd->ret = -EIO;
835eb0570c7SDamien Le Moal }
836eb0570c7SDamien Le Moal
837eb0570c7SDamien Le Moal if (cmd->ret < 0 && !test_bit(ZLOOP_ZONE_CONV, &zone->flags)) {
838eb0570c7SDamien Le Moal /*
839eb0570c7SDamien Le Moal * A write to a sequential zone file failed: mark the
840eb0570c7SDamien Le Moal * zone as having an error. This will be corrected and
841eb0570c7SDamien Le Moal * cleared when the next IO is submitted.
842eb0570c7SDamien Le Moal */
843eb0570c7SDamien Le Moal set_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags);
844eb0570c7SDamien Le Moal break;
845eb0570c7SDamien Le Moal }
846eb0570c7SDamien Le Moal if (req_op(rq) == REQ_OP_ZONE_APPEND)
847eb0570c7SDamien Le Moal rq->__sector = cmd->sector;
848eb0570c7SDamien Le Moal
849eb0570c7SDamien Le Moal break;
850eb0570c7SDamien Le Moal default:
851eb0570c7SDamien Le Moal break;
852eb0570c7SDamien Le Moal }
853eb0570c7SDamien Le Moal
854eb0570c7SDamien Le Moal if (cmd->ret < 0)
855eb0570c7SDamien Le Moal sts = errno_to_blk_status(cmd->ret);
856eb0570c7SDamien Le Moal blk_mq_end_request(rq, sts);
857eb0570c7SDamien Le Moal }
858eb0570c7SDamien Le Moal
zloop_set_zone_append_sector(struct request * rq)859fcc6eaa3SDamien Le Moal static bool zloop_set_zone_append_sector(struct request *rq)
860fcc6eaa3SDamien Le Moal {
861fcc6eaa3SDamien Le Moal struct zloop_device *zlo = rq->q->queuedata;
862fcc6eaa3SDamien Le Moal unsigned int zone_no = rq_zone_no(rq);
863fcc6eaa3SDamien Le Moal struct zloop_zone *zone = &zlo->zones[zone_no];
864fcc6eaa3SDamien Le Moal sector_t zone_end = zone->start + zlo->zone_capacity;
865fcc6eaa3SDamien Le Moal sector_t nr_sectors = blk_rq_sectors(rq);
866fcc6eaa3SDamien Le Moal
867*64b437c4SChristoph Hellwig spin_lock(&zone->wp_lock);
868fcc6eaa3SDamien Le Moal
869fcc6eaa3SDamien Le Moal if (zone->cond == BLK_ZONE_COND_FULL ||
870fcc6eaa3SDamien Le Moal zone->wp + nr_sectors > zone_end) {
871*64b437c4SChristoph Hellwig spin_unlock(&zone->wp_lock);
872fcc6eaa3SDamien Le Moal return false;
873fcc6eaa3SDamien Le Moal }
874fcc6eaa3SDamien Le Moal
875fcc6eaa3SDamien Le Moal rq->__sector = zone->wp;
876fcc6eaa3SDamien Le Moal zone->wp += blk_rq_sectors(rq);
877ec5c045fSChristoph Hellwig if (zone->wp >= zone_end)
878ec5c045fSChristoph Hellwig zloop_mark_full(zlo, zone);
879fcc6eaa3SDamien Le Moal
880*64b437c4SChristoph Hellwig spin_unlock(&zone->wp_lock);
881fcc6eaa3SDamien Le Moal
882fcc6eaa3SDamien Le Moal return true;
883fcc6eaa3SDamien Le Moal }
884fcc6eaa3SDamien Le Moal
zloop_queue_rq(struct blk_mq_hw_ctx * hctx,const struct blk_mq_queue_data * bd)885eb0570c7SDamien Le Moal static blk_status_t zloop_queue_rq(struct blk_mq_hw_ctx *hctx,
886eb0570c7SDamien Le Moal const struct blk_mq_queue_data *bd)
887eb0570c7SDamien Le Moal {
888eb0570c7SDamien Le Moal struct request *rq = bd->rq;
889eb0570c7SDamien Le Moal struct zloop_cmd *cmd = blk_mq_rq_to_pdu(rq);
890eb0570c7SDamien Le Moal struct zloop_device *zlo = rq->q->queuedata;
891eb0570c7SDamien Le Moal
8925b680d7aSChristoph Hellwig if (data_race(READ_ONCE(zlo->state)) == Zlo_deleting) {
8935b680d7aSChristoph Hellwig rq->rq_flags |= RQF_QUIET;
894eb0570c7SDamien Le Moal return BLK_STS_IOERR;
8955b680d7aSChristoph Hellwig }
896eb0570c7SDamien Le Moal
897fcc6eaa3SDamien Le Moal /*
898fcc6eaa3SDamien Le Moal * If we need to strongly order zone append operations, set the request
899fcc6eaa3SDamien Le Moal * sector to the zone write pointer location now instead of when the
900fcc6eaa3SDamien Le Moal * command work runs.
901fcc6eaa3SDamien Le Moal */
902fcc6eaa3SDamien Le Moal if (zlo->ordered_zone_append && req_op(rq) == REQ_OP_ZONE_APPEND) {
903fcc6eaa3SDamien Le Moal if (!zloop_set_zone_append_sector(rq))
904fcc6eaa3SDamien Le Moal return BLK_STS_IOERR;
905fcc6eaa3SDamien Le Moal }
906fcc6eaa3SDamien Le Moal
907eb0570c7SDamien Le Moal blk_mq_start_request(rq);
908eb0570c7SDamien Le Moal
909eb0570c7SDamien Le Moal INIT_WORK(&cmd->work, zloop_cmd_workfn);
910eb0570c7SDamien Le Moal queue_work(zlo->workqueue, &cmd->work);
911eb0570c7SDamien Le Moal
912eb0570c7SDamien Le Moal return BLK_STS_OK;
913eb0570c7SDamien Le Moal }
914eb0570c7SDamien Le Moal
915eb0570c7SDamien Le Moal static const struct blk_mq_ops zloop_mq_ops = {
916eb0570c7SDamien Le Moal .queue_rq = zloop_queue_rq,
917eb0570c7SDamien Le Moal .complete = zloop_complete_rq,
918eb0570c7SDamien Le Moal };
919eb0570c7SDamien Le Moal
zloop_open(struct gendisk * disk,blk_mode_t mode)920eb0570c7SDamien Le Moal static int zloop_open(struct gendisk *disk, blk_mode_t mode)
921eb0570c7SDamien Le Moal {
922eb0570c7SDamien Le Moal struct zloop_device *zlo = disk->private_data;
923eb0570c7SDamien Le Moal int ret;
924eb0570c7SDamien Le Moal
925eb0570c7SDamien Le Moal ret = mutex_lock_killable(&zloop_ctl_mutex);
926eb0570c7SDamien Le Moal if (ret)
927eb0570c7SDamien Le Moal return ret;
928eb0570c7SDamien Le Moal
929eb0570c7SDamien Le Moal if (zlo->state != Zlo_live)
930eb0570c7SDamien Le Moal ret = -ENXIO;
931eb0570c7SDamien Le Moal mutex_unlock(&zloop_ctl_mutex);
932eb0570c7SDamien Le Moal return ret;
933eb0570c7SDamien Le Moal }
934eb0570c7SDamien Le Moal
zloop_report_zones(struct gendisk * disk,sector_t sector,unsigned int nr_zones,struct blk_report_zones_args * args)935eb0570c7SDamien Le Moal static int zloop_report_zones(struct gendisk *disk, sector_t sector,
936fdb9aed8SDamien Le Moal unsigned int nr_zones, struct blk_report_zones_args *args)
937eb0570c7SDamien Le Moal {
938eb0570c7SDamien Le Moal struct zloop_device *zlo = disk->private_data;
939eb0570c7SDamien Le Moal struct blk_zone blkz = {};
940eb0570c7SDamien Le Moal unsigned int first, i;
941eb0570c7SDamien Le Moal int ret;
942eb0570c7SDamien Le Moal
943eb0570c7SDamien Le Moal first = disk_zone_no(disk, sector);
944eb0570c7SDamien Le Moal if (first >= zlo->nr_zones)
945eb0570c7SDamien Le Moal return 0;
946eb0570c7SDamien Le Moal nr_zones = min(nr_zones, zlo->nr_zones - first);
947eb0570c7SDamien Le Moal
948eb0570c7SDamien Le Moal for (i = 0; i < nr_zones; i++) {
949eb0570c7SDamien Le Moal unsigned int zone_no = first + i;
950eb0570c7SDamien Le Moal struct zloop_zone *zone = &zlo->zones[zone_no];
951eb0570c7SDamien Le Moal
952eb0570c7SDamien Le Moal mutex_lock(&zone->lock);
953eb0570c7SDamien Le Moal
954eb0570c7SDamien Le Moal if (test_and_clear_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags)) {
955eb0570c7SDamien Le Moal ret = zloop_update_seq_zone(zlo, zone_no);
956eb0570c7SDamien Le Moal if (ret) {
957eb0570c7SDamien Le Moal mutex_unlock(&zone->lock);
958eb0570c7SDamien Le Moal return ret;
959eb0570c7SDamien Le Moal }
960eb0570c7SDamien Le Moal }
961eb0570c7SDamien Le Moal
962eb0570c7SDamien Le Moal blkz.start = zone->start;
963eb0570c7SDamien Le Moal blkz.len = zlo->zone_size;
964*64b437c4SChristoph Hellwig spin_lock(&zone->wp_lock);
965eb0570c7SDamien Le Moal blkz.wp = zone->wp;
966*64b437c4SChristoph Hellwig spin_unlock(&zone->wp_lock);
967eb0570c7SDamien Le Moal blkz.cond = zone->cond;
968eb0570c7SDamien Le Moal if (test_bit(ZLOOP_ZONE_CONV, &zone->flags)) {
969eb0570c7SDamien Le Moal blkz.type = BLK_ZONE_TYPE_CONVENTIONAL;
970eb0570c7SDamien Le Moal blkz.capacity = zlo->zone_size;
971eb0570c7SDamien Le Moal } else {
972eb0570c7SDamien Le Moal blkz.type = BLK_ZONE_TYPE_SEQWRITE_REQ;
973eb0570c7SDamien Le Moal blkz.capacity = zlo->zone_capacity;
974eb0570c7SDamien Le Moal }
975eb0570c7SDamien Le Moal
976eb0570c7SDamien Le Moal mutex_unlock(&zone->lock);
977eb0570c7SDamien Le Moal
978fdb9aed8SDamien Le Moal ret = disk_report_zone(disk, &blkz, i, args);
979eb0570c7SDamien Le Moal if (ret)
980eb0570c7SDamien Le Moal return ret;
981eb0570c7SDamien Le Moal }
982eb0570c7SDamien Le Moal
983eb0570c7SDamien Le Moal return nr_zones;
984eb0570c7SDamien Le Moal }
985eb0570c7SDamien Le Moal
zloop_free_disk(struct gendisk * disk)986eb0570c7SDamien Le Moal static void zloop_free_disk(struct gendisk *disk)
987eb0570c7SDamien Le Moal {
988eb0570c7SDamien Le Moal struct zloop_device *zlo = disk->private_data;
989eb0570c7SDamien Le Moal unsigned int i;
990eb0570c7SDamien Le Moal
99176576185SShin'ichiro Kawasaki blk_mq_free_tag_set(&zlo->tag_set);
99276576185SShin'ichiro Kawasaki
993eb0570c7SDamien Le Moal for (i = 0; i < zlo->nr_zones; i++) {
994eb0570c7SDamien Le Moal struct zloop_zone *zone = &zlo->zones[i];
995eb0570c7SDamien Le Moal
996eb0570c7SDamien Le Moal mapping_set_gfp_mask(zone->file->f_mapping,
997eb0570c7SDamien Le Moal zone->old_gfp_mask);
998eb0570c7SDamien Le Moal fput(zone->file);
999eb0570c7SDamien Le Moal }
1000eb0570c7SDamien Le Moal
1001eb0570c7SDamien Le Moal fput(zlo->data_dir);
1002eb0570c7SDamien Le Moal destroy_workqueue(zlo->workqueue);
1003eb0570c7SDamien Le Moal kfree(zlo->base_dir);
1004eb0570c7SDamien Le Moal kvfree(zlo);
1005eb0570c7SDamien Le Moal }
1006eb0570c7SDamien Le Moal
1007eb0570c7SDamien Le Moal static const struct block_device_operations zloop_fops = {
1008eb0570c7SDamien Le Moal .owner = THIS_MODULE,
1009eb0570c7SDamien Le Moal .open = zloop_open,
1010eb0570c7SDamien Le Moal .report_zones = zloop_report_zones,
1011eb0570c7SDamien Le Moal .free_disk = zloop_free_disk,
1012eb0570c7SDamien Le Moal };
1013eb0570c7SDamien Le Moal
1014eb0570c7SDamien Le Moal __printf(3, 4)
zloop_filp_open_fmt(int oflags,umode_t mode,const char * fmt,...)1015eb0570c7SDamien Le Moal static struct file *zloop_filp_open_fmt(int oflags, umode_t mode,
1016eb0570c7SDamien Le Moal const char *fmt, ...)
1017eb0570c7SDamien Le Moal {
1018eb0570c7SDamien Le Moal struct file *file;
1019eb0570c7SDamien Le Moal va_list ap;
1020eb0570c7SDamien Le Moal char *p;
1021eb0570c7SDamien Le Moal
1022eb0570c7SDamien Le Moal va_start(ap, fmt);
1023eb0570c7SDamien Le Moal p = kvasprintf(GFP_KERNEL, fmt, ap);
1024eb0570c7SDamien Le Moal va_end(ap);
1025eb0570c7SDamien Le Moal
1026eb0570c7SDamien Le Moal if (!p)
1027eb0570c7SDamien Le Moal return ERR_PTR(-ENOMEM);
1028eb0570c7SDamien Le Moal file = filp_open(p, oflags, mode);
1029eb0570c7SDamien Le Moal kfree(p);
1030eb0570c7SDamien Le Moal return file;
1031eb0570c7SDamien Le Moal }
1032eb0570c7SDamien Le Moal
zloop_get_block_size(struct zloop_device * zlo,struct zloop_zone * zone)1033eb0570c7SDamien Le Moal static int zloop_get_block_size(struct zloop_device *zlo,
1034eb0570c7SDamien Le Moal struct zloop_zone *zone)
1035eb0570c7SDamien Le Moal {
1036eb0570c7SDamien Le Moal struct block_device *sb_bdev = zone->file->f_mapping->host->i_sb->s_bdev;
1037eb0570c7SDamien Le Moal struct kstat st;
1038eb0570c7SDamien Le Moal
1039eb0570c7SDamien Le Moal /*
1040eb0570c7SDamien Le Moal * If the FS block size is lower than or equal to 4K, use that as the
1041eb0570c7SDamien Le Moal * device block size. Otherwise, fallback to the FS direct IO alignment
1042eb0570c7SDamien Le Moal * constraint if that is provided, and to the FS underlying device
1043eb0570c7SDamien Le Moal * physical block size if the direct IO alignment is unknown.
1044eb0570c7SDamien Le Moal */
1045eb0570c7SDamien Le Moal if (file_inode(zone->file)->i_sb->s_blocksize <= SZ_4K)
1046eb0570c7SDamien Le Moal zlo->block_size = file_inode(zone->file)->i_sb->s_blocksize;
1047eb0570c7SDamien Le Moal else if (!vfs_getattr(&zone->file->f_path, &st, STATX_DIOALIGN, 0) &&
1048eb0570c7SDamien Le Moal (st.result_mask & STATX_DIOALIGN))
1049eb0570c7SDamien Le Moal zlo->block_size = st.dio_offset_align;
1050eb0570c7SDamien Le Moal else if (sb_bdev)
1051eb0570c7SDamien Le Moal zlo->block_size = bdev_physical_block_size(sb_bdev);
1052eb0570c7SDamien Le Moal else
1053eb0570c7SDamien Le Moal zlo->block_size = SECTOR_SIZE;
1054eb0570c7SDamien Le Moal
1055eb0570c7SDamien Le Moal if (zlo->zone_capacity & ((zlo->block_size >> SECTOR_SHIFT) - 1)) {
1056eb0570c7SDamien Le Moal pr_err("Zone capacity is not aligned to block size %u\n",
1057eb0570c7SDamien Le Moal zlo->block_size);
1058eb0570c7SDamien Le Moal return -EINVAL;
1059eb0570c7SDamien Le Moal }
1060eb0570c7SDamien Le Moal
1061eb0570c7SDamien Le Moal return 0;
1062eb0570c7SDamien Le Moal }
1063eb0570c7SDamien Le Moal
zloop_init_zone(struct zloop_device * zlo,struct zloop_options * opts,unsigned int zone_no,bool restore)1064eb0570c7SDamien Le Moal static int zloop_init_zone(struct zloop_device *zlo, struct zloop_options *opts,
1065eb0570c7SDamien Le Moal unsigned int zone_no, bool restore)
1066eb0570c7SDamien Le Moal {
1067eb0570c7SDamien Le Moal struct zloop_zone *zone = &zlo->zones[zone_no];
1068eb0570c7SDamien Le Moal int oflags = O_RDWR;
1069eb0570c7SDamien Le Moal struct kstat stat;
1070eb0570c7SDamien Le Moal sector_t file_sectors;
1071eb0570c7SDamien Le Moal int ret;
1072eb0570c7SDamien Le Moal
1073eb0570c7SDamien Le Moal mutex_init(&zone->lock);
1074b2a78fecSDamien Le Moal INIT_LIST_HEAD(&zone->open_zone_entry);
1075fcc6eaa3SDamien Le Moal spin_lock_init(&zone->wp_lock);
1076eb0570c7SDamien Le Moal zone->start = (sector_t)zone_no << zlo->zone_shift;
1077eb0570c7SDamien Le Moal
1078eb0570c7SDamien Le Moal if (!restore)
1079eb0570c7SDamien Le Moal oflags |= O_CREAT;
1080eb0570c7SDamien Le Moal
1081eb0570c7SDamien Le Moal if (!opts->buffered_io)
1082eb0570c7SDamien Le Moal oflags |= O_DIRECT;
1083eb0570c7SDamien Le Moal
1084eb0570c7SDamien Le Moal if (zone_no < zlo->nr_conv_zones) {
1085eb0570c7SDamien Le Moal /* Conventional zone file. */
1086eb0570c7SDamien Le Moal set_bit(ZLOOP_ZONE_CONV, &zone->flags);
1087eb0570c7SDamien Le Moal zone->cond = BLK_ZONE_COND_NOT_WP;
1088eb0570c7SDamien Le Moal zone->wp = U64_MAX;
1089eb0570c7SDamien Le Moal
1090eb0570c7SDamien Le Moal zone->file = zloop_filp_open_fmt(oflags, 0600, "%s/%u/cnv-%06u",
1091eb0570c7SDamien Le Moal zlo->base_dir, zlo->id, zone_no);
1092eb0570c7SDamien Le Moal if (IS_ERR(zone->file)) {
1093eb0570c7SDamien Le Moal pr_err("Failed to open zone %u file %s/%u/cnv-%06u (err=%ld)",
1094eb0570c7SDamien Le Moal zone_no, zlo->base_dir, zlo->id, zone_no,
1095eb0570c7SDamien Le Moal PTR_ERR(zone->file));
1096eb0570c7SDamien Le Moal return PTR_ERR(zone->file);
1097eb0570c7SDamien Le Moal }
1098eb0570c7SDamien Le Moal
1099eb0570c7SDamien Le Moal if (!zlo->block_size) {
1100eb0570c7SDamien Le Moal ret = zloop_get_block_size(zlo, zone);
1101eb0570c7SDamien Le Moal if (ret)
1102eb0570c7SDamien Le Moal return ret;
1103eb0570c7SDamien Le Moal }
1104eb0570c7SDamien Le Moal
1105eb0570c7SDamien Le Moal ret = vfs_getattr(&zone->file->f_path, &stat, STATX_SIZE, 0);
1106eb0570c7SDamien Le Moal if (ret < 0) {
1107eb0570c7SDamien Le Moal pr_err("Failed to get zone %u file stat\n", zone_no);
1108eb0570c7SDamien Le Moal return ret;
1109eb0570c7SDamien Le Moal }
1110eb0570c7SDamien Le Moal file_sectors = stat.size >> SECTOR_SHIFT;
1111eb0570c7SDamien Le Moal
1112eb0570c7SDamien Le Moal if (restore && file_sectors != zlo->zone_size) {
1113eb0570c7SDamien Le Moal pr_err("Invalid conventional zone %u file size (%llu sectors != %llu)\n",
1114eb0570c7SDamien Le Moal zone_no, file_sectors, zlo->zone_capacity);
1115eb0570c7SDamien Le Moal return ret;
1116eb0570c7SDamien Le Moal }
1117eb0570c7SDamien Le Moal
1118eb0570c7SDamien Le Moal ret = vfs_truncate(&zone->file->f_path,
1119eb0570c7SDamien Le Moal zlo->zone_size << SECTOR_SHIFT);
1120eb0570c7SDamien Le Moal if (ret < 0) {
1121eb0570c7SDamien Le Moal pr_err("Failed to truncate zone %u file (err=%d)\n",
1122eb0570c7SDamien Le Moal zone_no, ret);
1123eb0570c7SDamien Le Moal return ret;
1124eb0570c7SDamien Le Moal }
1125eb0570c7SDamien Le Moal
1126eb0570c7SDamien Le Moal return 0;
1127eb0570c7SDamien Le Moal }
1128eb0570c7SDamien Le Moal
1129eb0570c7SDamien Le Moal /* Sequential zone file. */
1130eb0570c7SDamien Le Moal zone->file = zloop_filp_open_fmt(oflags, 0600, "%s/%u/seq-%06u",
1131eb0570c7SDamien Le Moal zlo->base_dir, zlo->id, zone_no);
1132eb0570c7SDamien Le Moal if (IS_ERR(zone->file)) {
1133eb0570c7SDamien Le Moal pr_err("Failed to open zone %u file %s/%u/seq-%06u (err=%ld)",
1134eb0570c7SDamien Le Moal zone_no, zlo->base_dir, zlo->id, zone_no,
1135eb0570c7SDamien Le Moal PTR_ERR(zone->file));
1136eb0570c7SDamien Le Moal return PTR_ERR(zone->file);
1137eb0570c7SDamien Le Moal }
1138eb0570c7SDamien Le Moal
1139eb0570c7SDamien Le Moal if (!zlo->block_size) {
1140eb0570c7SDamien Le Moal ret = zloop_get_block_size(zlo, zone);
1141eb0570c7SDamien Le Moal if (ret)
1142eb0570c7SDamien Le Moal return ret;
1143eb0570c7SDamien Le Moal }
1144eb0570c7SDamien Le Moal
1145eb0570c7SDamien Le Moal zloop_get_block_size(zlo, zone);
1146eb0570c7SDamien Le Moal
1147eb0570c7SDamien Le Moal mutex_lock(&zone->lock);
1148eb0570c7SDamien Le Moal ret = zloop_update_seq_zone(zlo, zone_no);
1149eb0570c7SDamien Le Moal mutex_unlock(&zone->lock);
1150eb0570c7SDamien Le Moal
1151eb0570c7SDamien Le Moal return ret;
1152eb0570c7SDamien Le Moal }
1153eb0570c7SDamien Le Moal
zloop_dev_exists(struct zloop_device * zlo)1154eb0570c7SDamien Le Moal static bool zloop_dev_exists(struct zloop_device *zlo)
1155eb0570c7SDamien Le Moal {
1156eb0570c7SDamien Le Moal struct file *cnv, *seq;
1157eb0570c7SDamien Le Moal bool exists;
1158eb0570c7SDamien Le Moal
1159eb0570c7SDamien Le Moal cnv = zloop_filp_open_fmt(O_RDONLY, 0600, "%s/%u/cnv-%06u",
1160eb0570c7SDamien Le Moal zlo->base_dir, zlo->id, 0);
1161eb0570c7SDamien Le Moal seq = zloop_filp_open_fmt(O_RDONLY, 0600, "%s/%u/seq-%06u",
1162eb0570c7SDamien Le Moal zlo->base_dir, zlo->id, 0);
1163eb0570c7SDamien Le Moal exists = !IS_ERR(cnv) || !IS_ERR(seq);
1164eb0570c7SDamien Le Moal
1165eb0570c7SDamien Le Moal if (!IS_ERR(cnv))
1166eb0570c7SDamien Le Moal fput(cnv);
1167eb0570c7SDamien Le Moal if (!IS_ERR(seq))
1168eb0570c7SDamien Le Moal fput(seq);
1169eb0570c7SDamien Le Moal
1170eb0570c7SDamien Le Moal return exists;
1171eb0570c7SDamien Le Moal }
1172eb0570c7SDamien Le Moal
zloop_ctl_add(struct zloop_options * opts)1173eb0570c7SDamien Le Moal static int zloop_ctl_add(struct zloop_options *opts)
1174eb0570c7SDamien Le Moal {
1175eb0570c7SDamien Le Moal struct queue_limits lim = {
1176eb0570c7SDamien Le Moal .max_hw_sectors = SZ_1M >> SECTOR_SHIFT,
1177eb0570c7SDamien Le Moal .chunk_sectors = opts->zone_size,
11786acf7860SChristoph Hellwig .features = BLK_FEAT_ZONED | BLK_FEAT_WRITE_CACHE,
11796acf7860SChristoph Hellwig
1180eb0570c7SDamien Le Moal };
1181eb0570c7SDamien Le Moal unsigned int nr_zones, i, j;
1182eb0570c7SDamien Le Moal struct zloop_device *zlo;
1183eb0570c7SDamien Le Moal int ret = -EINVAL;
1184eb0570c7SDamien Le Moal bool restore;
1185eb0570c7SDamien Le Moal
1186eb0570c7SDamien Le Moal __module_get(THIS_MODULE);
1187eb0570c7SDamien Le Moal
1188eb0570c7SDamien Le Moal nr_zones = opts->capacity >> ilog2(opts->zone_size);
1189eb0570c7SDamien Le Moal if (opts->nr_conv_zones >= nr_zones) {
1190eb0570c7SDamien Le Moal pr_err("Invalid number of conventional zones %u\n",
1191eb0570c7SDamien Le Moal opts->nr_conv_zones);
1192eb0570c7SDamien Le Moal goto out;
1193eb0570c7SDamien Le Moal }
1194eb0570c7SDamien Le Moal
1195b2a78fecSDamien Le Moal if (opts->max_open_zones > nr_zones - opts->nr_conv_zones) {
1196b2a78fecSDamien Le Moal pr_err("Invalid maximum number of open zones %u\n",
1197b2a78fecSDamien Le Moal opts->max_open_zones);
1198b2a78fecSDamien Le Moal goto out;
1199b2a78fecSDamien Le Moal }
1200b2a78fecSDamien Le Moal
1201323bbfcfSLinus Torvalds zlo = kvzalloc_flex(*zlo, zones, nr_zones);
1202eb0570c7SDamien Le Moal if (!zlo) {
1203eb0570c7SDamien Le Moal ret = -ENOMEM;
1204eb0570c7SDamien Le Moal goto out;
1205eb0570c7SDamien Le Moal }
12064b2b0315SYongpeng Yang WRITE_ONCE(zlo->state, Zlo_creating);
1207b2a78fecSDamien Le Moal spin_lock_init(&zlo->open_zones_lock);
1208b2a78fecSDamien Le Moal INIT_LIST_HEAD(&zlo->open_zones_lru_list);
1209eb0570c7SDamien Le Moal
1210eb0570c7SDamien Le Moal ret = mutex_lock_killable(&zloop_ctl_mutex);
1211eb0570c7SDamien Le Moal if (ret)
1212eb0570c7SDamien Le Moal goto out_free_dev;
1213eb0570c7SDamien Le Moal
1214eb0570c7SDamien Le Moal /* Allocate id, if @opts->id >= 0, we're requesting that specific id */
1215eb0570c7SDamien Le Moal if (opts->id >= 0) {
1216eb0570c7SDamien Le Moal ret = idr_alloc(&zloop_index_idr, zlo,
1217eb0570c7SDamien Le Moal opts->id, opts->id + 1, GFP_KERNEL);
1218eb0570c7SDamien Le Moal if (ret == -ENOSPC)
1219eb0570c7SDamien Le Moal ret = -EEXIST;
1220eb0570c7SDamien Le Moal } else {
1221eb0570c7SDamien Le Moal ret = idr_alloc(&zloop_index_idr, zlo, 0, 0, GFP_KERNEL);
1222eb0570c7SDamien Le Moal }
1223eb0570c7SDamien Le Moal mutex_unlock(&zloop_ctl_mutex);
1224eb0570c7SDamien Le Moal if (ret < 0)
1225eb0570c7SDamien Le Moal goto out_free_dev;
1226eb0570c7SDamien Le Moal
1227eb0570c7SDamien Le Moal zlo->id = ret;
1228eb0570c7SDamien Le Moal zlo->zone_shift = ilog2(opts->zone_size);
1229eb0570c7SDamien Le Moal zlo->zone_size = opts->zone_size;
1230eb0570c7SDamien Le Moal if (opts->zone_capacity)
1231eb0570c7SDamien Le Moal zlo->zone_capacity = opts->zone_capacity;
1232eb0570c7SDamien Le Moal else
1233eb0570c7SDamien Le Moal zlo->zone_capacity = zlo->zone_size;
1234eb0570c7SDamien Le Moal zlo->nr_zones = nr_zones;
1235eb0570c7SDamien Le Moal zlo->nr_conv_zones = opts->nr_conv_zones;
1236b2a78fecSDamien Le Moal zlo->max_open_zones = opts->max_open_zones;
1237eb0570c7SDamien Le Moal zlo->buffered_io = opts->buffered_io;
12389236c5fdSDamien Le Moal zlo->zone_append = opts->zone_append;
1239fcc6eaa3SDamien Le Moal if (zlo->zone_append)
1240fcc6eaa3SDamien Le Moal zlo->ordered_zone_append = opts->ordered_zone_append;
1241829def1eSChristoph Hellwig zlo->discard_write_cache = opts->discard_write_cache;
1242eb0570c7SDamien Le Moal
1243eb0570c7SDamien Le Moal zlo->workqueue = alloc_workqueue("zloop%d", WQ_UNBOUND | WQ_FREEZABLE,
1244eb0570c7SDamien Le Moal opts->nr_queues * opts->queue_depth, zlo->id);
1245eb0570c7SDamien Le Moal if (!zlo->workqueue) {
1246eb0570c7SDamien Le Moal ret = -ENOMEM;
1247eb0570c7SDamien Le Moal goto out_free_idr;
1248eb0570c7SDamien Le Moal }
1249eb0570c7SDamien Le Moal
1250eb0570c7SDamien Le Moal if (opts->base_dir)
1251eb0570c7SDamien Le Moal zlo->base_dir = kstrdup(opts->base_dir, GFP_KERNEL);
1252eb0570c7SDamien Le Moal else
1253eb0570c7SDamien Le Moal zlo->base_dir = kstrdup(ZLOOP_DEF_BASE_DIR, GFP_KERNEL);
1254eb0570c7SDamien Le Moal if (!zlo->base_dir) {
1255eb0570c7SDamien Le Moal ret = -ENOMEM;
1256eb0570c7SDamien Le Moal goto out_destroy_workqueue;
1257eb0570c7SDamien Le Moal }
1258eb0570c7SDamien Le Moal
1259eb0570c7SDamien Le Moal zlo->data_dir = zloop_filp_open_fmt(O_RDONLY | O_DIRECTORY, 0, "%s/%u",
1260eb0570c7SDamien Le Moal zlo->base_dir, zlo->id);
1261eb0570c7SDamien Le Moal if (IS_ERR(zlo->data_dir)) {
1262eb0570c7SDamien Le Moal ret = PTR_ERR(zlo->data_dir);
1263eb0570c7SDamien Le Moal pr_warn("Failed to open directory %s/%u (err=%d)\n",
1264eb0570c7SDamien Le Moal zlo->base_dir, zlo->id, ret);
1265eb0570c7SDamien Le Moal goto out_free_base_dir;
1266eb0570c7SDamien Le Moal }
1267eb0570c7SDamien Le Moal
1268eb0570c7SDamien Le Moal /*
1269eb0570c7SDamien Le Moal * If we already have zone files, we are restoring a device created by a
1270eb0570c7SDamien Le Moal * previous add operation. In this case, zloop_init_zone() will check
1271eb0570c7SDamien Le Moal * that the zone files are consistent with the zone configuration given.
1272eb0570c7SDamien Le Moal */
1273eb0570c7SDamien Le Moal restore = zloop_dev_exists(zlo);
1274eb0570c7SDamien Le Moal for (i = 0; i < nr_zones; i++) {
1275eb0570c7SDamien Le Moal ret = zloop_init_zone(zlo, opts, i, restore);
1276eb0570c7SDamien Le Moal if (ret)
1277eb0570c7SDamien Le Moal goto out_close_files;
1278eb0570c7SDamien Le Moal }
1279eb0570c7SDamien Le Moal
1280eb0570c7SDamien Le Moal lim.physical_block_size = zlo->block_size;
1281eb0570c7SDamien Le Moal lim.logical_block_size = zlo->block_size;
12829236c5fdSDamien Le Moal if (zlo->zone_append)
12839236c5fdSDamien Le Moal lim.max_hw_zone_append_sectors = lim.max_hw_sectors;
1284b2a78fecSDamien Le Moal lim.max_open_zones = zlo->max_open_zones;
1285eb0570c7SDamien Le Moal
1286eb0570c7SDamien Le Moal zlo->tag_set.ops = &zloop_mq_ops;
1287eb0570c7SDamien Le Moal zlo->tag_set.nr_hw_queues = opts->nr_queues;
1288eb0570c7SDamien Le Moal zlo->tag_set.queue_depth = opts->queue_depth;
1289eb0570c7SDamien Le Moal zlo->tag_set.numa_node = NUMA_NO_NODE;
1290eb0570c7SDamien Le Moal zlo->tag_set.cmd_size = sizeof(struct zloop_cmd);
1291eb0570c7SDamien Le Moal zlo->tag_set.driver_data = zlo;
1292eb0570c7SDamien Le Moal
1293eb0570c7SDamien Le Moal ret = blk_mq_alloc_tag_set(&zlo->tag_set);
1294eb0570c7SDamien Le Moal if (ret) {
1295eb0570c7SDamien Le Moal pr_err("blk_mq_alloc_tag_set failed (err=%d)\n", ret);
1296eb0570c7SDamien Le Moal goto out_close_files;
1297eb0570c7SDamien Le Moal }
1298eb0570c7SDamien Le Moal
1299eb0570c7SDamien Le Moal zlo->disk = blk_mq_alloc_disk(&zlo->tag_set, &lim, zlo);
1300eb0570c7SDamien Le Moal if (IS_ERR(zlo->disk)) {
1301eb0570c7SDamien Le Moal pr_err("blk_mq_alloc_disk failed (err=%d)\n", ret);
1302eb0570c7SDamien Le Moal ret = PTR_ERR(zlo->disk);
1303eb0570c7SDamien Le Moal goto out_cleanup_tags;
1304eb0570c7SDamien Le Moal }
1305eb0570c7SDamien Le Moal zlo->disk->flags = GENHD_FL_NO_PART;
1306eb0570c7SDamien Le Moal zlo->disk->fops = &zloop_fops;
1307eb0570c7SDamien Le Moal zlo->disk->private_data = zlo;
1308eb0570c7SDamien Le Moal sprintf(zlo->disk->disk_name, "zloop%d", zlo->id);
1309eb0570c7SDamien Le Moal set_capacity(zlo->disk, (u64)lim.chunk_sectors * zlo->nr_zones);
1310eb0570c7SDamien Le Moal
1311eb0570c7SDamien Le Moal ret = blk_revalidate_disk_zones(zlo->disk);
1312eb0570c7SDamien Le Moal if (ret)
1313eb0570c7SDamien Le Moal goto out_cleanup_disk;
1314eb0570c7SDamien Le Moal
1315eb0570c7SDamien Le Moal ret = add_disk(zlo->disk);
1316eb0570c7SDamien Le Moal if (ret) {
1317eb0570c7SDamien Le Moal pr_err("add_disk failed (err=%d)\n", ret);
1318eb0570c7SDamien Le Moal goto out_cleanup_disk;
1319eb0570c7SDamien Le Moal }
1320eb0570c7SDamien Le Moal
1321eb0570c7SDamien Le Moal mutex_lock(&zloop_ctl_mutex);
13224b2b0315SYongpeng Yang WRITE_ONCE(zlo->state, Zlo_live);
1323eb0570c7SDamien Le Moal mutex_unlock(&zloop_ctl_mutex);
1324eb0570c7SDamien Le Moal
13259236c5fdSDamien Le Moal pr_info("zloop: device %d, %u zones of %llu MiB, %u B block size\n",
1326eb0570c7SDamien Le Moal zlo->id, zlo->nr_zones,
1327eb0570c7SDamien Le Moal ((sector_t)zlo->zone_size << SECTOR_SHIFT) >> 20,
1328eb0570c7SDamien Le Moal zlo->block_size);
1329fcc6eaa3SDamien Le Moal pr_info("zloop%d: using %s%s zone append\n",
13309236c5fdSDamien Le Moal zlo->id,
1331fcc6eaa3SDamien Le Moal zlo->ordered_zone_append ? "ordered " : "",
13329236c5fdSDamien Le Moal zlo->zone_append ? "native" : "emulated");
1333eb0570c7SDamien Le Moal
1334eb0570c7SDamien Le Moal return 0;
1335eb0570c7SDamien Le Moal
1336eb0570c7SDamien Le Moal out_cleanup_disk:
1337eb0570c7SDamien Le Moal put_disk(zlo->disk);
1338eb0570c7SDamien Le Moal out_cleanup_tags:
1339eb0570c7SDamien Le Moal blk_mq_free_tag_set(&zlo->tag_set);
1340eb0570c7SDamien Le Moal out_close_files:
1341eb0570c7SDamien Le Moal for (j = 0; j < i; j++) {
1342eb0570c7SDamien Le Moal struct zloop_zone *zone = &zlo->zones[j];
1343eb0570c7SDamien Le Moal
1344eb0570c7SDamien Le Moal if (!IS_ERR_OR_NULL(zone->file))
1345eb0570c7SDamien Le Moal fput(zone->file);
1346eb0570c7SDamien Le Moal }
1347eb0570c7SDamien Le Moal fput(zlo->data_dir);
1348eb0570c7SDamien Le Moal out_free_base_dir:
1349eb0570c7SDamien Le Moal kfree(zlo->base_dir);
1350eb0570c7SDamien Le Moal out_destroy_workqueue:
1351eb0570c7SDamien Le Moal destroy_workqueue(zlo->workqueue);
1352eb0570c7SDamien Le Moal out_free_idr:
1353eb0570c7SDamien Le Moal mutex_lock(&zloop_ctl_mutex);
1354eb0570c7SDamien Le Moal idr_remove(&zloop_index_idr, zlo->id);
1355eb0570c7SDamien Le Moal mutex_unlock(&zloop_ctl_mutex);
1356eb0570c7SDamien Le Moal out_free_dev:
1357eb0570c7SDamien Le Moal kvfree(zlo);
1358eb0570c7SDamien Le Moal out:
1359eb0570c7SDamien Le Moal module_put(THIS_MODULE);
1360eb0570c7SDamien Le Moal if (ret == -ENOENT)
1361eb0570c7SDamien Le Moal ret = -EINVAL;
1362eb0570c7SDamien Le Moal return ret;
1363eb0570c7SDamien Le Moal }
1364eb0570c7SDamien Le Moal
zloop_forget_cache(struct zloop_device * zlo)1365829def1eSChristoph Hellwig static void zloop_forget_cache(struct zloop_device *zlo)
1366829def1eSChristoph Hellwig {
1367829def1eSChristoph Hellwig unsigned int i;
1368829def1eSChristoph Hellwig int ret;
1369829def1eSChristoph Hellwig
1370829def1eSChristoph Hellwig pr_info("%pg: discarding volatile write cache\n", zlo->disk->part0);
1371829def1eSChristoph Hellwig
1372829def1eSChristoph Hellwig for (i = 0; i < zlo->nr_zones; i++) {
1373829def1eSChristoph Hellwig struct zloop_zone *zone = &zlo->zones[i];
1374829def1eSChristoph Hellwig struct file *file = zone->file;
1375829def1eSChristoph Hellwig sector_t old_wp;
1376829def1eSChristoph Hellwig
1377829def1eSChristoph Hellwig if (!zloop_zone_is_active(zone))
1378829def1eSChristoph Hellwig continue;
1379829def1eSChristoph Hellwig
1380829def1eSChristoph Hellwig ret = vfs_getxattr(file_mnt_idmap(file), file_dentry(file),
1381829def1eSChristoph Hellwig "user.zloop.wp", &old_wp, sizeof(old_wp));
1382829def1eSChristoph Hellwig if (ret == -ENODATA) {
1383829def1eSChristoph Hellwig old_wp = 0;
1384829def1eSChristoph Hellwig } else if (ret != sizeof(old_wp)) {
1385829def1eSChristoph Hellwig pr_err("%pg: failed to retrieve write pointer (%d)\n",
1386829def1eSChristoph Hellwig zlo->disk->part0, ret);
1387829def1eSChristoph Hellwig continue;
1388829def1eSChristoph Hellwig }
138932be3c01SChristoph Hellwig
139032be3c01SChristoph Hellwig if (old_wp > zone->wp)
139132be3c01SChristoph Hellwig continue;
139232be3c01SChristoph Hellwig /*
139332be3c01SChristoph Hellwig * This should not happen, if we recored a full zone, it can't
139432be3c01SChristoph Hellwig * be active.
139532be3c01SChristoph Hellwig */
139632be3c01SChristoph Hellwig if (WARN_ON_ONCE(old_wp == ULLONG_MAX))
139732be3c01SChristoph Hellwig continue;
139832be3c01SChristoph Hellwig
139914e00779SChristoph Hellwig vfs_truncate(&file->f_path,
140014e00779SChristoph Hellwig (old_wp - zone->start) << SECTOR_SHIFT);
1401829def1eSChristoph Hellwig }
1402829def1eSChristoph Hellwig }
1403829def1eSChristoph Hellwig
zloop_ctl_remove(struct zloop_options * opts)1404eb0570c7SDamien Le Moal static int zloop_ctl_remove(struct zloop_options *opts)
1405eb0570c7SDamien Le Moal {
1406eb0570c7SDamien Le Moal struct zloop_device *zlo;
1407eb0570c7SDamien Le Moal int ret;
1408eb0570c7SDamien Le Moal
1409eb0570c7SDamien Le Moal if (!(opts->mask & ZLOOP_OPT_ID)) {
14103c461711SChristoph Hellwig pr_err("No ID specified for remove\n");
14113c461711SChristoph Hellwig return -EINVAL;
14123c461711SChristoph Hellwig }
14133c461711SChristoph Hellwig
14143c461711SChristoph Hellwig if (opts->mask & ~ZLOOP_OPT_ID) {
14153c461711SChristoph Hellwig pr_err("Invalid option specified for remove\n");
1416eb0570c7SDamien Le Moal return -EINVAL;
1417eb0570c7SDamien Le Moal }
1418eb0570c7SDamien Le Moal
1419eb0570c7SDamien Le Moal ret = mutex_lock_killable(&zloop_ctl_mutex);
1420eb0570c7SDamien Le Moal if (ret)
1421eb0570c7SDamien Le Moal return ret;
1422eb0570c7SDamien Le Moal
1423eb0570c7SDamien Le Moal zlo = idr_find(&zloop_index_idr, opts->id);
1424eb0570c7SDamien Le Moal if (!zlo || zlo->state == Zlo_creating) {
1425eb0570c7SDamien Le Moal ret = -ENODEV;
1426eb0570c7SDamien Le Moal } else if (zlo->state == Zlo_deleting) {
1427eb0570c7SDamien Le Moal ret = -EINVAL;
1428eb0570c7SDamien Le Moal } else {
1429eb0570c7SDamien Le Moal idr_remove(&zloop_index_idr, zlo->id);
14304b2b0315SYongpeng Yang WRITE_ONCE(zlo->state, Zlo_deleting);
1431eb0570c7SDamien Le Moal }
1432eb0570c7SDamien Le Moal
1433eb0570c7SDamien Le Moal mutex_unlock(&zloop_ctl_mutex);
1434eb0570c7SDamien Le Moal if (ret)
1435eb0570c7SDamien Le Moal return ret;
1436eb0570c7SDamien Le Moal
1437eb0570c7SDamien Le Moal del_gendisk(zlo->disk);
1438829def1eSChristoph Hellwig
1439829def1eSChristoph Hellwig if (zlo->discard_write_cache)
1440829def1eSChristoph Hellwig zloop_forget_cache(zlo);
1441829def1eSChristoph Hellwig
1442eb0570c7SDamien Le Moal put_disk(zlo->disk);
1443eb0570c7SDamien Le Moal
1444eb0570c7SDamien Le Moal pr_info("Removed device %d\n", opts->id);
1445eb0570c7SDamien Le Moal
1446eb0570c7SDamien Le Moal module_put(THIS_MODULE);
1447eb0570c7SDamien Le Moal
1448eb0570c7SDamien Le Moal return 0;
1449eb0570c7SDamien Le Moal }
1450eb0570c7SDamien Le Moal
zloop_parse_options(struct zloop_options * opts,const char * buf)1451eb0570c7SDamien Le Moal static int zloop_parse_options(struct zloop_options *opts, const char *buf)
1452eb0570c7SDamien Le Moal {
1453eb0570c7SDamien Le Moal substring_t args[MAX_OPT_ARGS];
1454eb0570c7SDamien Le Moal char *options, *o, *p;
1455eb0570c7SDamien Le Moal unsigned int token;
1456eb0570c7SDamien Le Moal int ret = 0;
1457eb0570c7SDamien Le Moal
1458eb0570c7SDamien Le Moal /* Set defaults. */
1459eb0570c7SDamien Le Moal opts->mask = 0;
1460eb0570c7SDamien Le Moal opts->id = ZLOOP_DEF_ID;
1461eb0570c7SDamien Le Moal opts->capacity = ZLOOP_DEF_ZONE_SIZE * ZLOOP_DEF_NR_ZONES;
1462eb0570c7SDamien Le Moal opts->zone_size = ZLOOP_DEF_ZONE_SIZE;
1463eb0570c7SDamien Le Moal opts->nr_conv_zones = ZLOOP_DEF_NR_CONV_ZONES;
1464b2a78fecSDamien Le Moal opts->max_open_zones = ZLOOP_DEF_MAX_OPEN_ZONES;
1465eb0570c7SDamien Le Moal opts->nr_queues = ZLOOP_DEF_NR_QUEUES;
1466eb0570c7SDamien Le Moal opts->queue_depth = ZLOOP_DEF_QUEUE_DEPTH;
1467eb0570c7SDamien Le Moal opts->buffered_io = ZLOOP_DEF_BUFFERED_IO;
14689236c5fdSDamien Le Moal opts->zone_append = ZLOOP_DEF_ZONE_APPEND;
1469fcc6eaa3SDamien Le Moal opts->ordered_zone_append = ZLOOP_DEF_ORDERED_ZONE_APPEND;
1470eb0570c7SDamien Le Moal
1471eb0570c7SDamien Le Moal if (!buf)
1472eb0570c7SDamien Le Moal return 0;
1473eb0570c7SDamien Le Moal
1474eb0570c7SDamien Le Moal /* Skip leading spaces before the options. */
1475eb0570c7SDamien Le Moal while (isspace(*buf))
1476eb0570c7SDamien Le Moal buf++;
1477eb0570c7SDamien Le Moal
1478eb0570c7SDamien Le Moal options = o = kstrdup(buf, GFP_KERNEL);
1479eb0570c7SDamien Le Moal if (!options)
1480eb0570c7SDamien Le Moal return -ENOMEM;
1481eb0570c7SDamien Le Moal
1482eb0570c7SDamien Le Moal /* Parse the options, doing only some light invalid value checks. */
1483eb0570c7SDamien Le Moal while ((p = strsep(&o, ",\n")) != NULL) {
1484eb0570c7SDamien Le Moal if (!*p)
1485eb0570c7SDamien Le Moal continue;
1486eb0570c7SDamien Le Moal
1487eb0570c7SDamien Le Moal token = match_token(p, zloop_opt_tokens, args);
1488eb0570c7SDamien Le Moal opts->mask |= token;
1489eb0570c7SDamien Le Moal switch (token) {
1490eb0570c7SDamien Le Moal case ZLOOP_OPT_ID:
1491eb0570c7SDamien Le Moal if (match_int(args, &opts->id)) {
1492eb0570c7SDamien Le Moal ret = -EINVAL;
1493eb0570c7SDamien Le Moal goto out;
1494eb0570c7SDamien Le Moal }
1495eb0570c7SDamien Le Moal break;
1496eb0570c7SDamien Le Moal case ZLOOP_OPT_CAPACITY:
1497eb0570c7SDamien Le Moal if (match_uint(args, &token)) {
1498eb0570c7SDamien Le Moal ret = -EINVAL;
1499eb0570c7SDamien Le Moal goto out;
1500eb0570c7SDamien Le Moal }
1501eb0570c7SDamien Le Moal if (!token) {
1502eb0570c7SDamien Le Moal pr_err("Invalid capacity\n");
1503eb0570c7SDamien Le Moal ret = -EINVAL;
1504eb0570c7SDamien Le Moal goto out;
1505eb0570c7SDamien Le Moal }
1506eb0570c7SDamien Le Moal opts->capacity =
1507eb0570c7SDamien Le Moal ((sector_t)token * SZ_1M) >> SECTOR_SHIFT;
1508eb0570c7SDamien Le Moal break;
1509eb0570c7SDamien Le Moal case ZLOOP_OPT_ZONE_SIZE:
1510eb0570c7SDamien Le Moal if (match_uint(args, &token)) {
1511eb0570c7SDamien Le Moal ret = -EINVAL;
1512eb0570c7SDamien Le Moal goto out;
1513eb0570c7SDamien Le Moal }
1514eb0570c7SDamien Le Moal if (!token || token > ZLOOP_MAX_ZONE_SIZE_MB ||
1515eb0570c7SDamien Le Moal !is_power_of_2(token)) {
1516eb0570c7SDamien Le Moal pr_err("Invalid zone size %u\n", token);
1517eb0570c7SDamien Le Moal ret = -EINVAL;
1518eb0570c7SDamien Le Moal goto out;
1519eb0570c7SDamien Le Moal }
1520eb0570c7SDamien Le Moal opts->zone_size =
1521eb0570c7SDamien Le Moal ((sector_t)token * SZ_1M) >> SECTOR_SHIFT;
1522eb0570c7SDamien Le Moal break;
1523eb0570c7SDamien Le Moal case ZLOOP_OPT_ZONE_CAPACITY:
1524eb0570c7SDamien Le Moal if (match_uint(args, &token)) {
1525eb0570c7SDamien Le Moal ret = -EINVAL;
1526eb0570c7SDamien Le Moal goto out;
1527eb0570c7SDamien Le Moal }
1528eb0570c7SDamien Le Moal if (!token) {
1529eb0570c7SDamien Le Moal pr_err("Invalid zone capacity\n");
1530eb0570c7SDamien Le Moal ret = -EINVAL;
1531eb0570c7SDamien Le Moal goto out;
1532eb0570c7SDamien Le Moal }
1533eb0570c7SDamien Le Moal opts->zone_capacity =
1534eb0570c7SDamien Le Moal ((sector_t)token * SZ_1M) >> SECTOR_SHIFT;
1535eb0570c7SDamien Le Moal break;
1536eb0570c7SDamien Le Moal case ZLOOP_OPT_NR_CONV_ZONES:
1537eb0570c7SDamien Le Moal if (match_uint(args, &token)) {
1538eb0570c7SDamien Le Moal ret = -EINVAL;
1539eb0570c7SDamien Le Moal goto out;
1540eb0570c7SDamien Le Moal }
1541eb0570c7SDamien Le Moal opts->nr_conv_zones = token;
1542eb0570c7SDamien Le Moal break;
1543b2a78fecSDamien Le Moal case ZLOOP_OPT_MAX_OPEN_ZONES:
1544b2a78fecSDamien Le Moal if (match_uint(args, &token)) {
1545b2a78fecSDamien Le Moal ret = -EINVAL;
1546b2a78fecSDamien Le Moal goto out;
1547b2a78fecSDamien Le Moal }
1548b2a78fecSDamien Le Moal opts->max_open_zones = token;
1549b2a78fecSDamien Le Moal break;
1550eb0570c7SDamien Le Moal case ZLOOP_OPT_BASE_DIR:
1551eb0570c7SDamien Le Moal p = match_strdup(args);
1552eb0570c7SDamien Le Moal if (!p) {
1553eb0570c7SDamien Le Moal ret = -ENOMEM;
1554eb0570c7SDamien Le Moal goto out;
1555eb0570c7SDamien Le Moal }
1556eb0570c7SDamien Le Moal kfree(opts->base_dir);
1557eb0570c7SDamien Le Moal opts->base_dir = p;
1558eb0570c7SDamien Le Moal break;
1559eb0570c7SDamien Le Moal case ZLOOP_OPT_NR_QUEUES:
1560eb0570c7SDamien Le Moal if (match_uint(args, &token)) {
1561eb0570c7SDamien Le Moal ret = -EINVAL;
1562eb0570c7SDamien Le Moal goto out;
1563eb0570c7SDamien Le Moal }
1564eb0570c7SDamien Le Moal if (!token) {
1565eb0570c7SDamien Le Moal pr_err("Invalid number of queues\n");
1566eb0570c7SDamien Le Moal ret = -EINVAL;
1567eb0570c7SDamien Le Moal goto out;
1568eb0570c7SDamien Le Moal }
1569eb0570c7SDamien Le Moal opts->nr_queues = min(token, num_online_cpus());
1570eb0570c7SDamien Le Moal break;
1571eb0570c7SDamien Le Moal case ZLOOP_OPT_QUEUE_DEPTH:
1572eb0570c7SDamien Le Moal if (match_uint(args, &token)) {
1573eb0570c7SDamien Le Moal ret = -EINVAL;
1574eb0570c7SDamien Le Moal goto out;
1575eb0570c7SDamien Le Moal }
1576eb0570c7SDamien Le Moal if (!token) {
1577eb0570c7SDamien Le Moal pr_err("Invalid queue depth\n");
1578eb0570c7SDamien Le Moal ret = -EINVAL;
1579eb0570c7SDamien Le Moal goto out;
1580eb0570c7SDamien Le Moal }
1581eb0570c7SDamien Le Moal opts->queue_depth = token;
1582eb0570c7SDamien Le Moal break;
1583eb0570c7SDamien Le Moal case ZLOOP_OPT_BUFFERED_IO:
1584eb0570c7SDamien Le Moal opts->buffered_io = true;
1585eb0570c7SDamien Le Moal break;
15869236c5fdSDamien Le Moal case ZLOOP_OPT_ZONE_APPEND:
15879236c5fdSDamien Le Moal if (match_uint(args, &token)) {
15889236c5fdSDamien Le Moal ret = -EINVAL;
15899236c5fdSDamien Le Moal goto out;
15909236c5fdSDamien Le Moal }
15919236c5fdSDamien Le Moal if (token != 0 && token != 1) {
15929236c5fdSDamien Le Moal pr_err("Invalid zone_append value\n");
15939236c5fdSDamien Le Moal ret = -EINVAL;
15949236c5fdSDamien Le Moal goto out;
15959236c5fdSDamien Le Moal }
15969236c5fdSDamien Le Moal opts->zone_append = token;
15979236c5fdSDamien Le Moal break;
1598fcc6eaa3SDamien Le Moal case ZLOOP_OPT_ORDERED_ZONE_APPEND:
1599fcc6eaa3SDamien Le Moal opts->ordered_zone_append = true;
1600fcc6eaa3SDamien Le Moal break;
1601829def1eSChristoph Hellwig case ZLOOP_OPT_DISCARD_WRITE_CACHE:
1602829def1eSChristoph Hellwig opts->discard_write_cache = true;
1603829def1eSChristoph Hellwig break;
1604eb0570c7SDamien Le Moal case ZLOOP_OPT_ERR:
1605eb0570c7SDamien Le Moal default:
1606eb0570c7SDamien Le Moal pr_warn("unknown parameter or missing value '%s'\n", p);
1607eb0570c7SDamien Le Moal ret = -EINVAL;
1608eb0570c7SDamien Le Moal goto out;
1609eb0570c7SDamien Le Moal }
1610eb0570c7SDamien Le Moal }
1611eb0570c7SDamien Le Moal
1612eb0570c7SDamien Le Moal ret = -EINVAL;
1613eb0570c7SDamien Le Moal if (opts->capacity <= opts->zone_size) {
1614eb0570c7SDamien Le Moal pr_err("Invalid capacity\n");
1615eb0570c7SDamien Le Moal goto out;
1616eb0570c7SDamien Le Moal }
1617eb0570c7SDamien Le Moal
1618eb0570c7SDamien Le Moal if (opts->zone_capacity > opts->zone_size) {
1619eb0570c7SDamien Le Moal pr_err("Invalid zone capacity\n");
1620eb0570c7SDamien Le Moal goto out;
1621eb0570c7SDamien Le Moal }
1622eb0570c7SDamien Le Moal
1623eb0570c7SDamien Le Moal ret = 0;
1624eb0570c7SDamien Le Moal out:
1625eb0570c7SDamien Le Moal kfree(options);
1626eb0570c7SDamien Le Moal return ret;
1627eb0570c7SDamien Le Moal }
1628eb0570c7SDamien Le Moal
1629eb0570c7SDamien Le Moal enum {
1630eb0570c7SDamien Le Moal ZLOOP_CTL_ADD,
1631eb0570c7SDamien Le Moal ZLOOP_CTL_REMOVE,
1632eb0570c7SDamien Le Moal };
1633eb0570c7SDamien Le Moal
1634eb0570c7SDamien Le Moal static struct zloop_ctl_op {
1635eb0570c7SDamien Le Moal int code;
1636eb0570c7SDamien Le Moal const char *name;
1637eb0570c7SDamien Le Moal } zloop_ctl_ops[] = {
1638eb0570c7SDamien Le Moal { ZLOOP_CTL_ADD, "add" },
1639eb0570c7SDamien Le Moal { ZLOOP_CTL_REMOVE, "remove" },
1640eb0570c7SDamien Le Moal { -1, NULL },
1641eb0570c7SDamien Le Moal };
1642eb0570c7SDamien Le Moal
zloop_ctl_write(struct file * file,const char __user * ubuf,size_t count,loff_t * pos)1643eb0570c7SDamien Le Moal static ssize_t zloop_ctl_write(struct file *file, const char __user *ubuf,
1644eb0570c7SDamien Le Moal size_t count, loff_t *pos)
1645eb0570c7SDamien Le Moal {
1646eb0570c7SDamien Le Moal struct zloop_options opts = { };
1647eb0570c7SDamien Le Moal struct zloop_ctl_op *op;
1648eb0570c7SDamien Le Moal const char *buf, *opts_buf;
1649eb0570c7SDamien Le Moal int i, ret;
1650eb0570c7SDamien Le Moal
1651eb0570c7SDamien Le Moal if (count > PAGE_SIZE)
1652eb0570c7SDamien Le Moal return -ENOMEM;
1653eb0570c7SDamien Le Moal
1654eb0570c7SDamien Le Moal buf = memdup_user_nul(ubuf, count);
1655eb0570c7SDamien Le Moal if (IS_ERR(buf))
1656eb0570c7SDamien Le Moal return PTR_ERR(buf);
1657eb0570c7SDamien Le Moal
1658eb0570c7SDamien Le Moal for (i = 0; i < ARRAY_SIZE(zloop_ctl_ops); i++) {
1659eb0570c7SDamien Le Moal op = &zloop_ctl_ops[i];
1660eb0570c7SDamien Le Moal if (!op->name) {
1661eb0570c7SDamien Le Moal pr_err("Invalid operation\n");
1662eb0570c7SDamien Le Moal ret = -EINVAL;
1663eb0570c7SDamien Le Moal goto out;
1664eb0570c7SDamien Le Moal }
1665eb0570c7SDamien Le Moal if (!strncmp(buf, op->name, strlen(op->name)))
1666eb0570c7SDamien Le Moal break;
1667eb0570c7SDamien Le Moal }
1668eb0570c7SDamien Le Moal
1669eb0570c7SDamien Le Moal if (count <= strlen(op->name))
1670eb0570c7SDamien Le Moal opts_buf = NULL;
1671eb0570c7SDamien Le Moal else
1672eb0570c7SDamien Le Moal opts_buf = buf + strlen(op->name);
1673eb0570c7SDamien Le Moal
1674eb0570c7SDamien Le Moal ret = zloop_parse_options(&opts, opts_buf);
1675eb0570c7SDamien Le Moal if (ret) {
1676eb0570c7SDamien Le Moal pr_err("Failed to parse options\n");
1677eb0570c7SDamien Le Moal goto out;
1678eb0570c7SDamien Le Moal }
1679eb0570c7SDamien Le Moal
1680eb0570c7SDamien Le Moal switch (op->code) {
1681eb0570c7SDamien Le Moal case ZLOOP_CTL_ADD:
1682eb0570c7SDamien Le Moal ret = zloop_ctl_add(&opts);
1683eb0570c7SDamien Le Moal break;
1684eb0570c7SDamien Le Moal case ZLOOP_CTL_REMOVE:
1685eb0570c7SDamien Le Moal ret = zloop_ctl_remove(&opts);
1686eb0570c7SDamien Le Moal break;
1687eb0570c7SDamien Le Moal default:
1688eb0570c7SDamien Le Moal pr_err("Invalid operation\n");
1689eb0570c7SDamien Le Moal ret = -EINVAL;
1690eb0570c7SDamien Le Moal goto out;
1691eb0570c7SDamien Le Moal }
1692eb0570c7SDamien Le Moal
1693eb0570c7SDamien Le Moal out:
1694eb0570c7SDamien Le Moal kfree(opts.base_dir);
1695eb0570c7SDamien Le Moal kfree(buf);
1696eb0570c7SDamien Le Moal return ret ? ret : count;
1697eb0570c7SDamien Le Moal }
1698eb0570c7SDamien Le Moal
zloop_ctl_show(struct seq_file * seq_file,void * private)1699eb0570c7SDamien Le Moal static int zloop_ctl_show(struct seq_file *seq_file, void *private)
1700eb0570c7SDamien Le Moal {
1701eb0570c7SDamien Le Moal const struct match_token *tok;
1702eb0570c7SDamien Le Moal int i;
1703eb0570c7SDamien Le Moal
1704eb0570c7SDamien Le Moal /* Add operation */
1705eb0570c7SDamien Le Moal seq_printf(seq_file, "%s ", zloop_ctl_ops[0].name);
1706eb0570c7SDamien Le Moal for (i = 0; i < ARRAY_SIZE(zloop_opt_tokens); i++) {
1707eb0570c7SDamien Le Moal tok = &zloop_opt_tokens[i];
1708eb0570c7SDamien Le Moal if (!tok->pattern)
1709eb0570c7SDamien Le Moal break;
1710eb0570c7SDamien Le Moal if (i)
1711eb0570c7SDamien Le Moal seq_putc(seq_file, ',');
1712eb0570c7SDamien Le Moal seq_puts(seq_file, tok->pattern);
1713eb0570c7SDamien Le Moal }
1714eb0570c7SDamien Le Moal seq_putc(seq_file, '\n');
1715eb0570c7SDamien Le Moal
1716eb0570c7SDamien Le Moal /* Remove operation */
1717eb0570c7SDamien Le Moal seq_puts(seq_file, zloop_ctl_ops[1].name);
1718eb0570c7SDamien Le Moal seq_puts(seq_file, " id=%d\n");
1719eb0570c7SDamien Le Moal
1720eb0570c7SDamien Le Moal return 0;
1721eb0570c7SDamien Le Moal }
1722eb0570c7SDamien Le Moal
zloop_ctl_open(struct inode * inode,struct file * file)1723eb0570c7SDamien Le Moal static int zloop_ctl_open(struct inode *inode, struct file *file)
1724eb0570c7SDamien Le Moal {
1725eb0570c7SDamien Le Moal file->private_data = NULL;
1726eb0570c7SDamien Le Moal return single_open(file, zloop_ctl_show, NULL);
1727eb0570c7SDamien Le Moal }
1728eb0570c7SDamien Le Moal
zloop_ctl_release(struct inode * inode,struct file * file)1729eb0570c7SDamien Le Moal static int zloop_ctl_release(struct inode *inode, struct file *file)
1730eb0570c7SDamien Le Moal {
1731eb0570c7SDamien Le Moal return single_release(inode, file);
1732eb0570c7SDamien Le Moal }
1733eb0570c7SDamien Le Moal
1734eb0570c7SDamien Le Moal static const struct file_operations zloop_ctl_fops = {
1735eb0570c7SDamien Le Moal .owner = THIS_MODULE,
1736eb0570c7SDamien Le Moal .open = zloop_ctl_open,
1737eb0570c7SDamien Le Moal .release = zloop_ctl_release,
1738eb0570c7SDamien Le Moal .write = zloop_ctl_write,
1739eb0570c7SDamien Le Moal .read = seq_read,
1740eb0570c7SDamien Le Moal };
1741eb0570c7SDamien Le Moal
1742eb0570c7SDamien Le Moal static struct miscdevice zloop_misc = {
1743eb0570c7SDamien Le Moal .minor = MISC_DYNAMIC_MINOR,
1744eb0570c7SDamien Le Moal .name = "zloop-control",
1745eb0570c7SDamien Le Moal .fops = &zloop_ctl_fops,
1746eb0570c7SDamien Le Moal };
1747eb0570c7SDamien Le Moal
zloop_init(void)1748eb0570c7SDamien Le Moal static int __init zloop_init(void)
1749eb0570c7SDamien Le Moal {
1750eb0570c7SDamien Le Moal int ret;
1751eb0570c7SDamien Le Moal
1752eb0570c7SDamien Le Moal ret = misc_register(&zloop_misc);
1753eb0570c7SDamien Le Moal if (ret) {
1754eb0570c7SDamien Le Moal pr_err("Failed to register misc device: %d\n", ret);
1755eb0570c7SDamien Le Moal return ret;
1756eb0570c7SDamien Le Moal }
1757eb0570c7SDamien Le Moal pr_info("Module loaded\n");
1758eb0570c7SDamien Le Moal
1759eb0570c7SDamien Le Moal return 0;
1760eb0570c7SDamien Le Moal }
1761eb0570c7SDamien Le Moal
zloop_exit(void)1762eb0570c7SDamien Le Moal static void __exit zloop_exit(void)
1763eb0570c7SDamien Le Moal {
1764eb0570c7SDamien Le Moal misc_deregister(&zloop_misc);
1765eb0570c7SDamien Le Moal idr_destroy(&zloop_index_idr);
1766eb0570c7SDamien Le Moal }
1767eb0570c7SDamien Le Moal
1768eb0570c7SDamien Le Moal module_init(zloop_init);
1769eb0570c7SDamien Le Moal module_exit(zloop_exit);
1770eb0570c7SDamien Le Moal
1771eb0570c7SDamien Le Moal MODULE_DESCRIPTION("Zoned loopback device");
1772eb0570c7SDamien Le Moal MODULE_LICENSE("GPL");
1773