Skip to content

Commit b2a78fe

Browse files
damien-lemoalaxboe
authored andcommitted
zloop: add max_open_zones option
Introduce the new max_open_zones option to allow specifying a limit on the maximum number of open zones of a zloop device. This change allows creating a zloop device that can more closely mimick the characteristics of a physical SMR drive. When set to a non zero value, only up to max_open_zones zones can be in the implicit open (BLK_ZONE_COND_IMP_OPEN) and explicit open (BLK_ZONE_COND_EXP_OPEN) conditions at any time. The transition to the implicit open condition of a zone on a write operation can result in an implicit close of an already implicitly open zone. This is handled in the function zloop_do_open_zone(). This function also handles transitions to the explicit open condition. Implicit close transitions are handled using an LRU ordered list of open zones which is managed using the helper functions zloop_lru_rotate_open_zone() and zloop_lru_remove_open_zone(). Signed-off-by: Damien Le Moal <dlemoal@kernel.org> Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com> Reviewed-by: Christoph Hellwig <hch@lst.de> Link: https://patch.msgid.link/20260326203245.946830-1-dlemoal@kernel.org Signed-off-by: Jens Axboe <axboe@kernel.dk>
1 parent 2a2f520 commit b2a78fe

2 files changed

Lines changed: 168 additions & 17 deletions

File tree

Documentation/admin-guide/blockdev/zoned_loop.rst

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ The options available for the add command can be listed by reading the
6262
/dev/zloop-control device::
6363

6464
$ cat /dev/zloop-control
65-
add id=%d,capacity_mb=%u,zone_size_mb=%u,zone_capacity_mb=%u,conv_zones=%u,base_dir=%s,nr_queues=%u,queue_depth=%u,buffered_io
65+
add id=%d,capacity_mb=%u,zone_size_mb=%u,zone_capacity_mb=%u,conv_zones=%u,max_open_zones=%u,base_dir=%s,nr_queues=%u,queue_depth=%u,buffered_io,zone_append=%u,ordered_zone_append,discard_write_cache
6666
remove id=%d
6767

6868
In more details, the options that can be used with the "add" command are as
@@ -80,6 +80,9 @@ zone_capacity_mb Device zone capacity (must always be equal to or lower
8080
conv_zones Total number of conventioanl zones starting from
8181
sector 0
8282
Default: 8
83+
max_open_zones Maximum number of open sequential write required zones
84+
(0 for no limit).
85+
Default: 0
8386
base_dir Path to the base directory where to create the directory
8487
containing the zone files of the device.
8588
Default=/var/local/zloop.

drivers/block/zloop.c

Lines changed: 164 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ enum {
3636
ZLOOP_OPT_ZONE_APPEND = (1 << 9),
3737
ZLOOP_OPT_ORDERED_ZONE_APPEND = (1 << 10),
3838
ZLOOP_OPT_DISCARD_WRITE_CACHE = (1 << 11),
39+
ZLOOP_OPT_MAX_OPEN_ZONES = (1 << 12),
3940
};
4041

4142
static const match_table_t zloop_opt_tokens = {
@@ -51,6 +52,7 @@ static const match_table_t zloop_opt_tokens = {
5152
{ ZLOOP_OPT_ZONE_APPEND, "zone_append=%u" },
5253
{ ZLOOP_OPT_ORDERED_ZONE_APPEND, "ordered_zone_append" },
5354
{ ZLOOP_OPT_DISCARD_WRITE_CACHE, "discard_write_cache" },
55+
{ ZLOOP_OPT_MAX_OPEN_ZONES, "max_open_zones=%u" },
5456
{ ZLOOP_OPT_ERR, NULL }
5557
};
5658

@@ -59,6 +61,7 @@ static const match_table_t zloop_opt_tokens = {
5961
#define ZLOOP_DEF_ZONE_SIZE ((256ULL * SZ_1M) >> SECTOR_SHIFT)
6062
#define ZLOOP_DEF_NR_ZONES 64
6163
#define ZLOOP_DEF_NR_CONV_ZONES 8
64+
#define ZLOOP_DEF_MAX_OPEN_ZONES 0
6265
#define ZLOOP_DEF_BASE_DIR "/var/local/zloop"
6366
#define ZLOOP_DEF_NR_QUEUES 1
6467
#define ZLOOP_DEF_QUEUE_DEPTH 128
@@ -76,6 +79,7 @@ struct zloop_options {
7679
sector_t zone_size;
7780
sector_t zone_capacity;
7881
unsigned int nr_conv_zones;
82+
unsigned int max_open_zones;
7983
char *base_dir;
8084
unsigned int nr_queues;
8185
unsigned int queue_depth;
@@ -99,7 +103,12 @@ enum zloop_zone_flags {
99103
ZLOOP_ZONE_SEQ_ERROR,
100104
};
101105

106+
/*
107+
* Zone descriptor.
108+
* Locking order: z.lock -> z.wp_lock -> zlo.open_zones_lock
109+
*/
102110
struct zloop_zone {
111+
struct list_head open_zone_entry;
103112
struct file *file;
104113

105114
unsigned long flags;
@@ -133,8 +142,13 @@ struct zloop_device {
133142
sector_t zone_capacity;
134143
unsigned int nr_zones;
135144
unsigned int nr_conv_zones;
145+
unsigned int max_open_zones;
136146
unsigned int block_size;
137147

148+
spinlock_t open_zones_lock;
149+
struct list_head open_zones_lru_list;
150+
unsigned int nr_open_zones;
151+
138152
struct zloop_zone zones[] __counted_by(nr_zones);
139153
};
140154

@@ -158,6 +172,122 @@ static unsigned int rq_zone_no(struct request *rq)
158172
return blk_rq_pos(rq) >> zlo->zone_shift;
159173
}
160174

175+
/*
176+
* Open an already open zone. This is mostly a no-op, except for the imp open ->
177+
* exp open condition change that may happen. We also move a zone at the tail of
178+
* the list of open zones so that if we need to
179+
* implicitly close one open zone, we can do so in LRU order.
180+
*/
181+
static inline void zloop_lru_rotate_open_zone(struct zloop_device *zlo,
182+
struct zloop_zone *zone)
183+
{
184+
if (zlo->max_open_zones) {
185+
spin_lock(&zlo->open_zones_lock);
186+
list_move_tail(&zone->open_zone_entry,
187+
&zlo->open_zones_lru_list);
188+
spin_unlock(&zlo->open_zones_lock);
189+
}
190+
}
191+
192+
static inline void zloop_lru_remove_open_zone(struct zloop_device *zlo,
193+
struct zloop_zone *zone)
194+
{
195+
if (zone->cond == BLK_ZONE_COND_IMP_OPEN ||
196+
zone->cond == BLK_ZONE_COND_EXP_OPEN) {
197+
spin_lock(&zlo->open_zones_lock);
198+
list_del_init(&zone->open_zone_entry);
199+
zlo->nr_open_zones--;
200+
spin_unlock(&zlo->open_zones_lock);
201+
}
202+
}
203+
204+
static inline bool zloop_can_open_zone(struct zloop_device *zlo)
205+
{
206+
return !zlo->max_open_zones || zlo->nr_open_zones < zlo->max_open_zones;
207+
}
208+
209+
/*
210+
* If we have reached the maximum open zones limit, attempt to close an
211+
* implicitly open zone (if we have any) so that we can implicitly open another
212+
* zone without exceeding the maximum number of open zones.
213+
*/
214+
static bool zloop_close_imp_open_zone(struct zloop_device *zlo)
215+
{
216+
struct zloop_zone *zone;
217+
218+
lockdep_assert_held(&zlo->open_zones_lock);
219+
220+
if (zloop_can_open_zone(zlo))
221+
return true;
222+
223+
list_for_each_entry(zone, &zlo->open_zones_lru_list, open_zone_entry) {
224+
if (zone->cond == BLK_ZONE_COND_IMP_OPEN) {
225+
zone->cond = BLK_ZONE_COND_CLOSED;
226+
list_del_init(&zone->open_zone_entry);
227+
zlo->nr_open_zones--;
228+
return true;
229+
}
230+
}
231+
232+
return false;
233+
}
234+
235+
static bool zloop_open_closed_or_empty_zone(struct zloop_device *zlo,
236+
struct zloop_zone *zone,
237+
bool explicit)
238+
{
239+
spin_lock(&zlo->open_zones_lock);
240+
241+
if (explicit) {
242+
/*
243+
* Explicit open: we cannot allow this if we have reached the
244+
* maximum open zones limit.
245+
*/
246+
if (!zloop_can_open_zone(zlo))
247+
goto fail;
248+
zone->cond = BLK_ZONE_COND_EXP_OPEN;
249+
} else {
250+
/*
251+
* Implicit open case: if we have reached the maximum open zones
252+
* limit, try to close an implicitly open zone first.
253+
*/
254+
if (!zloop_close_imp_open_zone(zlo))
255+
goto fail;
256+
zone->cond = BLK_ZONE_COND_IMP_OPEN;
257+
}
258+
259+
zlo->nr_open_zones++;
260+
list_add_tail(&zone->open_zone_entry,
261+
&zlo->open_zones_lru_list);
262+
263+
spin_unlock(&zlo->open_zones_lock);
264+
265+
return true;
266+
267+
fail:
268+
spin_unlock(&zlo->open_zones_lock);
269+
270+
return false;
271+
}
272+
273+
static bool zloop_do_open_zone(struct zloop_device *zlo,
274+
struct zloop_zone *zone, bool explicit)
275+
{
276+
switch (zone->cond) {
277+
case BLK_ZONE_COND_IMP_OPEN:
278+
case BLK_ZONE_COND_EXP_OPEN:
279+
if (explicit)
280+
zone->cond = BLK_ZONE_COND_EXP_OPEN;
281+
zloop_lru_rotate_open_zone(zlo, zone);
282+
return true;
283+
case BLK_ZONE_COND_EMPTY:
284+
case BLK_ZONE_COND_CLOSED:
285+
return zloop_open_closed_or_empty_zone(zlo, zone, explicit);
286+
default:
287+
return false;
288+
}
289+
}
290+
161291
static int zloop_update_seq_zone(struct zloop_device *zlo, unsigned int zone_no)
162292
{
163293
struct zloop_zone *zone = &zlo->zones[zone_no];
@@ -191,13 +321,17 @@ static int zloop_update_seq_zone(struct zloop_device *zlo, unsigned int zone_no)
191321

192322
spin_lock_irqsave(&zone->wp_lock, flags);
193323
if (!file_sectors) {
324+
zloop_lru_remove_open_zone(zlo, zone);
194325
zone->cond = BLK_ZONE_COND_EMPTY;
195326
zone->wp = zone->start;
196327
} else if (file_sectors == zlo->zone_capacity) {
328+
zloop_lru_remove_open_zone(zlo, zone);
197329
zone->cond = BLK_ZONE_COND_FULL;
198330
zone->wp = ULLONG_MAX;
199331
} else {
200-
zone->cond = BLK_ZONE_COND_CLOSED;
332+
if (zone->cond != BLK_ZONE_COND_IMP_OPEN &&
333+
zone->cond != BLK_ZONE_COND_EXP_OPEN)
334+
zone->cond = BLK_ZONE_COND_CLOSED;
201335
zone->wp = zone->start + file_sectors;
202336
}
203337
spin_unlock_irqrestore(&zone->wp_lock, flags);
@@ -221,19 +355,8 @@ static int zloop_open_zone(struct zloop_device *zlo, unsigned int zone_no)
221355
goto unlock;
222356
}
223357

224-
switch (zone->cond) {
225-
case BLK_ZONE_COND_EXP_OPEN:
226-
break;
227-
case BLK_ZONE_COND_EMPTY:
228-
case BLK_ZONE_COND_CLOSED:
229-
case BLK_ZONE_COND_IMP_OPEN:
230-
zone->cond = BLK_ZONE_COND_EXP_OPEN;
231-
break;
232-
case BLK_ZONE_COND_FULL:
233-
default:
358+
if (!zloop_do_open_zone(zlo, zone, true))
234359
ret = -EIO;
235-
break;
236-
}
237360

238361
unlock:
239362
mutex_unlock(&zone->lock);
@@ -264,6 +387,7 @@ static int zloop_close_zone(struct zloop_device *zlo, unsigned int zone_no)
264387
case BLK_ZONE_COND_IMP_OPEN:
265388
case BLK_ZONE_COND_EXP_OPEN:
266389
spin_lock_irqsave(&zone->wp_lock, flags);
390+
zloop_lru_remove_open_zone(zlo, zone);
267391
if (zone->wp == zone->start)
268392
zone->cond = BLK_ZONE_COND_EMPTY;
269393
else
@@ -305,6 +429,7 @@ static int zloop_reset_zone(struct zloop_device *zlo, unsigned int zone_no)
305429
}
306430

307431
spin_lock_irqsave(&zone->wp_lock, flags);
432+
zloop_lru_remove_open_zone(zlo, zone);
308433
zone->cond = BLK_ZONE_COND_EMPTY;
309434
zone->wp = zone->start;
310435
clear_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags);
@@ -352,6 +477,7 @@ static int zloop_finish_zone(struct zloop_device *zlo, unsigned int zone_no)
352477
}
353478

354479
spin_lock_irqsave(&zone->wp_lock, flags);
480+
zloop_lru_remove_open_zone(zlo, zone);
355481
zone->cond = BLK_ZONE_COND_FULL;
356482
zone->wp = ULLONG_MAX;
357483
clear_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags);
@@ -478,9 +604,10 @@ static int zloop_seq_write_prep(struct zloop_cmd *cmd)
478604
}
479605

480606
/* Implicitly open the target zone. */
481-
if (zone->cond == BLK_ZONE_COND_CLOSED ||
482-
zone->cond == BLK_ZONE_COND_EMPTY)
483-
zone->cond = BLK_ZONE_COND_IMP_OPEN;
607+
if (!zloop_do_open_zone(zlo, zone, false)) {
608+
ret = -EIO;
609+
goto out_unlock;
610+
}
484611

485612
/*
486613
* Advance the write pointer, unless ordered zone append is in use. If
@@ -490,6 +617,7 @@ static int zloop_seq_write_prep(struct zloop_cmd *cmd)
490617
if (!is_append || !zlo->ordered_zone_append) {
491618
zone->wp += nr_sectors;
492619
if (zone->wp == zone_end) {
620+
zloop_lru_remove_open_zone(zlo, zone);
493621
zone->cond = BLK_ZONE_COND_FULL;
494622
zone->wp = ULLONG_MAX;
495623
}
@@ -746,6 +874,7 @@ static bool zloop_set_zone_append_sector(struct request *rq)
746874
rq->__sector = zone->wp;
747875
zone->wp += blk_rq_sectors(rq);
748876
if (zone->wp >= zone_end) {
877+
zloop_lru_remove_open_zone(zlo, zone);
749878
zone->cond = BLK_ZONE_COND_FULL;
750879
zone->wp = ULLONG_MAX;
751880
}
@@ -943,6 +1072,7 @@ static int zloop_init_zone(struct zloop_device *zlo, struct zloop_options *opts,
9431072
int ret;
9441073

9451074
mutex_init(&zone->lock);
1075+
INIT_LIST_HEAD(&zone->open_zone_entry);
9461076
spin_lock_init(&zone->wp_lock);
9471077
zone->start = (sector_t)zone_no << zlo->zone_shift;
9481078

@@ -1063,12 +1193,20 @@ static int zloop_ctl_add(struct zloop_options *opts)
10631193
goto out;
10641194
}
10651195

1196+
if (opts->max_open_zones > nr_zones - opts->nr_conv_zones) {
1197+
pr_err("Invalid maximum number of open zones %u\n",
1198+
opts->max_open_zones);
1199+
goto out;
1200+
}
1201+
10661202
zlo = kvzalloc_flex(*zlo, zones, nr_zones);
10671203
if (!zlo) {
10681204
ret = -ENOMEM;
10691205
goto out;
10701206
}
10711207
WRITE_ONCE(zlo->state, Zlo_creating);
1208+
spin_lock_init(&zlo->open_zones_lock);
1209+
INIT_LIST_HEAD(&zlo->open_zones_lru_list);
10721210

10731211
ret = mutex_lock_killable(&zloop_ctl_mutex);
10741212
if (ret)
@@ -1096,6 +1234,7 @@ static int zloop_ctl_add(struct zloop_options *opts)
10961234
zlo->zone_capacity = zlo->zone_size;
10971235
zlo->nr_zones = nr_zones;
10981236
zlo->nr_conv_zones = opts->nr_conv_zones;
1237+
zlo->max_open_zones = opts->max_open_zones;
10991238
zlo->buffered_io = opts->buffered_io;
11001239
zlo->zone_append = opts->zone_append;
11011240
if (zlo->zone_append)
@@ -1143,6 +1282,7 @@ static int zloop_ctl_add(struct zloop_options *opts)
11431282
lim.logical_block_size = zlo->block_size;
11441283
if (zlo->zone_append)
11451284
lim.max_hw_zone_append_sectors = lim.max_hw_sectors;
1285+
lim.max_open_zones = zlo->max_open_zones;
11461286

11471287
zlo->tag_set.ops = &zloop_mq_ops;
11481288
zlo->tag_set.nr_hw_queues = opts->nr_queues;
@@ -1326,6 +1466,7 @@ static int zloop_parse_options(struct zloop_options *opts, const char *buf)
13261466
opts->capacity = ZLOOP_DEF_ZONE_SIZE * ZLOOP_DEF_NR_ZONES;
13271467
opts->zone_size = ZLOOP_DEF_ZONE_SIZE;
13281468
opts->nr_conv_zones = ZLOOP_DEF_NR_CONV_ZONES;
1469+
opts->max_open_zones = ZLOOP_DEF_MAX_OPEN_ZONES;
13291470
opts->nr_queues = ZLOOP_DEF_NR_QUEUES;
13301471
opts->queue_depth = ZLOOP_DEF_QUEUE_DEPTH;
13311472
opts->buffered_io = ZLOOP_DEF_BUFFERED_IO;
@@ -1404,6 +1545,13 @@ static int zloop_parse_options(struct zloop_options *opts, const char *buf)
14041545
}
14051546
opts->nr_conv_zones = token;
14061547
break;
1548+
case ZLOOP_OPT_MAX_OPEN_ZONES:
1549+
if (match_uint(args, &token)) {
1550+
ret = -EINVAL;
1551+
goto out;
1552+
}
1553+
opts->max_open_zones = token;
1554+
break;
14071555
case ZLOOP_OPT_BASE_DIR:
14081556
p = match_strdup(args);
14091557
if (!p) {

0 commit comments

Comments
 (0)