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 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 */ 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 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 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 */ 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 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 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 291eb0570c7SDamien Le Moal static int zloop_update_seq_zone(struct zloop_device *zlo, unsigned int zone_no) 292eb0570c7SDamien Le Moal { 293eb0570c7SDamien Le Moal struct zloop_zone *zone = &zlo->zones[zone_no]; 294eb0570c7SDamien Le Moal struct kstat stat; 295eb0570c7SDamien Le Moal sector_t file_sectors; 296fcc6eaa3SDamien Le Moal unsigned long flags; 297eb0570c7SDamien Le Moal int ret; 298eb0570c7SDamien Le Moal 299eb0570c7SDamien Le Moal lockdep_assert_held(&zone->lock); 300eb0570c7SDamien Le Moal 301eb0570c7SDamien Le Moal ret = vfs_getattr(&zone->file->f_path, &stat, STATX_SIZE, 0); 302eb0570c7SDamien Le Moal if (ret < 0) { 303eb0570c7SDamien Le Moal pr_err("Failed to get zone %u file stat (err=%d)\n", 304eb0570c7SDamien Le Moal zone_no, ret); 305eb0570c7SDamien Le Moal set_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags); 306eb0570c7SDamien Le Moal return ret; 307eb0570c7SDamien Le Moal } 308eb0570c7SDamien Le Moal 309eb0570c7SDamien Le Moal file_sectors = stat.size >> SECTOR_SHIFT; 310eb0570c7SDamien Le Moal if (file_sectors > zlo->zone_capacity) { 311eb0570c7SDamien Le Moal pr_err("Zone %u file too large (%llu sectors > %llu)\n", 312eb0570c7SDamien Le Moal zone_no, file_sectors, zlo->zone_capacity); 313eb0570c7SDamien Le Moal return -EINVAL; 314eb0570c7SDamien Le Moal } 315eb0570c7SDamien Le Moal 316eb0570c7SDamien Le Moal if (file_sectors & ((zlo->block_size >> SECTOR_SHIFT) - 1)) { 317eb0570c7SDamien Le Moal pr_err("Zone %u file size not aligned to block size %u\n", 318eb0570c7SDamien Le Moal zone_no, zlo->block_size); 319eb0570c7SDamien Le Moal return -EINVAL; 320eb0570c7SDamien Le Moal } 321eb0570c7SDamien Le Moal 322fcc6eaa3SDamien Le Moal spin_lock_irqsave(&zone->wp_lock, flags); 323eb0570c7SDamien Le Moal if (!file_sectors) { 324b2a78fecSDamien Le Moal zloop_lru_remove_open_zone(zlo, zone); 325eb0570c7SDamien Le Moal zone->cond = BLK_ZONE_COND_EMPTY; 326eb0570c7SDamien Le Moal zone->wp = zone->start; 327eb0570c7SDamien Le Moal } else if (file_sectors == zlo->zone_capacity) { 328b2a78fecSDamien Le Moal zloop_lru_remove_open_zone(zlo, zone); 329eb0570c7SDamien Le Moal zone->cond = BLK_ZONE_COND_FULL; 330866d6574SDamien Le Moal zone->wp = ULLONG_MAX; 331eb0570c7SDamien Le Moal } else { 332b2a78fecSDamien Le Moal if (zone->cond != BLK_ZONE_COND_IMP_OPEN && 333b2a78fecSDamien Le Moal zone->cond != BLK_ZONE_COND_EXP_OPEN) 334eb0570c7SDamien Le Moal zone->cond = BLK_ZONE_COND_CLOSED; 335eb0570c7SDamien Le Moal zone->wp = zone->start + file_sectors; 336eb0570c7SDamien Le Moal } 337fcc6eaa3SDamien Le Moal spin_unlock_irqrestore(&zone->wp_lock, flags); 338eb0570c7SDamien Le Moal 339eb0570c7SDamien Le Moal return 0; 340eb0570c7SDamien Le Moal } 341eb0570c7SDamien Le Moal 342eb0570c7SDamien Le Moal static int zloop_open_zone(struct zloop_device *zlo, unsigned int zone_no) 343eb0570c7SDamien Le Moal { 344eb0570c7SDamien Le Moal struct zloop_zone *zone = &zlo->zones[zone_no]; 345eb0570c7SDamien Le Moal int ret = 0; 346eb0570c7SDamien Le Moal 347eb0570c7SDamien Le Moal if (test_bit(ZLOOP_ZONE_CONV, &zone->flags)) 348eb0570c7SDamien Le Moal return -EIO; 349eb0570c7SDamien Le Moal 350eb0570c7SDamien Le Moal mutex_lock(&zone->lock); 351eb0570c7SDamien Le Moal 352eb0570c7SDamien Le Moal if (test_and_clear_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags)) { 353eb0570c7SDamien Le Moal ret = zloop_update_seq_zone(zlo, zone_no); 354eb0570c7SDamien Le Moal if (ret) 355eb0570c7SDamien Le Moal goto unlock; 356eb0570c7SDamien Le Moal } 357eb0570c7SDamien Le Moal 358b2a78fecSDamien Le Moal if (!zloop_do_open_zone(zlo, zone, true)) 359eb0570c7SDamien Le Moal ret = -EIO; 360eb0570c7SDamien Le Moal 361eb0570c7SDamien Le Moal unlock: 362eb0570c7SDamien Le Moal mutex_unlock(&zone->lock); 363eb0570c7SDamien Le Moal 364eb0570c7SDamien Le Moal return ret; 365eb0570c7SDamien Le Moal } 366eb0570c7SDamien Le Moal 367eb0570c7SDamien Le Moal static int zloop_close_zone(struct zloop_device *zlo, unsigned int zone_no) 368eb0570c7SDamien Le Moal { 369eb0570c7SDamien Le Moal struct zloop_zone *zone = &zlo->zones[zone_no]; 370fcc6eaa3SDamien Le Moal unsigned long flags; 371eb0570c7SDamien Le Moal int ret = 0; 372eb0570c7SDamien Le Moal 373eb0570c7SDamien Le Moal if (test_bit(ZLOOP_ZONE_CONV, &zone->flags)) 374eb0570c7SDamien Le Moal return -EIO; 375eb0570c7SDamien Le Moal 376eb0570c7SDamien Le Moal mutex_lock(&zone->lock); 377eb0570c7SDamien Le Moal 378eb0570c7SDamien Le Moal if (test_and_clear_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags)) { 379eb0570c7SDamien Le Moal ret = zloop_update_seq_zone(zlo, zone_no); 380eb0570c7SDamien Le Moal if (ret) 381eb0570c7SDamien Le Moal goto unlock; 382eb0570c7SDamien Le Moal } 383eb0570c7SDamien Le Moal 384eb0570c7SDamien Le Moal switch (zone->cond) { 385eb0570c7SDamien Le Moal case BLK_ZONE_COND_CLOSED: 386eb0570c7SDamien Le Moal break; 387eb0570c7SDamien Le Moal case BLK_ZONE_COND_IMP_OPEN: 388eb0570c7SDamien Le Moal case BLK_ZONE_COND_EXP_OPEN: 389fcc6eaa3SDamien Le Moal spin_lock_irqsave(&zone->wp_lock, flags); 390b2a78fecSDamien Le Moal zloop_lru_remove_open_zone(zlo, zone); 391eb0570c7SDamien Le Moal if (zone->wp == zone->start) 392eb0570c7SDamien Le Moal zone->cond = BLK_ZONE_COND_EMPTY; 393eb0570c7SDamien Le Moal else 394eb0570c7SDamien Le Moal zone->cond = BLK_ZONE_COND_CLOSED; 395fcc6eaa3SDamien Le Moal spin_unlock_irqrestore(&zone->wp_lock, flags); 396eb0570c7SDamien Le Moal break; 397eb0570c7SDamien Le Moal case BLK_ZONE_COND_EMPTY: 398eb0570c7SDamien Le Moal case BLK_ZONE_COND_FULL: 399eb0570c7SDamien Le Moal default: 400eb0570c7SDamien Le Moal ret = -EIO; 401eb0570c7SDamien Le Moal break; 402eb0570c7SDamien Le Moal } 403eb0570c7SDamien Le Moal 404eb0570c7SDamien Le Moal unlock: 405eb0570c7SDamien Le Moal mutex_unlock(&zone->lock); 406eb0570c7SDamien Le Moal 407eb0570c7SDamien Le Moal return ret; 408eb0570c7SDamien Le Moal } 409eb0570c7SDamien Le Moal 410eb0570c7SDamien Le Moal static int zloop_reset_zone(struct zloop_device *zlo, unsigned int zone_no) 411eb0570c7SDamien Le Moal { 412eb0570c7SDamien Le Moal struct zloop_zone *zone = &zlo->zones[zone_no]; 413fcc6eaa3SDamien Le Moal unsigned long flags; 414eb0570c7SDamien Le Moal int ret = 0; 415eb0570c7SDamien Le Moal 416eb0570c7SDamien Le Moal if (test_bit(ZLOOP_ZONE_CONV, &zone->flags)) 417eb0570c7SDamien Le Moal return -EIO; 418eb0570c7SDamien Le Moal 419eb0570c7SDamien Le Moal mutex_lock(&zone->lock); 420eb0570c7SDamien Le Moal 421eb0570c7SDamien Le Moal if (!test_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags) && 422eb0570c7SDamien Le Moal zone->cond == BLK_ZONE_COND_EMPTY) 423eb0570c7SDamien Le Moal goto unlock; 424eb0570c7SDamien Le Moal 425eb0570c7SDamien Le Moal if (vfs_truncate(&zone->file->f_path, 0)) { 426eb0570c7SDamien Le Moal set_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags); 427eb0570c7SDamien Le Moal ret = -EIO; 428eb0570c7SDamien Le Moal goto unlock; 429eb0570c7SDamien Le Moal } 430eb0570c7SDamien Le Moal 431fcc6eaa3SDamien Le Moal spin_lock_irqsave(&zone->wp_lock, flags); 432b2a78fecSDamien Le Moal zloop_lru_remove_open_zone(zlo, zone); 433eb0570c7SDamien Le Moal zone->cond = BLK_ZONE_COND_EMPTY; 434eb0570c7SDamien Le Moal zone->wp = zone->start; 435eb0570c7SDamien Le Moal clear_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags); 436fcc6eaa3SDamien Le Moal spin_unlock_irqrestore(&zone->wp_lock, flags); 437eb0570c7SDamien Le Moal 438eb0570c7SDamien Le Moal unlock: 439eb0570c7SDamien Le Moal mutex_unlock(&zone->lock); 440eb0570c7SDamien Le Moal 441eb0570c7SDamien Le Moal return ret; 442eb0570c7SDamien Le Moal } 443eb0570c7SDamien Le Moal 444eb0570c7SDamien Le Moal static int zloop_reset_all_zones(struct zloop_device *zlo) 445eb0570c7SDamien Le Moal { 446eb0570c7SDamien Le Moal unsigned int i; 447eb0570c7SDamien Le Moal int ret; 448eb0570c7SDamien Le Moal 449eb0570c7SDamien Le Moal for (i = zlo->nr_conv_zones; i < zlo->nr_zones; i++) { 450eb0570c7SDamien Le Moal ret = zloop_reset_zone(zlo, i); 451eb0570c7SDamien Le Moal if (ret) 452eb0570c7SDamien Le Moal return ret; 453eb0570c7SDamien Le Moal } 454eb0570c7SDamien Le Moal 455eb0570c7SDamien Le Moal return 0; 456eb0570c7SDamien Le Moal } 457eb0570c7SDamien Le Moal 458eb0570c7SDamien Le Moal static int zloop_finish_zone(struct zloop_device *zlo, unsigned int zone_no) 459eb0570c7SDamien Le Moal { 460eb0570c7SDamien Le Moal struct zloop_zone *zone = &zlo->zones[zone_no]; 461fcc6eaa3SDamien Le Moal unsigned long flags; 462eb0570c7SDamien Le Moal int ret = 0; 463eb0570c7SDamien Le Moal 464eb0570c7SDamien Le Moal if (test_bit(ZLOOP_ZONE_CONV, &zone->flags)) 465eb0570c7SDamien Le Moal return -EIO; 466eb0570c7SDamien Le Moal 467eb0570c7SDamien Le Moal mutex_lock(&zone->lock); 468eb0570c7SDamien Le Moal 469eb0570c7SDamien Le Moal if (!test_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags) && 470eb0570c7SDamien Le Moal zone->cond == BLK_ZONE_COND_FULL) 471eb0570c7SDamien Le Moal goto unlock; 472eb0570c7SDamien Le Moal 473eb0570c7SDamien Le Moal if (vfs_truncate(&zone->file->f_path, zlo->zone_size << SECTOR_SHIFT)) { 474eb0570c7SDamien Le Moal set_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags); 475eb0570c7SDamien Le Moal ret = -EIO; 476eb0570c7SDamien Le Moal goto unlock; 477eb0570c7SDamien Le Moal } 478eb0570c7SDamien Le Moal 479fcc6eaa3SDamien Le Moal spin_lock_irqsave(&zone->wp_lock, flags); 480b2a78fecSDamien Le Moal zloop_lru_remove_open_zone(zlo, zone); 481eb0570c7SDamien Le Moal zone->cond = BLK_ZONE_COND_FULL; 482866d6574SDamien Le Moal zone->wp = ULLONG_MAX; 483eb0570c7SDamien Le Moal clear_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags); 484fcc6eaa3SDamien Le Moal spin_unlock_irqrestore(&zone->wp_lock, flags); 485eb0570c7SDamien Le Moal 486eb0570c7SDamien Le Moal unlock: 487eb0570c7SDamien Le Moal mutex_unlock(&zone->lock); 488eb0570c7SDamien Le Moal 489eb0570c7SDamien Le Moal return ret; 490eb0570c7SDamien Le Moal } 491eb0570c7SDamien Le Moal 492eb0570c7SDamien Le Moal static void zloop_put_cmd(struct zloop_cmd *cmd) 493eb0570c7SDamien Le Moal { 494eb0570c7SDamien Le Moal struct request *rq = blk_mq_rq_from_pdu(cmd); 495eb0570c7SDamien Le Moal 496eb0570c7SDamien Le Moal if (!atomic_dec_and_test(&cmd->ref)) 497eb0570c7SDamien Le Moal return; 498eb0570c7SDamien Le Moal kfree(cmd->bvec); 499eb0570c7SDamien Le Moal cmd->bvec = NULL; 500eb0570c7SDamien Le Moal if (likely(!blk_should_fake_timeout(rq->q))) 501eb0570c7SDamien Le Moal blk_mq_complete_request(rq); 502eb0570c7SDamien Le Moal } 503eb0570c7SDamien Le Moal 504eb0570c7SDamien Le Moal static void zloop_rw_complete(struct kiocb *iocb, long ret) 505eb0570c7SDamien Le Moal { 506eb0570c7SDamien Le Moal struct zloop_cmd *cmd = container_of(iocb, struct zloop_cmd, iocb); 507eb0570c7SDamien Le Moal 508eb0570c7SDamien Le Moal cmd->ret = ret; 509eb0570c7SDamien Le Moal zloop_put_cmd(cmd); 510eb0570c7SDamien Le Moal } 511eb0570c7SDamien Le Moal 512eff8d165SChristoph Hellwig static int zloop_do_rw(struct zloop_cmd *cmd) 513eb0570c7SDamien Le Moal { 514eb0570c7SDamien Le Moal struct request *rq = blk_mq_rq_from_pdu(cmd); 515eff8d165SChristoph Hellwig int rw = req_op(rq) == REQ_OP_READ ? ITER_DEST : ITER_SOURCE; 516eff8d165SChristoph Hellwig unsigned int nr_bvec = blk_rq_nr_bvec(rq); 517eb0570c7SDamien Le Moal struct zloop_device *zlo = rq->q->queuedata; 518eff8d165SChristoph Hellwig struct zloop_zone *zone = &zlo->zones[rq_zone_no(rq)]; 519eb0570c7SDamien Le Moal struct req_iterator rq_iter; 520eb0570c7SDamien Le Moal struct iov_iter iter; 521eb0570c7SDamien Le Moal 522eb0570c7SDamien Le Moal if (rq->bio != rq->biotail) { 523eff8d165SChristoph Hellwig struct bio_vec tmp, *bvec; 524eb0570c7SDamien Le Moal 52569050f8dSKees Cook cmd->bvec = kmalloc_objs(*cmd->bvec, nr_bvec, GFP_NOIO); 526eff8d165SChristoph Hellwig if (!cmd->bvec) 527eff8d165SChristoph Hellwig return -EIO; 528eb0570c7SDamien Le Moal 529eb0570c7SDamien Le Moal /* 530eb0570c7SDamien Le Moal * The bios of the request may be started from the middle of 531eb0570c7SDamien Le Moal * the 'bvec' because of bio splitting, so we can't directly 532eb0570c7SDamien Le Moal * copy bio->bi_iov_vec to new bvec. The rq_for_each_bvec 533eb0570c7SDamien Le Moal * API will take care of all details for us. 534eb0570c7SDamien Le Moal */ 535eb0570c7SDamien Le Moal bvec = cmd->bvec; 536eb0570c7SDamien Le Moal rq_for_each_bvec(tmp, rq, rq_iter) { 537eb0570c7SDamien Le Moal *bvec = tmp; 538eb0570c7SDamien Le Moal bvec++; 539eb0570c7SDamien Le Moal } 540eb0570c7SDamien Le Moal iov_iter_bvec(&iter, rw, cmd->bvec, nr_bvec, blk_rq_bytes(rq)); 541eb0570c7SDamien Le Moal } else { 542eb0570c7SDamien Le Moal /* 543eb0570c7SDamien Le Moal * Same here, this bio may be started from the middle of the 544eb0570c7SDamien Le Moal * 'bvec' because of bio splitting, so offset from the bvec 545eb0570c7SDamien Le Moal * must be passed to iov iterator 546eb0570c7SDamien Le Moal */ 547eb0570c7SDamien Le Moal iov_iter_bvec(&iter, rw, 548eb0570c7SDamien Le Moal __bvec_iter_bvec(rq->bio->bi_io_vec, rq->bio->bi_iter), 549eb0570c7SDamien Le Moal nr_bvec, blk_rq_bytes(rq)); 550eb0570c7SDamien Le Moal iter.iov_offset = rq->bio->bi_iter.bi_bvec_done; 551eb0570c7SDamien Le Moal } 552eb0570c7SDamien Le Moal 553eff8d165SChristoph Hellwig cmd->iocb.ki_pos = (cmd->sector - zone->start) << SECTOR_SHIFT; 554eb0570c7SDamien Le Moal cmd->iocb.ki_filp = zone->file; 555eb0570c7SDamien Le Moal cmd->iocb.ki_complete = zloop_rw_complete; 556eb0570c7SDamien Le Moal if (!zlo->buffered_io) 557eb0570c7SDamien Le Moal cmd->iocb.ki_flags = IOCB_DIRECT; 558eb0570c7SDamien Le Moal cmd->iocb.ki_ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_NONE, 0); 559eb0570c7SDamien Le Moal 560eb0570c7SDamien Le Moal if (rw == ITER_SOURCE) 561eff8d165SChristoph Hellwig return zone->file->f_op->write_iter(&cmd->iocb, &iter); 562eff8d165SChristoph Hellwig return zone->file->f_op->read_iter(&cmd->iocb, &iter); 563eff8d165SChristoph Hellwig } 564eff8d165SChristoph Hellwig 565eff8d165SChristoph Hellwig static int zloop_seq_write_prep(struct zloop_cmd *cmd) 566eff8d165SChristoph Hellwig { 567eff8d165SChristoph Hellwig struct request *rq = blk_mq_rq_from_pdu(cmd); 568eff8d165SChristoph Hellwig struct zloop_device *zlo = rq->q->queuedata; 569eff8d165SChristoph Hellwig unsigned int zone_no = rq_zone_no(rq); 570eff8d165SChristoph Hellwig sector_t nr_sectors = blk_rq_sectors(rq); 571eff8d165SChristoph Hellwig bool is_append = req_op(rq) == REQ_OP_ZONE_APPEND; 572eff8d165SChristoph Hellwig struct zloop_zone *zone = &zlo->zones[zone_no]; 573eff8d165SChristoph Hellwig sector_t zone_end = zone->start + zlo->zone_capacity; 574eff8d165SChristoph Hellwig unsigned long flags; 575eff8d165SChristoph Hellwig int ret = 0; 576eff8d165SChristoph Hellwig 577eff8d165SChristoph Hellwig spin_lock_irqsave(&zone->wp_lock, flags); 578eff8d165SChristoph Hellwig 579eff8d165SChristoph Hellwig /* 580eff8d165SChristoph Hellwig * Zone append operations always go at the current write pointer, but 581eff8d165SChristoph Hellwig * regular write operations must already be aligned to the write pointer 582eff8d165SChristoph Hellwig * when submitted. 583eff8d165SChristoph Hellwig */ 584eff8d165SChristoph Hellwig if (is_append) { 585eff8d165SChristoph Hellwig /* 586eff8d165SChristoph Hellwig * If ordered zone append is in use, we already checked and set 587eff8d165SChristoph Hellwig * the target sector in zloop_queue_rq(). 588eff8d165SChristoph Hellwig */ 589eff8d165SChristoph Hellwig if (!zlo->ordered_zone_append) { 590eff8d165SChristoph Hellwig if (zone->cond == BLK_ZONE_COND_FULL || 591eff8d165SChristoph Hellwig zone->wp + nr_sectors > zone_end) { 592eff8d165SChristoph Hellwig ret = -EIO; 593eff8d165SChristoph Hellwig goto out_unlock; 594eff8d165SChristoph Hellwig } 595eff8d165SChristoph Hellwig cmd->sector = zone->wp; 596eff8d165SChristoph Hellwig } 597eff8d165SChristoph Hellwig } else { 598eff8d165SChristoph Hellwig if (cmd->sector != zone->wp) { 599eff8d165SChristoph Hellwig pr_err("Zone %u: unaligned write: sect %llu, wp %llu\n", 600eff8d165SChristoph Hellwig zone_no, cmd->sector, zone->wp); 601eff8d165SChristoph Hellwig ret = -EIO; 602eff8d165SChristoph Hellwig goto out_unlock; 603eff8d165SChristoph Hellwig } 604eff8d165SChristoph Hellwig } 605eff8d165SChristoph Hellwig 606eff8d165SChristoph Hellwig /* Implicitly open the target zone. */ 607b2a78fecSDamien Le Moal if (!zloop_do_open_zone(zlo, zone, false)) { 608b2a78fecSDamien Le Moal ret = -EIO; 609b2a78fecSDamien Le Moal goto out_unlock; 610b2a78fecSDamien Le Moal } 611eff8d165SChristoph Hellwig 612eff8d165SChristoph Hellwig /* 613eff8d165SChristoph Hellwig * Advance the write pointer, unless ordered zone append is in use. If 614eff8d165SChristoph Hellwig * the write fails, the write pointer position will be corrected when 615eff8d165SChristoph Hellwig * the next I/O starts execution. 616eff8d165SChristoph Hellwig */ 617eff8d165SChristoph Hellwig if (!is_append || !zlo->ordered_zone_append) { 618eff8d165SChristoph Hellwig zone->wp += nr_sectors; 619eff8d165SChristoph Hellwig if (zone->wp == zone_end) { 620b2a78fecSDamien Le Moal zloop_lru_remove_open_zone(zlo, zone); 621eff8d165SChristoph Hellwig zone->cond = BLK_ZONE_COND_FULL; 622eff8d165SChristoph Hellwig zone->wp = ULLONG_MAX; 623eff8d165SChristoph Hellwig } 624eff8d165SChristoph Hellwig } 625eff8d165SChristoph Hellwig out_unlock: 626eff8d165SChristoph Hellwig spin_unlock_irqrestore(&zone->wp_lock, flags); 627eff8d165SChristoph Hellwig return ret; 628eff8d165SChristoph Hellwig } 629eff8d165SChristoph Hellwig 630eff8d165SChristoph Hellwig static void zloop_rw(struct zloop_cmd *cmd) 631eff8d165SChristoph Hellwig { 632eff8d165SChristoph Hellwig struct request *rq = blk_mq_rq_from_pdu(cmd); 633eff8d165SChristoph Hellwig struct zloop_device *zlo = rq->q->queuedata; 634eff8d165SChristoph Hellwig unsigned int zone_no = rq_zone_no(rq); 635eff8d165SChristoph Hellwig sector_t nr_sectors = blk_rq_sectors(rq); 636eff8d165SChristoph Hellwig bool is_append = req_op(rq) == REQ_OP_ZONE_APPEND; 637eff8d165SChristoph Hellwig bool is_write = req_op(rq) == REQ_OP_WRITE || is_append; 638eff8d165SChristoph Hellwig struct zloop_zone *zone; 639eff8d165SChristoph Hellwig int ret = -EIO; 640eff8d165SChristoph Hellwig 641eff8d165SChristoph Hellwig atomic_set(&cmd->ref, 2); 642eff8d165SChristoph Hellwig cmd->sector = blk_rq_pos(rq); 643eff8d165SChristoph Hellwig cmd->nr_sectors = nr_sectors; 644eff8d165SChristoph Hellwig cmd->ret = 0; 645eff8d165SChristoph Hellwig 646eff8d165SChristoph Hellwig if (WARN_ON_ONCE(is_append && !zlo->zone_append)) 647eff8d165SChristoph Hellwig goto out; 648eff8d165SChristoph Hellwig 649eff8d165SChristoph Hellwig /* We should never get an I/O beyond the device capacity. */ 650eff8d165SChristoph Hellwig if (WARN_ON_ONCE(zone_no >= zlo->nr_zones)) 651eff8d165SChristoph Hellwig goto out; 652eff8d165SChristoph Hellwig 653eff8d165SChristoph Hellwig zone = &zlo->zones[zone_no]; 654eff8d165SChristoph Hellwig 655eff8d165SChristoph Hellwig /* 656eff8d165SChristoph Hellwig * The block layer should never send requests that are not fully 657eff8d165SChristoph Hellwig * contained within the zone. 658eff8d165SChristoph Hellwig */ 659eff8d165SChristoph Hellwig if (WARN_ON_ONCE(cmd->sector + nr_sectors > 660eff8d165SChristoph Hellwig zone->start + zlo->zone_size)) 661eff8d165SChristoph Hellwig goto out; 662eff8d165SChristoph Hellwig 663eff8d165SChristoph Hellwig if (test_and_clear_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags)) { 664eff8d165SChristoph Hellwig mutex_lock(&zone->lock); 665eff8d165SChristoph Hellwig ret = zloop_update_seq_zone(zlo, zone_no); 666eb0570c7SDamien Le Moal mutex_unlock(&zone->lock); 667eff8d165SChristoph Hellwig if (ret) 668eff8d165SChristoph Hellwig goto out; 669eff8d165SChristoph Hellwig } 670eff8d165SChristoph Hellwig 671eff8d165SChristoph Hellwig if (!test_bit(ZLOOP_ZONE_CONV, &zone->flags) && is_write) { 672eff8d165SChristoph Hellwig mutex_lock(&zone->lock); 673eff8d165SChristoph Hellwig ret = zloop_seq_write_prep(cmd); 674eff8d165SChristoph Hellwig if (!ret) 675eff8d165SChristoph Hellwig ret = zloop_do_rw(cmd); 676eff8d165SChristoph Hellwig mutex_unlock(&zone->lock); 677eff8d165SChristoph Hellwig } else { 678eff8d165SChristoph Hellwig ret = zloop_do_rw(cmd); 679eff8d165SChristoph Hellwig } 680eb0570c7SDamien Le Moal out: 681eb0570c7SDamien Le Moal if (ret != -EIOCBQUEUED) 682eb0570c7SDamien Le Moal zloop_rw_complete(&cmd->iocb, ret); 683eb0570c7SDamien Le Moal zloop_put_cmd(cmd); 684eb0570c7SDamien Le Moal } 685eb0570c7SDamien Le Moal 686829def1eSChristoph Hellwig static inline bool zloop_zone_is_active(struct zloop_zone *zone) 687829def1eSChristoph Hellwig { 688829def1eSChristoph Hellwig switch (zone->cond) { 689829def1eSChristoph Hellwig case BLK_ZONE_COND_EXP_OPEN: 690829def1eSChristoph Hellwig case BLK_ZONE_COND_IMP_OPEN: 691829def1eSChristoph Hellwig case BLK_ZONE_COND_CLOSED: 692829def1eSChristoph Hellwig return true; 693829def1eSChristoph Hellwig default: 694829def1eSChristoph Hellwig return false; 695829def1eSChristoph Hellwig } 696829def1eSChristoph Hellwig } 697829def1eSChristoph Hellwig 698829def1eSChristoph Hellwig static int zloop_record_safe_wps(struct zloop_device *zlo) 699829def1eSChristoph Hellwig { 700829def1eSChristoph Hellwig unsigned int i; 701829def1eSChristoph Hellwig int ret; 702829def1eSChristoph Hellwig 703829def1eSChristoph Hellwig for (i = 0; i < zlo->nr_zones; i++) { 704829def1eSChristoph Hellwig struct zloop_zone *zone = &zlo->zones[i]; 705829def1eSChristoph Hellwig struct file *file = zone->file; 706829def1eSChristoph Hellwig 707829def1eSChristoph Hellwig if (!zloop_zone_is_active(zone)) 708829def1eSChristoph Hellwig continue; 709829def1eSChristoph Hellwig ret = vfs_setxattr(file_mnt_idmap(file), file_dentry(file), 710829def1eSChristoph Hellwig "user.zloop.wp", &zone->wp, sizeof(zone->wp), 0); 711829def1eSChristoph Hellwig if (ret) { 712829def1eSChristoph Hellwig pr_err("%pg: failed to record write pointer (%d)\n", 713829def1eSChristoph Hellwig zlo->disk->part0, ret); 714829def1eSChristoph Hellwig return ret; 715829def1eSChristoph Hellwig } 716829def1eSChristoph Hellwig } 717829def1eSChristoph Hellwig 718829def1eSChristoph Hellwig return 0; 719829def1eSChristoph Hellwig } 720829def1eSChristoph Hellwig 7216acf7860SChristoph Hellwig /* 7226acf7860SChristoph Hellwig * Sync the entire FS containing the zone files instead of walking all files. 7236acf7860SChristoph Hellwig */ 7246acf7860SChristoph Hellwig static int zloop_flush(struct zloop_device *zlo) 7256acf7860SChristoph Hellwig { 7266acf7860SChristoph Hellwig struct super_block *sb = file_inode(zlo->data_dir)->i_sb; 7276acf7860SChristoph Hellwig int ret; 7286acf7860SChristoph Hellwig 729829def1eSChristoph Hellwig if (zlo->discard_write_cache) { 730829def1eSChristoph Hellwig ret = zloop_record_safe_wps(zlo); 731829def1eSChristoph Hellwig if (ret) 732829def1eSChristoph Hellwig return ret; 733829def1eSChristoph Hellwig } 734829def1eSChristoph Hellwig 7356acf7860SChristoph Hellwig down_read(&sb->s_umount); 7366acf7860SChristoph Hellwig ret = sync_filesystem(sb); 7376acf7860SChristoph Hellwig up_read(&sb->s_umount); 7386acf7860SChristoph Hellwig 7396acf7860SChristoph Hellwig return ret; 7406acf7860SChristoph Hellwig } 7416acf7860SChristoph Hellwig 742eb0570c7SDamien Le Moal static void zloop_handle_cmd(struct zloop_cmd *cmd) 743eb0570c7SDamien Le Moal { 744eb0570c7SDamien Le Moal struct request *rq = blk_mq_rq_from_pdu(cmd); 745eb0570c7SDamien Le Moal struct zloop_device *zlo = rq->q->queuedata; 746eb0570c7SDamien Le Moal 747e8f0abddSChaitanya Kulkarni /* We can block in this context, so ignore REQ_NOWAIT. */ 748e8f0abddSChaitanya Kulkarni if (rq->cmd_flags & REQ_NOWAIT) 749e8f0abddSChaitanya Kulkarni rq->cmd_flags &= ~REQ_NOWAIT; 750e8f0abddSChaitanya Kulkarni 751eb0570c7SDamien Le Moal switch (req_op(rq)) { 752eb0570c7SDamien Le Moal case REQ_OP_READ: 753eb0570c7SDamien Le Moal case REQ_OP_WRITE: 754eb0570c7SDamien Le Moal case REQ_OP_ZONE_APPEND: 755eb0570c7SDamien Le Moal /* 756eb0570c7SDamien Le Moal * zloop_rw() always executes asynchronously or completes 757eb0570c7SDamien Le Moal * directly. 758eb0570c7SDamien Le Moal */ 759eb0570c7SDamien Le Moal zloop_rw(cmd); 760eb0570c7SDamien Le Moal return; 761eb0570c7SDamien Le Moal case REQ_OP_FLUSH: 7626acf7860SChristoph Hellwig cmd->ret = zloop_flush(zlo); 763eb0570c7SDamien Le Moal break; 764eb0570c7SDamien Le Moal case REQ_OP_ZONE_RESET: 765eb0570c7SDamien Le Moal cmd->ret = zloop_reset_zone(zlo, rq_zone_no(rq)); 766eb0570c7SDamien Le Moal break; 767eb0570c7SDamien Le Moal case REQ_OP_ZONE_RESET_ALL: 768eb0570c7SDamien Le Moal cmd->ret = zloop_reset_all_zones(zlo); 769eb0570c7SDamien Le Moal break; 770eb0570c7SDamien Le Moal case REQ_OP_ZONE_FINISH: 771eb0570c7SDamien Le Moal cmd->ret = zloop_finish_zone(zlo, rq_zone_no(rq)); 772eb0570c7SDamien Le Moal break; 773eb0570c7SDamien Le Moal case REQ_OP_ZONE_OPEN: 774eb0570c7SDamien Le Moal cmd->ret = zloop_open_zone(zlo, rq_zone_no(rq)); 775eb0570c7SDamien Le Moal break; 776eb0570c7SDamien Le Moal case REQ_OP_ZONE_CLOSE: 777eb0570c7SDamien Le Moal cmd->ret = zloop_close_zone(zlo, rq_zone_no(rq)); 778eb0570c7SDamien Le Moal break; 779eb0570c7SDamien Le Moal default: 780eb0570c7SDamien Le Moal WARN_ON_ONCE(1); 781eb0570c7SDamien Le Moal pr_err("Unsupported operation %d\n", req_op(rq)); 782eb0570c7SDamien Le Moal cmd->ret = -EOPNOTSUPP; 783eb0570c7SDamien Le Moal break; 784eb0570c7SDamien Le Moal } 785eb0570c7SDamien Le Moal 786eb0570c7SDamien Le Moal blk_mq_complete_request(rq); 787eb0570c7SDamien Le Moal } 788eb0570c7SDamien Le Moal 789eb0570c7SDamien Le Moal static void zloop_cmd_workfn(struct work_struct *work) 790eb0570c7SDamien Le Moal { 791eb0570c7SDamien Le Moal struct zloop_cmd *cmd = container_of(work, struct zloop_cmd, work); 792eb0570c7SDamien Le Moal int orig_flags = current->flags; 793eb0570c7SDamien Le Moal 794eb0570c7SDamien Le Moal current->flags |= PF_LOCAL_THROTTLE | PF_MEMALLOC_NOIO; 795eb0570c7SDamien Le Moal zloop_handle_cmd(cmd); 796eb0570c7SDamien Le Moal current->flags = orig_flags; 797eb0570c7SDamien Le Moal } 798eb0570c7SDamien Le Moal 799eb0570c7SDamien Le Moal static void zloop_complete_rq(struct request *rq) 800eb0570c7SDamien Le Moal { 801eb0570c7SDamien Le Moal struct zloop_cmd *cmd = blk_mq_rq_to_pdu(rq); 802eb0570c7SDamien Le Moal struct zloop_device *zlo = rq->q->queuedata; 803eb0570c7SDamien Le Moal unsigned int zone_no = cmd->sector >> zlo->zone_shift; 804eb0570c7SDamien Le Moal struct zloop_zone *zone = &zlo->zones[zone_no]; 805eb0570c7SDamien Le Moal blk_status_t sts = BLK_STS_OK; 806eb0570c7SDamien Le Moal 807eb0570c7SDamien Le Moal switch (req_op(rq)) { 808eb0570c7SDamien Le Moal case REQ_OP_READ: 809eb0570c7SDamien Le Moal if (cmd->ret < 0) 810eb0570c7SDamien Le Moal pr_err("Zone %u: failed read sector %llu, %llu sectors\n", 811eb0570c7SDamien Le Moal zone_no, cmd->sector, cmd->nr_sectors); 812eb0570c7SDamien Le Moal 813eb0570c7SDamien Le Moal if (cmd->ret >= 0 && cmd->ret != blk_rq_bytes(rq)) { 814eb0570c7SDamien Le Moal /* short read */ 815eb0570c7SDamien Le Moal struct bio *bio; 816eb0570c7SDamien Le Moal 817eb0570c7SDamien Le Moal __rq_for_each_bio(bio, rq) 818eb0570c7SDamien Le Moal zero_fill_bio(bio); 819eb0570c7SDamien Le Moal } 820eb0570c7SDamien Le Moal break; 821eb0570c7SDamien Le Moal case REQ_OP_WRITE: 822eb0570c7SDamien Le Moal case REQ_OP_ZONE_APPEND: 823eb0570c7SDamien Le Moal if (cmd->ret < 0) 824eb0570c7SDamien Le Moal pr_err("Zone %u: failed %swrite sector %llu, %llu sectors\n", 825eb0570c7SDamien Le Moal zone_no, 826eb0570c7SDamien Le Moal req_op(rq) == REQ_OP_WRITE ? "" : "append ", 827eb0570c7SDamien Le Moal cmd->sector, cmd->nr_sectors); 828eb0570c7SDamien Le Moal 829eb0570c7SDamien Le Moal if (cmd->ret >= 0 && cmd->ret != blk_rq_bytes(rq)) { 830eb0570c7SDamien Le Moal pr_err("Zone %u: partial write %ld/%u B\n", 831eb0570c7SDamien Le Moal zone_no, cmd->ret, blk_rq_bytes(rq)); 832eb0570c7SDamien Le Moal cmd->ret = -EIO; 833eb0570c7SDamien Le Moal } 834eb0570c7SDamien Le Moal 835eb0570c7SDamien Le Moal if (cmd->ret < 0 && !test_bit(ZLOOP_ZONE_CONV, &zone->flags)) { 836eb0570c7SDamien Le Moal /* 837eb0570c7SDamien Le Moal * A write to a sequential zone file failed: mark the 838eb0570c7SDamien Le Moal * zone as having an error. This will be corrected and 839eb0570c7SDamien Le Moal * cleared when the next IO is submitted. 840eb0570c7SDamien Le Moal */ 841eb0570c7SDamien Le Moal set_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags); 842eb0570c7SDamien Le Moal break; 843eb0570c7SDamien Le Moal } 844eb0570c7SDamien Le Moal if (req_op(rq) == REQ_OP_ZONE_APPEND) 845eb0570c7SDamien Le Moal rq->__sector = cmd->sector; 846eb0570c7SDamien Le Moal 847eb0570c7SDamien Le Moal break; 848eb0570c7SDamien Le Moal default: 849eb0570c7SDamien Le Moal break; 850eb0570c7SDamien Le Moal } 851eb0570c7SDamien Le Moal 852eb0570c7SDamien Le Moal if (cmd->ret < 0) 853eb0570c7SDamien Le Moal sts = errno_to_blk_status(cmd->ret); 854eb0570c7SDamien Le Moal blk_mq_end_request(rq, sts); 855eb0570c7SDamien Le Moal } 856eb0570c7SDamien Le Moal 857fcc6eaa3SDamien Le Moal static bool zloop_set_zone_append_sector(struct request *rq) 858fcc6eaa3SDamien Le Moal { 859fcc6eaa3SDamien Le Moal struct zloop_device *zlo = rq->q->queuedata; 860fcc6eaa3SDamien Le Moal unsigned int zone_no = rq_zone_no(rq); 861fcc6eaa3SDamien Le Moal struct zloop_zone *zone = &zlo->zones[zone_no]; 862fcc6eaa3SDamien Le Moal sector_t zone_end = zone->start + zlo->zone_capacity; 863fcc6eaa3SDamien Le Moal sector_t nr_sectors = blk_rq_sectors(rq); 864fcc6eaa3SDamien Le Moal unsigned long flags; 865fcc6eaa3SDamien Le Moal 866fcc6eaa3SDamien Le Moal spin_lock_irqsave(&zone->wp_lock, flags); 867fcc6eaa3SDamien Le Moal 868fcc6eaa3SDamien Le Moal if (zone->cond == BLK_ZONE_COND_FULL || 869fcc6eaa3SDamien Le Moal zone->wp + nr_sectors > zone_end) { 870fcc6eaa3SDamien Le Moal spin_unlock_irqrestore(&zone->wp_lock, flags); 871fcc6eaa3SDamien Le Moal return false; 872fcc6eaa3SDamien Le Moal } 873fcc6eaa3SDamien Le Moal 874fcc6eaa3SDamien Le Moal rq->__sector = zone->wp; 875fcc6eaa3SDamien Le Moal zone->wp += blk_rq_sectors(rq); 876fcc6eaa3SDamien Le Moal if (zone->wp >= zone_end) { 877b2a78fecSDamien Le Moal zloop_lru_remove_open_zone(zlo, zone); 878fcc6eaa3SDamien Le Moal zone->cond = BLK_ZONE_COND_FULL; 879fcc6eaa3SDamien Le Moal zone->wp = ULLONG_MAX; 880fcc6eaa3SDamien Le Moal } 881fcc6eaa3SDamien Le Moal 882fcc6eaa3SDamien Le Moal spin_unlock_irqrestore(&zone->wp_lock, flags); 883fcc6eaa3SDamien Le Moal 884fcc6eaa3SDamien Le Moal return true; 885fcc6eaa3SDamien Le Moal } 886fcc6eaa3SDamien Le Moal 887eb0570c7SDamien Le Moal static blk_status_t zloop_queue_rq(struct blk_mq_hw_ctx *hctx, 888eb0570c7SDamien Le Moal const struct blk_mq_queue_data *bd) 889eb0570c7SDamien Le Moal { 890eb0570c7SDamien Le Moal struct request *rq = bd->rq; 891eb0570c7SDamien Le Moal struct zloop_cmd *cmd = blk_mq_rq_to_pdu(rq); 892eb0570c7SDamien Le Moal struct zloop_device *zlo = rq->q->queuedata; 893eb0570c7SDamien Le Moal 8944b2b0315SYongpeng Yang if (data_race(READ_ONCE(zlo->state)) == Zlo_deleting) 895eb0570c7SDamien Le Moal return BLK_STS_IOERR; 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 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 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; 941fcc6eaa3SDamien Le Moal unsigned long flags; 942eb0570c7SDamien Le Moal int ret; 943eb0570c7SDamien Le Moal 944eb0570c7SDamien Le Moal first = disk_zone_no(disk, sector); 945eb0570c7SDamien Le Moal if (first >= zlo->nr_zones) 946eb0570c7SDamien Le Moal return 0; 947eb0570c7SDamien Le Moal nr_zones = min(nr_zones, zlo->nr_zones - first); 948eb0570c7SDamien Le Moal 949eb0570c7SDamien Le Moal for (i = 0; i < nr_zones; i++) { 950eb0570c7SDamien Le Moal unsigned int zone_no = first + i; 951eb0570c7SDamien Le Moal struct zloop_zone *zone = &zlo->zones[zone_no]; 952eb0570c7SDamien Le Moal 953eb0570c7SDamien Le Moal mutex_lock(&zone->lock); 954eb0570c7SDamien Le Moal 955eb0570c7SDamien Le Moal if (test_and_clear_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags)) { 956eb0570c7SDamien Le Moal ret = zloop_update_seq_zone(zlo, zone_no); 957eb0570c7SDamien Le Moal if (ret) { 958eb0570c7SDamien Le Moal mutex_unlock(&zone->lock); 959eb0570c7SDamien Le Moal return ret; 960eb0570c7SDamien Le Moal } 961eb0570c7SDamien Le Moal } 962eb0570c7SDamien Le Moal 963eb0570c7SDamien Le Moal blkz.start = zone->start; 964eb0570c7SDamien Le Moal blkz.len = zlo->zone_size; 965fcc6eaa3SDamien Le Moal spin_lock_irqsave(&zone->wp_lock, flags); 966eb0570c7SDamien Le Moal blkz.wp = zone->wp; 967fcc6eaa3SDamien Le Moal spin_unlock_irqrestore(&zone->wp_lock, flags); 968eb0570c7SDamien Le Moal blkz.cond = zone->cond; 969eb0570c7SDamien Le Moal if (test_bit(ZLOOP_ZONE_CONV, &zone->flags)) { 970eb0570c7SDamien Le Moal blkz.type = BLK_ZONE_TYPE_CONVENTIONAL; 971eb0570c7SDamien Le Moal blkz.capacity = zlo->zone_size; 972eb0570c7SDamien Le Moal } else { 973eb0570c7SDamien Le Moal blkz.type = BLK_ZONE_TYPE_SEQWRITE_REQ; 974eb0570c7SDamien Le Moal blkz.capacity = zlo->zone_capacity; 975eb0570c7SDamien Le Moal } 976eb0570c7SDamien Le Moal 977eb0570c7SDamien Le Moal mutex_unlock(&zone->lock); 978eb0570c7SDamien Le Moal 979fdb9aed8SDamien Le Moal ret = disk_report_zone(disk, &blkz, i, args); 980eb0570c7SDamien Le Moal if (ret) 981eb0570c7SDamien Le Moal return ret; 982eb0570c7SDamien Le Moal } 983eb0570c7SDamien Le Moal 984eb0570c7SDamien Le Moal return nr_zones; 985eb0570c7SDamien Le Moal } 986eb0570c7SDamien Le Moal 987eb0570c7SDamien Le Moal static void zloop_free_disk(struct gendisk *disk) 988eb0570c7SDamien Le Moal { 989eb0570c7SDamien Le Moal struct zloop_device *zlo = disk->private_data; 990eb0570c7SDamien Le Moal unsigned int i; 991eb0570c7SDamien Le Moal 99276576185SShin'ichiro Kawasaki blk_mq_free_tag_set(&zlo->tag_set); 99376576185SShin'ichiro Kawasaki 994eb0570c7SDamien Le Moal for (i = 0; i < zlo->nr_zones; i++) { 995eb0570c7SDamien Le Moal struct zloop_zone *zone = &zlo->zones[i]; 996eb0570c7SDamien Le Moal 997eb0570c7SDamien Le Moal mapping_set_gfp_mask(zone->file->f_mapping, 998eb0570c7SDamien Le Moal zone->old_gfp_mask); 999eb0570c7SDamien Le Moal fput(zone->file); 1000eb0570c7SDamien Le Moal } 1001eb0570c7SDamien Le Moal 1002eb0570c7SDamien Le Moal fput(zlo->data_dir); 1003eb0570c7SDamien Le Moal destroy_workqueue(zlo->workqueue); 1004eb0570c7SDamien Le Moal kfree(zlo->base_dir); 1005eb0570c7SDamien Le Moal kvfree(zlo); 1006eb0570c7SDamien Le Moal } 1007eb0570c7SDamien Le Moal 1008eb0570c7SDamien Le Moal static const struct block_device_operations zloop_fops = { 1009eb0570c7SDamien Le Moal .owner = THIS_MODULE, 1010eb0570c7SDamien Le Moal .open = zloop_open, 1011eb0570c7SDamien Le Moal .report_zones = zloop_report_zones, 1012eb0570c7SDamien Le Moal .free_disk = zloop_free_disk, 1013eb0570c7SDamien Le Moal }; 1014eb0570c7SDamien Le Moal 1015eb0570c7SDamien Le Moal __printf(3, 4) 1016eb0570c7SDamien Le Moal static struct file *zloop_filp_open_fmt(int oflags, umode_t mode, 1017eb0570c7SDamien Le Moal const char *fmt, ...) 1018eb0570c7SDamien Le Moal { 1019eb0570c7SDamien Le Moal struct file *file; 1020eb0570c7SDamien Le Moal va_list ap; 1021eb0570c7SDamien Le Moal char *p; 1022eb0570c7SDamien Le Moal 1023eb0570c7SDamien Le Moal va_start(ap, fmt); 1024eb0570c7SDamien Le Moal p = kvasprintf(GFP_KERNEL, fmt, ap); 1025eb0570c7SDamien Le Moal va_end(ap); 1026eb0570c7SDamien Le Moal 1027eb0570c7SDamien Le Moal if (!p) 1028eb0570c7SDamien Le Moal return ERR_PTR(-ENOMEM); 1029eb0570c7SDamien Le Moal file = filp_open(p, oflags, mode); 1030eb0570c7SDamien Le Moal kfree(p); 1031eb0570c7SDamien Le Moal return file; 1032eb0570c7SDamien Le Moal } 1033eb0570c7SDamien Le Moal 1034eb0570c7SDamien Le Moal static int zloop_get_block_size(struct zloop_device *zlo, 1035eb0570c7SDamien Le Moal struct zloop_zone *zone) 1036eb0570c7SDamien Le Moal { 1037eb0570c7SDamien Le Moal struct block_device *sb_bdev = zone->file->f_mapping->host->i_sb->s_bdev; 1038eb0570c7SDamien Le Moal struct kstat st; 1039eb0570c7SDamien Le Moal 1040eb0570c7SDamien Le Moal /* 1041eb0570c7SDamien Le Moal * If the FS block size is lower than or equal to 4K, use that as the 1042eb0570c7SDamien Le Moal * device block size. Otherwise, fallback to the FS direct IO alignment 1043eb0570c7SDamien Le Moal * constraint if that is provided, and to the FS underlying device 1044eb0570c7SDamien Le Moal * physical block size if the direct IO alignment is unknown. 1045eb0570c7SDamien Le Moal */ 1046eb0570c7SDamien Le Moal if (file_inode(zone->file)->i_sb->s_blocksize <= SZ_4K) 1047eb0570c7SDamien Le Moal zlo->block_size = file_inode(zone->file)->i_sb->s_blocksize; 1048eb0570c7SDamien Le Moal else if (!vfs_getattr(&zone->file->f_path, &st, STATX_DIOALIGN, 0) && 1049eb0570c7SDamien Le Moal (st.result_mask & STATX_DIOALIGN)) 1050eb0570c7SDamien Le Moal zlo->block_size = st.dio_offset_align; 1051eb0570c7SDamien Le Moal else if (sb_bdev) 1052eb0570c7SDamien Le Moal zlo->block_size = bdev_physical_block_size(sb_bdev); 1053eb0570c7SDamien Le Moal else 1054eb0570c7SDamien Le Moal zlo->block_size = SECTOR_SIZE; 1055eb0570c7SDamien Le Moal 1056eb0570c7SDamien Le Moal if (zlo->zone_capacity & ((zlo->block_size >> SECTOR_SHIFT) - 1)) { 1057eb0570c7SDamien Le Moal pr_err("Zone capacity is not aligned to block size %u\n", 1058eb0570c7SDamien Le Moal zlo->block_size); 1059eb0570c7SDamien Le Moal return -EINVAL; 1060eb0570c7SDamien Le Moal } 1061eb0570c7SDamien Le Moal 1062eb0570c7SDamien Le Moal return 0; 1063eb0570c7SDamien Le Moal } 1064eb0570c7SDamien Le Moal 1065eb0570c7SDamien Le Moal static int zloop_init_zone(struct zloop_device *zlo, struct zloop_options *opts, 1066eb0570c7SDamien Le Moal unsigned int zone_no, bool restore) 1067eb0570c7SDamien Le Moal { 1068eb0570c7SDamien Le Moal struct zloop_zone *zone = &zlo->zones[zone_no]; 1069eb0570c7SDamien Le Moal int oflags = O_RDWR; 1070eb0570c7SDamien Le Moal struct kstat stat; 1071eb0570c7SDamien Le Moal sector_t file_sectors; 1072eb0570c7SDamien Le Moal int ret; 1073eb0570c7SDamien Le Moal 1074eb0570c7SDamien Le Moal mutex_init(&zone->lock); 1075b2a78fecSDamien Le Moal INIT_LIST_HEAD(&zone->open_zone_entry); 1076fcc6eaa3SDamien Le Moal spin_lock_init(&zone->wp_lock); 1077eb0570c7SDamien Le Moal zone->start = (sector_t)zone_no << zlo->zone_shift; 1078eb0570c7SDamien Le Moal 1079eb0570c7SDamien Le Moal if (!restore) 1080eb0570c7SDamien Le Moal oflags |= O_CREAT; 1081eb0570c7SDamien Le Moal 1082eb0570c7SDamien Le Moal if (!opts->buffered_io) 1083eb0570c7SDamien Le Moal oflags |= O_DIRECT; 1084eb0570c7SDamien Le Moal 1085eb0570c7SDamien Le Moal if (zone_no < zlo->nr_conv_zones) { 1086eb0570c7SDamien Le Moal /* Conventional zone file. */ 1087eb0570c7SDamien Le Moal set_bit(ZLOOP_ZONE_CONV, &zone->flags); 1088eb0570c7SDamien Le Moal zone->cond = BLK_ZONE_COND_NOT_WP; 1089eb0570c7SDamien Le Moal zone->wp = U64_MAX; 1090eb0570c7SDamien Le Moal 1091eb0570c7SDamien Le Moal zone->file = zloop_filp_open_fmt(oflags, 0600, "%s/%u/cnv-%06u", 1092eb0570c7SDamien Le Moal zlo->base_dir, zlo->id, zone_no); 1093eb0570c7SDamien Le Moal if (IS_ERR(zone->file)) { 1094eb0570c7SDamien Le Moal pr_err("Failed to open zone %u file %s/%u/cnv-%06u (err=%ld)", 1095eb0570c7SDamien Le Moal zone_no, zlo->base_dir, zlo->id, zone_no, 1096eb0570c7SDamien Le Moal PTR_ERR(zone->file)); 1097eb0570c7SDamien Le Moal return PTR_ERR(zone->file); 1098eb0570c7SDamien Le Moal } 1099eb0570c7SDamien Le Moal 1100eb0570c7SDamien Le Moal if (!zlo->block_size) { 1101eb0570c7SDamien Le Moal ret = zloop_get_block_size(zlo, zone); 1102eb0570c7SDamien Le Moal if (ret) 1103eb0570c7SDamien Le Moal return ret; 1104eb0570c7SDamien Le Moal } 1105eb0570c7SDamien Le Moal 1106eb0570c7SDamien Le Moal ret = vfs_getattr(&zone->file->f_path, &stat, STATX_SIZE, 0); 1107eb0570c7SDamien Le Moal if (ret < 0) { 1108eb0570c7SDamien Le Moal pr_err("Failed to get zone %u file stat\n", zone_no); 1109eb0570c7SDamien Le Moal return ret; 1110eb0570c7SDamien Le Moal } 1111eb0570c7SDamien Le Moal file_sectors = stat.size >> SECTOR_SHIFT; 1112eb0570c7SDamien Le Moal 1113eb0570c7SDamien Le Moal if (restore && file_sectors != zlo->zone_size) { 1114eb0570c7SDamien Le Moal pr_err("Invalid conventional zone %u file size (%llu sectors != %llu)\n", 1115eb0570c7SDamien Le Moal zone_no, file_sectors, zlo->zone_capacity); 1116eb0570c7SDamien Le Moal return ret; 1117eb0570c7SDamien Le Moal } 1118eb0570c7SDamien Le Moal 1119eb0570c7SDamien Le Moal ret = vfs_truncate(&zone->file->f_path, 1120eb0570c7SDamien Le Moal zlo->zone_size << SECTOR_SHIFT); 1121eb0570c7SDamien Le Moal if (ret < 0) { 1122eb0570c7SDamien Le Moal pr_err("Failed to truncate zone %u file (err=%d)\n", 1123eb0570c7SDamien Le Moal zone_no, ret); 1124eb0570c7SDamien Le Moal return ret; 1125eb0570c7SDamien Le Moal } 1126eb0570c7SDamien Le Moal 1127eb0570c7SDamien Le Moal return 0; 1128eb0570c7SDamien Le Moal } 1129eb0570c7SDamien Le Moal 1130eb0570c7SDamien Le Moal /* Sequential zone file. */ 1131eb0570c7SDamien Le Moal zone->file = zloop_filp_open_fmt(oflags, 0600, "%s/%u/seq-%06u", 1132eb0570c7SDamien Le Moal zlo->base_dir, zlo->id, zone_no); 1133eb0570c7SDamien Le Moal if (IS_ERR(zone->file)) { 1134eb0570c7SDamien Le Moal pr_err("Failed to open zone %u file %s/%u/seq-%06u (err=%ld)", 1135eb0570c7SDamien Le Moal zone_no, zlo->base_dir, zlo->id, zone_no, 1136eb0570c7SDamien Le Moal PTR_ERR(zone->file)); 1137eb0570c7SDamien Le Moal return PTR_ERR(zone->file); 1138eb0570c7SDamien Le Moal } 1139eb0570c7SDamien Le Moal 1140eb0570c7SDamien Le Moal if (!zlo->block_size) { 1141eb0570c7SDamien Le Moal ret = zloop_get_block_size(zlo, zone); 1142eb0570c7SDamien Le Moal if (ret) 1143eb0570c7SDamien Le Moal return ret; 1144eb0570c7SDamien Le Moal } 1145eb0570c7SDamien Le Moal 1146eb0570c7SDamien Le Moal zloop_get_block_size(zlo, zone); 1147eb0570c7SDamien Le Moal 1148eb0570c7SDamien Le Moal mutex_lock(&zone->lock); 1149eb0570c7SDamien Le Moal ret = zloop_update_seq_zone(zlo, zone_no); 1150eb0570c7SDamien Le Moal mutex_unlock(&zone->lock); 1151eb0570c7SDamien Le Moal 1152eb0570c7SDamien Le Moal return ret; 1153eb0570c7SDamien Le Moal } 1154eb0570c7SDamien Le Moal 1155eb0570c7SDamien Le Moal static bool zloop_dev_exists(struct zloop_device *zlo) 1156eb0570c7SDamien Le Moal { 1157eb0570c7SDamien Le Moal struct file *cnv, *seq; 1158eb0570c7SDamien Le Moal bool exists; 1159eb0570c7SDamien Le Moal 1160eb0570c7SDamien Le Moal cnv = zloop_filp_open_fmt(O_RDONLY, 0600, "%s/%u/cnv-%06u", 1161eb0570c7SDamien Le Moal zlo->base_dir, zlo->id, 0); 1162eb0570c7SDamien Le Moal seq = zloop_filp_open_fmt(O_RDONLY, 0600, "%s/%u/seq-%06u", 1163eb0570c7SDamien Le Moal zlo->base_dir, zlo->id, 0); 1164eb0570c7SDamien Le Moal exists = !IS_ERR(cnv) || !IS_ERR(seq); 1165eb0570c7SDamien Le Moal 1166eb0570c7SDamien Le Moal if (!IS_ERR(cnv)) 1167eb0570c7SDamien Le Moal fput(cnv); 1168eb0570c7SDamien Le Moal if (!IS_ERR(seq)) 1169eb0570c7SDamien Le Moal fput(seq); 1170eb0570c7SDamien Le Moal 1171eb0570c7SDamien Le Moal return exists; 1172eb0570c7SDamien Le Moal } 1173eb0570c7SDamien Le Moal 1174eb0570c7SDamien Le Moal static int zloop_ctl_add(struct zloop_options *opts) 1175eb0570c7SDamien Le Moal { 1176eb0570c7SDamien Le Moal struct queue_limits lim = { 1177eb0570c7SDamien Le Moal .max_hw_sectors = SZ_1M >> SECTOR_SHIFT, 1178eb0570c7SDamien Le Moal .chunk_sectors = opts->zone_size, 11796acf7860SChristoph Hellwig .features = BLK_FEAT_ZONED | BLK_FEAT_WRITE_CACHE, 11806acf7860SChristoph Hellwig 1181eb0570c7SDamien Le Moal }; 1182eb0570c7SDamien Le Moal unsigned int nr_zones, i, j; 1183eb0570c7SDamien Le Moal struct zloop_device *zlo; 1184eb0570c7SDamien Le Moal int ret = -EINVAL; 1185eb0570c7SDamien Le Moal bool restore; 1186eb0570c7SDamien Le Moal 1187eb0570c7SDamien Le Moal __module_get(THIS_MODULE); 1188eb0570c7SDamien Le Moal 1189eb0570c7SDamien Le Moal nr_zones = opts->capacity >> ilog2(opts->zone_size); 1190eb0570c7SDamien Le Moal if (opts->nr_conv_zones >= nr_zones) { 1191eb0570c7SDamien Le Moal pr_err("Invalid number of conventional zones %u\n", 1192eb0570c7SDamien Le Moal opts->nr_conv_zones); 1193eb0570c7SDamien Le Moal goto out; 1194eb0570c7SDamien Le Moal } 1195eb0570c7SDamien Le Moal 1196b2a78fecSDamien Le Moal if (opts->max_open_zones > nr_zones - opts->nr_conv_zones) { 1197b2a78fecSDamien Le Moal pr_err("Invalid maximum number of open zones %u\n", 1198b2a78fecSDamien Le Moal opts->max_open_zones); 1199b2a78fecSDamien Le Moal goto out; 1200b2a78fecSDamien Le Moal } 1201b2a78fecSDamien Le Moal 1202323bbfcfSLinus Torvalds zlo = kvzalloc_flex(*zlo, zones, nr_zones); 1203eb0570c7SDamien Le Moal if (!zlo) { 1204eb0570c7SDamien Le Moal ret = -ENOMEM; 1205eb0570c7SDamien Le Moal goto out; 1206eb0570c7SDamien Le Moal } 12074b2b0315SYongpeng Yang WRITE_ONCE(zlo->state, Zlo_creating); 1208b2a78fecSDamien Le Moal spin_lock_init(&zlo->open_zones_lock); 1209b2a78fecSDamien Le Moal INIT_LIST_HEAD(&zlo->open_zones_lru_list); 1210eb0570c7SDamien Le Moal 1211eb0570c7SDamien Le Moal ret = mutex_lock_killable(&zloop_ctl_mutex); 1212eb0570c7SDamien Le Moal if (ret) 1213eb0570c7SDamien Le Moal goto out_free_dev; 1214eb0570c7SDamien Le Moal 1215eb0570c7SDamien Le Moal /* Allocate id, if @opts->id >= 0, we're requesting that specific id */ 1216eb0570c7SDamien Le Moal if (opts->id >= 0) { 1217eb0570c7SDamien Le Moal ret = idr_alloc(&zloop_index_idr, zlo, 1218eb0570c7SDamien Le Moal opts->id, opts->id + 1, GFP_KERNEL); 1219eb0570c7SDamien Le Moal if (ret == -ENOSPC) 1220eb0570c7SDamien Le Moal ret = -EEXIST; 1221eb0570c7SDamien Le Moal } else { 1222eb0570c7SDamien Le Moal ret = idr_alloc(&zloop_index_idr, zlo, 0, 0, GFP_KERNEL); 1223eb0570c7SDamien Le Moal } 1224eb0570c7SDamien Le Moal mutex_unlock(&zloop_ctl_mutex); 1225eb0570c7SDamien Le Moal if (ret < 0) 1226eb0570c7SDamien Le Moal goto out_free_dev; 1227eb0570c7SDamien Le Moal 1228eb0570c7SDamien Le Moal zlo->id = ret; 1229eb0570c7SDamien Le Moal zlo->zone_shift = ilog2(opts->zone_size); 1230eb0570c7SDamien Le Moal zlo->zone_size = opts->zone_size; 1231eb0570c7SDamien Le Moal if (opts->zone_capacity) 1232eb0570c7SDamien Le Moal zlo->zone_capacity = opts->zone_capacity; 1233eb0570c7SDamien Le Moal else 1234eb0570c7SDamien Le Moal zlo->zone_capacity = zlo->zone_size; 1235eb0570c7SDamien Le Moal zlo->nr_zones = nr_zones; 1236eb0570c7SDamien Le Moal zlo->nr_conv_zones = opts->nr_conv_zones; 1237b2a78fecSDamien Le Moal zlo->max_open_zones = opts->max_open_zones; 1238eb0570c7SDamien Le Moal zlo->buffered_io = opts->buffered_io; 12399236c5fdSDamien Le Moal zlo->zone_append = opts->zone_append; 1240fcc6eaa3SDamien Le Moal if (zlo->zone_append) 1241fcc6eaa3SDamien Le Moal zlo->ordered_zone_append = opts->ordered_zone_append; 1242829def1eSChristoph Hellwig zlo->discard_write_cache = opts->discard_write_cache; 1243eb0570c7SDamien Le Moal 1244eb0570c7SDamien Le Moal zlo->workqueue = alloc_workqueue("zloop%d", WQ_UNBOUND | WQ_FREEZABLE, 1245eb0570c7SDamien Le Moal opts->nr_queues * opts->queue_depth, zlo->id); 1246eb0570c7SDamien Le Moal if (!zlo->workqueue) { 1247eb0570c7SDamien Le Moal ret = -ENOMEM; 1248eb0570c7SDamien Le Moal goto out_free_idr; 1249eb0570c7SDamien Le Moal } 1250eb0570c7SDamien Le Moal 1251eb0570c7SDamien Le Moal if (opts->base_dir) 1252eb0570c7SDamien Le Moal zlo->base_dir = kstrdup(opts->base_dir, GFP_KERNEL); 1253eb0570c7SDamien Le Moal else 1254eb0570c7SDamien Le Moal zlo->base_dir = kstrdup(ZLOOP_DEF_BASE_DIR, GFP_KERNEL); 1255eb0570c7SDamien Le Moal if (!zlo->base_dir) { 1256eb0570c7SDamien Le Moal ret = -ENOMEM; 1257eb0570c7SDamien Le Moal goto out_destroy_workqueue; 1258eb0570c7SDamien Le Moal } 1259eb0570c7SDamien Le Moal 1260eb0570c7SDamien Le Moal zlo->data_dir = zloop_filp_open_fmt(O_RDONLY | O_DIRECTORY, 0, "%s/%u", 1261eb0570c7SDamien Le Moal zlo->base_dir, zlo->id); 1262eb0570c7SDamien Le Moal if (IS_ERR(zlo->data_dir)) { 1263eb0570c7SDamien Le Moal ret = PTR_ERR(zlo->data_dir); 1264eb0570c7SDamien Le Moal pr_warn("Failed to open directory %s/%u (err=%d)\n", 1265eb0570c7SDamien Le Moal zlo->base_dir, zlo->id, ret); 1266eb0570c7SDamien Le Moal goto out_free_base_dir; 1267eb0570c7SDamien Le Moal } 1268eb0570c7SDamien Le Moal 1269eb0570c7SDamien Le Moal /* 1270eb0570c7SDamien Le Moal * If we already have zone files, we are restoring a device created by a 1271eb0570c7SDamien Le Moal * previous add operation. In this case, zloop_init_zone() will check 1272eb0570c7SDamien Le Moal * that the zone files are consistent with the zone configuration given. 1273eb0570c7SDamien Le Moal */ 1274eb0570c7SDamien Le Moal restore = zloop_dev_exists(zlo); 1275eb0570c7SDamien Le Moal for (i = 0; i < nr_zones; i++) { 1276eb0570c7SDamien Le Moal ret = zloop_init_zone(zlo, opts, i, restore); 1277eb0570c7SDamien Le Moal if (ret) 1278eb0570c7SDamien Le Moal goto out_close_files; 1279eb0570c7SDamien Le Moal } 1280eb0570c7SDamien Le Moal 1281eb0570c7SDamien Le Moal lim.physical_block_size = zlo->block_size; 1282eb0570c7SDamien Le Moal lim.logical_block_size = zlo->block_size; 12839236c5fdSDamien Le Moal if (zlo->zone_append) 12849236c5fdSDamien Le Moal lim.max_hw_zone_append_sectors = lim.max_hw_sectors; 1285b2a78fecSDamien Le Moal lim.max_open_zones = zlo->max_open_zones; 1286eb0570c7SDamien Le Moal 1287eb0570c7SDamien Le Moal zlo->tag_set.ops = &zloop_mq_ops; 1288eb0570c7SDamien Le Moal zlo->tag_set.nr_hw_queues = opts->nr_queues; 1289eb0570c7SDamien Le Moal zlo->tag_set.queue_depth = opts->queue_depth; 1290eb0570c7SDamien Le Moal zlo->tag_set.numa_node = NUMA_NO_NODE; 1291eb0570c7SDamien Le Moal zlo->tag_set.cmd_size = sizeof(struct zloop_cmd); 1292eb0570c7SDamien Le Moal zlo->tag_set.driver_data = zlo; 1293eb0570c7SDamien Le Moal 1294eb0570c7SDamien Le Moal ret = blk_mq_alloc_tag_set(&zlo->tag_set); 1295eb0570c7SDamien Le Moal if (ret) { 1296eb0570c7SDamien Le Moal pr_err("blk_mq_alloc_tag_set failed (err=%d)\n", ret); 1297eb0570c7SDamien Le Moal goto out_close_files; 1298eb0570c7SDamien Le Moal } 1299eb0570c7SDamien Le Moal 1300eb0570c7SDamien Le Moal zlo->disk = blk_mq_alloc_disk(&zlo->tag_set, &lim, zlo); 1301eb0570c7SDamien Le Moal if (IS_ERR(zlo->disk)) { 1302eb0570c7SDamien Le Moal pr_err("blk_mq_alloc_disk failed (err=%d)\n", ret); 1303eb0570c7SDamien Le Moal ret = PTR_ERR(zlo->disk); 1304eb0570c7SDamien Le Moal goto out_cleanup_tags; 1305eb0570c7SDamien Le Moal } 1306eb0570c7SDamien Le Moal zlo->disk->flags = GENHD_FL_NO_PART; 1307eb0570c7SDamien Le Moal zlo->disk->fops = &zloop_fops; 1308eb0570c7SDamien Le Moal zlo->disk->private_data = zlo; 1309eb0570c7SDamien Le Moal sprintf(zlo->disk->disk_name, "zloop%d", zlo->id); 1310eb0570c7SDamien Le Moal set_capacity(zlo->disk, (u64)lim.chunk_sectors * zlo->nr_zones); 1311eb0570c7SDamien Le Moal 1312eb0570c7SDamien Le Moal ret = blk_revalidate_disk_zones(zlo->disk); 1313eb0570c7SDamien Le Moal if (ret) 1314eb0570c7SDamien Le Moal goto out_cleanup_disk; 1315eb0570c7SDamien Le Moal 1316eb0570c7SDamien Le Moal ret = add_disk(zlo->disk); 1317eb0570c7SDamien Le Moal if (ret) { 1318eb0570c7SDamien Le Moal pr_err("add_disk failed (err=%d)\n", ret); 1319eb0570c7SDamien Le Moal goto out_cleanup_disk; 1320eb0570c7SDamien Le Moal } 1321eb0570c7SDamien Le Moal 1322eb0570c7SDamien Le Moal mutex_lock(&zloop_ctl_mutex); 13234b2b0315SYongpeng Yang WRITE_ONCE(zlo->state, Zlo_live); 1324eb0570c7SDamien Le Moal mutex_unlock(&zloop_ctl_mutex); 1325eb0570c7SDamien Le Moal 13269236c5fdSDamien Le Moal pr_info("zloop: device %d, %u zones of %llu MiB, %u B block size\n", 1327eb0570c7SDamien Le Moal zlo->id, zlo->nr_zones, 1328eb0570c7SDamien Le Moal ((sector_t)zlo->zone_size << SECTOR_SHIFT) >> 20, 1329eb0570c7SDamien Le Moal zlo->block_size); 1330fcc6eaa3SDamien Le Moal pr_info("zloop%d: using %s%s zone append\n", 13319236c5fdSDamien Le Moal zlo->id, 1332fcc6eaa3SDamien Le Moal zlo->ordered_zone_append ? "ordered " : "", 13339236c5fdSDamien Le Moal zlo->zone_append ? "native" : "emulated"); 1334eb0570c7SDamien Le Moal 1335eb0570c7SDamien Le Moal return 0; 1336eb0570c7SDamien Le Moal 1337eb0570c7SDamien Le Moal out_cleanup_disk: 1338eb0570c7SDamien Le Moal put_disk(zlo->disk); 1339eb0570c7SDamien Le Moal out_cleanup_tags: 1340eb0570c7SDamien Le Moal blk_mq_free_tag_set(&zlo->tag_set); 1341eb0570c7SDamien Le Moal out_close_files: 1342eb0570c7SDamien Le Moal for (j = 0; j < i; j++) { 1343eb0570c7SDamien Le Moal struct zloop_zone *zone = &zlo->zones[j]; 1344eb0570c7SDamien Le Moal 1345eb0570c7SDamien Le Moal if (!IS_ERR_OR_NULL(zone->file)) 1346eb0570c7SDamien Le Moal fput(zone->file); 1347eb0570c7SDamien Le Moal } 1348eb0570c7SDamien Le Moal fput(zlo->data_dir); 1349eb0570c7SDamien Le Moal out_free_base_dir: 1350eb0570c7SDamien Le Moal kfree(zlo->base_dir); 1351eb0570c7SDamien Le Moal out_destroy_workqueue: 1352eb0570c7SDamien Le Moal destroy_workqueue(zlo->workqueue); 1353eb0570c7SDamien Le Moal out_free_idr: 1354eb0570c7SDamien Le Moal mutex_lock(&zloop_ctl_mutex); 1355eb0570c7SDamien Le Moal idr_remove(&zloop_index_idr, zlo->id); 1356eb0570c7SDamien Le Moal mutex_unlock(&zloop_ctl_mutex); 1357eb0570c7SDamien Le Moal out_free_dev: 1358eb0570c7SDamien Le Moal kvfree(zlo); 1359eb0570c7SDamien Le Moal out: 1360eb0570c7SDamien Le Moal module_put(THIS_MODULE); 1361eb0570c7SDamien Le Moal if (ret == -ENOENT) 1362eb0570c7SDamien Le Moal ret = -EINVAL; 1363eb0570c7SDamien Le Moal return ret; 1364eb0570c7SDamien Le Moal } 1365eb0570c7SDamien Le Moal 1366829def1eSChristoph Hellwig static void zloop_truncate(struct file *file, loff_t pos) 1367829def1eSChristoph Hellwig { 1368829def1eSChristoph Hellwig struct mnt_idmap *idmap = file_mnt_idmap(file); 1369829def1eSChristoph Hellwig struct dentry *dentry = file_dentry(file); 1370829def1eSChristoph Hellwig struct iattr newattrs; 1371829def1eSChristoph Hellwig 1372829def1eSChristoph Hellwig newattrs.ia_size = pos; 1373829def1eSChristoph Hellwig newattrs.ia_valid = ATTR_SIZE; 1374829def1eSChristoph Hellwig 1375829def1eSChristoph Hellwig inode_lock(dentry->d_inode); 1376829def1eSChristoph Hellwig notify_change(idmap, dentry, &newattrs, NULL); 1377829def1eSChristoph Hellwig inode_unlock(dentry->d_inode); 1378829def1eSChristoph Hellwig } 1379829def1eSChristoph Hellwig 1380829def1eSChristoph Hellwig static void zloop_forget_cache(struct zloop_device *zlo) 1381829def1eSChristoph Hellwig { 1382829def1eSChristoph Hellwig unsigned int i; 1383829def1eSChristoph Hellwig int ret; 1384829def1eSChristoph Hellwig 1385829def1eSChristoph Hellwig pr_info("%pg: discarding volatile write cache\n", zlo->disk->part0); 1386829def1eSChristoph Hellwig 1387829def1eSChristoph Hellwig for (i = 0; i < zlo->nr_zones; i++) { 1388829def1eSChristoph Hellwig struct zloop_zone *zone = &zlo->zones[i]; 1389829def1eSChristoph Hellwig struct file *file = zone->file; 1390829def1eSChristoph Hellwig sector_t old_wp; 1391829def1eSChristoph Hellwig 1392829def1eSChristoph Hellwig if (!zloop_zone_is_active(zone)) 1393829def1eSChristoph Hellwig continue; 1394829def1eSChristoph Hellwig 1395829def1eSChristoph Hellwig ret = vfs_getxattr(file_mnt_idmap(file), file_dentry(file), 1396829def1eSChristoph Hellwig "user.zloop.wp", &old_wp, sizeof(old_wp)); 1397829def1eSChristoph Hellwig if (ret == -ENODATA) { 1398829def1eSChristoph Hellwig old_wp = 0; 1399829def1eSChristoph Hellwig } else if (ret != sizeof(old_wp)) { 1400829def1eSChristoph Hellwig pr_err("%pg: failed to retrieve write pointer (%d)\n", 1401829def1eSChristoph Hellwig zlo->disk->part0, ret); 1402829def1eSChristoph Hellwig continue; 1403829def1eSChristoph Hellwig } 1404*32be3c01SChristoph Hellwig 1405*32be3c01SChristoph Hellwig if (old_wp > zone->wp) 1406*32be3c01SChristoph Hellwig continue; 1407*32be3c01SChristoph Hellwig /* 1408*32be3c01SChristoph Hellwig * This should not happen, if we recored a full zone, it can't 1409*32be3c01SChristoph Hellwig * be active. 1410*32be3c01SChristoph Hellwig */ 1411*32be3c01SChristoph Hellwig if (WARN_ON_ONCE(old_wp == ULLONG_MAX)) 1412*32be3c01SChristoph Hellwig continue; 1413*32be3c01SChristoph Hellwig 1414*32be3c01SChristoph Hellwig zloop_truncate(file, (old_wp - zone->start) << SECTOR_SHIFT); 1415829def1eSChristoph Hellwig } 1416829def1eSChristoph Hellwig } 1417829def1eSChristoph Hellwig 1418eb0570c7SDamien Le Moal static int zloop_ctl_remove(struct zloop_options *opts) 1419eb0570c7SDamien Le Moal { 1420eb0570c7SDamien Le Moal struct zloop_device *zlo; 1421eb0570c7SDamien Le Moal int ret; 1422eb0570c7SDamien Le Moal 1423eb0570c7SDamien Le Moal if (!(opts->mask & ZLOOP_OPT_ID)) { 14243c461711SChristoph Hellwig pr_err("No ID specified for remove\n"); 14253c461711SChristoph Hellwig return -EINVAL; 14263c461711SChristoph Hellwig } 14273c461711SChristoph Hellwig 14283c461711SChristoph Hellwig if (opts->mask & ~ZLOOP_OPT_ID) { 14293c461711SChristoph Hellwig pr_err("Invalid option specified for remove\n"); 1430eb0570c7SDamien Le Moal return -EINVAL; 1431eb0570c7SDamien Le Moal } 1432eb0570c7SDamien Le Moal 1433eb0570c7SDamien Le Moal ret = mutex_lock_killable(&zloop_ctl_mutex); 1434eb0570c7SDamien Le Moal if (ret) 1435eb0570c7SDamien Le Moal return ret; 1436eb0570c7SDamien Le Moal 1437eb0570c7SDamien Le Moal zlo = idr_find(&zloop_index_idr, opts->id); 1438eb0570c7SDamien Le Moal if (!zlo || zlo->state == Zlo_creating) { 1439eb0570c7SDamien Le Moal ret = -ENODEV; 1440eb0570c7SDamien Le Moal } else if (zlo->state == Zlo_deleting) { 1441eb0570c7SDamien Le Moal ret = -EINVAL; 1442eb0570c7SDamien Le Moal } else { 1443eb0570c7SDamien Le Moal idr_remove(&zloop_index_idr, zlo->id); 14444b2b0315SYongpeng Yang WRITE_ONCE(zlo->state, Zlo_deleting); 1445eb0570c7SDamien Le Moal } 1446eb0570c7SDamien Le Moal 1447eb0570c7SDamien Le Moal mutex_unlock(&zloop_ctl_mutex); 1448eb0570c7SDamien Le Moal if (ret) 1449eb0570c7SDamien Le Moal return ret; 1450eb0570c7SDamien Le Moal 1451eb0570c7SDamien Le Moal del_gendisk(zlo->disk); 1452829def1eSChristoph Hellwig 1453829def1eSChristoph Hellwig if (zlo->discard_write_cache) 1454829def1eSChristoph Hellwig zloop_forget_cache(zlo); 1455829def1eSChristoph Hellwig 1456eb0570c7SDamien Le Moal put_disk(zlo->disk); 1457eb0570c7SDamien Le Moal 1458eb0570c7SDamien Le Moal pr_info("Removed device %d\n", opts->id); 1459eb0570c7SDamien Le Moal 1460eb0570c7SDamien Le Moal module_put(THIS_MODULE); 1461eb0570c7SDamien Le Moal 1462eb0570c7SDamien Le Moal return 0; 1463eb0570c7SDamien Le Moal } 1464eb0570c7SDamien Le Moal 1465eb0570c7SDamien Le Moal static int zloop_parse_options(struct zloop_options *opts, const char *buf) 1466eb0570c7SDamien Le Moal { 1467eb0570c7SDamien Le Moal substring_t args[MAX_OPT_ARGS]; 1468eb0570c7SDamien Le Moal char *options, *o, *p; 1469eb0570c7SDamien Le Moal unsigned int token; 1470eb0570c7SDamien Le Moal int ret = 0; 1471eb0570c7SDamien Le Moal 1472eb0570c7SDamien Le Moal /* Set defaults. */ 1473eb0570c7SDamien Le Moal opts->mask = 0; 1474eb0570c7SDamien Le Moal opts->id = ZLOOP_DEF_ID; 1475eb0570c7SDamien Le Moal opts->capacity = ZLOOP_DEF_ZONE_SIZE * ZLOOP_DEF_NR_ZONES; 1476eb0570c7SDamien Le Moal opts->zone_size = ZLOOP_DEF_ZONE_SIZE; 1477eb0570c7SDamien Le Moal opts->nr_conv_zones = ZLOOP_DEF_NR_CONV_ZONES; 1478b2a78fecSDamien Le Moal opts->max_open_zones = ZLOOP_DEF_MAX_OPEN_ZONES; 1479eb0570c7SDamien Le Moal opts->nr_queues = ZLOOP_DEF_NR_QUEUES; 1480eb0570c7SDamien Le Moal opts->queue_depth = ZLOOP_DEF_QUEUE_DEPTH; 1481eb0570c7SDamien Le Moal opts->buffered_io = ZLOOP_DEF_BUFFERED_IO; 14829236c5fdSDamien Le Moal opts->zone_append = ZLOOP_DEF_ZONE_APPEND; 1483fcc6eaa3SDamien Le Moal opts->ordered_zone_append = ZLOOP_DEF_ORDERED_ZONE_APPEND; 1484eb0570c7SDamien Le Moal 1485eb0570c7SDamien Le Moal if (!buf) 1486eb0570c7SDamien Le Moal return 0; 1487eb0570c7SDamien Le Moal 1488eb0570c7SDamien Le Moal /* Skip leading spaces before the options. */ 1489eb0570c7SDamien Le Moal while (isspace(*buf)) 1490eb0570c7SDamien Le Moal buf++; 1491eb0570c7SDamien Le Moal 1492eb0570c7SDamien Le Moal options = o = kstrdup(buf, GFP_KERNEL); 1493eb0570c7SDamien Le Moal if (!options) 1494eb0570c7SDamien Le Moal return -ENOMEM; 1495eb0570c7SDamien Le Moal 1496eb0570c7SDamien Le Moal /* Parse the options, doing only some light invalid value checks. */ 1497eb0570c7SDamien Le Moal while ((p = strsep(&o, ",\n")) != NULL) { 1498eb0570c7SDamien Le Moal if (!*p) 1499eb0570c7SDamien Le Moal continue; 1500eb0570c7SDamien Le Moal 1501eb0570c7SDamien Le Moal token = match_token(p, zloop_opt_tokens, args); 1502eb0570c7SDamien Le Moal opts->mask |= token; 1503eb0570c7SDamien Le Moal switch (token) { 1504eb0570c7SDamien Le Moal case ZLOOP_OPT_ID: 1505eb0570c7SDamien Le Moal if (match_int(args, &opts->id)) { 1506eb0570c7SDamien Le Moal ret = -EINVAL; 1507eb0570c7SDamien Le Moal goto out; 1508eb0570c7SDamien Le Moal } 1509eb0570c7SDamien Le Moal break; 1510eb0570c7SDamien Le Moal case ZLOOP_OPT_CAPACITY: 1511eb0570c7SDamien Le Moal if (match_uint(args, &token)) { 1512eb0570c7SDamien Le Moal ret = -EINVAL; 1513eb0570c7SDamien Le Moal goto out; 1514eb0570c7SDamien Le Moal } 1515eb0570c7SDamien Le Moal if (!token) { 1516eb0570c7SDamien Le Moal pr_err("Invalid capacity\n"); 1517eb0570c7SDamien Le Moal ret = -EINVAL; 1518eb0570c7SDamien Le Moal goto out; 1519eb0570c7SDamien Le Moal } 1520eb0570c7SDamien Le Moal opts->capacity = 1521eb0570c7SDamien Le Moal ((sector_t)token * SZ_1M) >> SECTOR_SHIFT; 1522eb0570c7SDamien Le Moal break; 1523eb0570c7SDamien Le Moal case ZLOOP_OPT_ZONE_SIZE: 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 || token > ZLOOP_MAX_ZONE_SIZE_MB || 1529eb0570c7SDamien Le Moal !is_power_of_2(token)) { 1530eb0570c7SDamien Le Moal pr_err("Invalid zone size %u\n", token); 1531eb0570c7SDamien Le Moal ret = -EINVAL; 1532eb0570c7SDamien Le Moal goto out; 1533eb0570c7SDamien Le Moal } 1534eb0570c7SDamien Le Moal opts->zone_size = 1535eb0570c7SDamien Le Moal ((sector_t)token * SZ_1M) >> SECTOR_SHIFT; 1536eb0570c7SDamien Le Moal break; 1537eb0570c7SDamien Le Moal case ZLOOP_OPT_ZONE_CAPACITY: 1538eb0570c7SDamien Le Moal if (match_uint(args, &token)) { 1539eb0570c7SDamien Le Moal ret = -EINVAL; 1540eb0570c7SDamien Le Moal goto out; 1541eb0570c7SDamien Le Moal } 1542eb0570c7SDamien Le Moal if (!token) { 1543eb0570c7SDamien Le Moal pr_err("Invalid zone capacity\n"); 1544eb0570c7SDamien Le Moal ret = -EINVAL; 1545eb0570c7SDamien Le Moal goto out; 1546eb0570c7SDamien Le Moal } 1547eb0570c7SDamien Le Moal opts->zone_capacity = 1548eb0570c7SDamien Le Moal ((sector_t)token * SZ_1M) >> SECTOR_SHIFT; 1549eb0570c7SDamien Le Moal break; 1550eb0570c7SDamien Le Moal case ZLOOP_OPT_NR_CONV_ZONES: 1551eb0570c7SDamien Le Moal if (match_uint(args, &token)) { 1552eb0570c7SDamien Le Moal ret = -EINVAL; 1553eb0570c7SDamien Le Moal goto out; 1554eb0570c7SDamien Le Moal } 1555eb0570c7SDamien Le Moal opts->nr_conv_zones = token; 1556eb0570c7SDamien Le Moal break; 1557b2a78fecSDamien Le Moal case ZLOOP_OPT_MAX_OPEN_ZONES: 1558b2a78fecSDamien Le Moal if (match_uint(args, &token)) { 1559b2a78fecSDamien Le Moal ret = -EINVAL; 1560b2a78fecSDamien Le Moal goto out; 1561b2a78fecSDamien Le Moal } 1562b2a78fecSDamien Le Moal opts->max_open_zones = token; 1563b2a78fecSDamien Le Moal break; 1564eb0570c7SDamien Le Moal case ZLOOP_OPT_BASE_DIR: 1565eb0570c7SDamien Le Moal p = match_strdup(args); 1566eb0570c7SDamien Le Moal if (!p) { 1567eb0570c7SDamien Le Moal ret = -ENOMEM; 1568eb0570c7SDamien Le Moal goto out; 1569eb0570c7SDamien Le Moal } 1570eb0570c7SDamien Le Moal kfree(opts->base_dir); 1571eb0570c7SDamien Le Moal opts->base_dir = p; 1572eb0570c7SDamien Le Moal break; 1573eb0570c7SDamien Le Moal case ZLOOP_OPT_NR_QUEUES: 1574eb0570c7SDamien Le Moal if (match_uint(args, &token)) { 1575eb0570c7SDamien Le Moal ret = -EINVAL; 1576eb0570c7SDamien Le Moal goto out; 1577eb0570c7SDamien Le Moal } 1578eb0570c7SDamien Le Moal if (!token) { 1579eb0570c7SDamien Le Moal pr_err("Invalid number of queues\n"); 1580eb0570c7SDamien Le Moal ret = -EINVAL; 1581eb0570c7SDamien Le Moal goto out; 1582eb0570c7SDamien Le Moal } 1583eb0570c7SDamien Le Moal opts->nr_queues = min(token, num_online_cpus()); 1584eb0570c7SDamien Le Moal break; 1585eb0570c7SDamien Le Moal case ZLOOP_OPT_QUEUE_DEPTH: 1586eb0570c7SDamien Le Moal if (match_uint(args, &token)) { 1587eb0570c7SDamien Le Moal ret = -EINVAL; 1588eb0570c7SDamien Le Moal goto out; 1589eb0570c7SDamien Le Moal } 1590eb0570c7SDamien Le Moal if (!token) { 1591eb0570c7SDamien Le Moal pr_err("Invalid queue depth\n"); 1592eb0570c7SDamien Le Moal ret = -EINVAL; 1593eb0570c7SDamien Le Moal goto out; 1594eb0570c7SDamien Le Moal } 1595eb0570c7SDamien Le Moal opts->queue_depth = token; 1596eb0570c7SDamien Le Moal break; 1597eb0570c7SDamien Le Moal case ZLOOP_OPT_BUFFERED_IO: 1598eb0570c7SDamien Le Moal opts->buffered_io = true; 1599eb0570c7SDamien Le Moal break; 16009236c5fdSDamien Le Moal case ZLOOP_OPT_ZONE_APPEND: 16019236c5fdSDamien Le Moal if (match_uint(args, &token)) { 16029236c5fdSDamien Le Moal ret = -EINVAL; 16039236c5fdSDamien Le Moal goto out; 16049236c5fdSDamien Le Moal } 16059236c5fdSDamien Le Moal if (token != 0 && token != 1) { 16069236c5fdSDamien Le Moal pr_err("Invalid zone_append value\n"); 16079236c5fdSDamien Le Moal ret = -EINVAL; 16089236c5fdSDamien Le Moal goto out; 16099236c5fdSDamien Le Moal } 16109236c5fdSDamien Le Moal opts->zone_append = token; 16119236c5fdSDamien Le Moal break; 1612fcc6eaa3SDamien Le Moal case ZLOOP_OPT_ORDERED_ZONE_APPEND: 1613fcc6eaa3SDamien Le Moal opts->ordered_zone_append = true; 1614fcc6eaa3SDamien Le Moal break; 1615829def1eSChristoph Hellwig case ZLOOP_OPT_DISCARD_WRITE_CACHE: 1616829def1eSChristoph Hellwig opts->discard_write_cache = true; 1617829def1eSChristoph Hellwig break; 1618eb0570c7SDamien Le Moal case ZLOOP_OPT_ERR: 1619eb0570c7SDamien Le Moal default: 1620eb0570c7SDamien Le Moal pr_warn("unknown parameter or missing value '%s'\n", p); 1621eb0570c7SDamien Le Moal ret = -EINVAL; 1622eb0570c7SDamien Le Moal goto out; 1623eb0570c7SDamien Le Moal } 1624eb0570c7SDamien Le Moal } 1625eb0570c7SDamien Le Moal 1626eb0570c7SDamien Le Moal ret = -EINVAL; 1627eb0570c7SDamien Le Moal if (opts->capacity <= opts->zone_size) { 1628eb0570c7SDamien Le Moal pr_err("Invalid capacity\n"); 1629eb0570c7SDamien Le Moal goto out; 1630eb0570c7SDamien Le Moal } 1631eb0570c7SDamien Le Moal 1632eb0570c7SDamien Le Moal if (opts->zone_capacity > opts->zone_size) { 1633eb0570c7SDamien Le Moal pr_err("Invalid zone capacity\n"); 1634eb0570c7SDamien Le Moal goto out; 1635eb0570c7SDamien Le Moal } 1636eb0570c7SDamien Le Moal 1637eb0570c7SDamien Le Moal ret = 0; 1638eb0570c7SDamien Le Moal out: 1639eb0570c7SDamien Le Moal kfree(options); 1640eb0570c7SDamien Le Moal return ret; 1641eb0570c7SDamien Le Moal } 1642eb0570c7SDamien Le Moal 1643eb0570c7SDamien Le Moal enum { 1644eb0570c7SDamien Le Moal ZLOOP_CTL_ADD, 1645eb0570c7SDamien Le Moal ZLOOP_CTL_REMOVE, 1646eb0570c7SDamien Le Moal }; 1647eb0570c7SDamien Le Moal 1648eb0570c7SDamien Le Moal static struct zloop_ctl_op { 1649eb0570c7SDamien Le Moal int code; 1650eb0570c7SDamien Le Moal const char *name; 1651eb0570c7SDamien Le Moal } zloop_ctl_ops[] = { 1652eb0570c7SDamien Le Moal { ZLOOP_CTL_ADD, "add" }, 1653eb0570c7SDamien Le Moal { ZLOOP_CTL_REMOVE, "remove" }, 1654eb0570c7SDamien Le Moal { -1, NULL }, 1655eb0570c7SDamien Le Moal }; 1656eb0570c7SDamien Le Moal 1657eb0570c7SDamien Le Moal static ssize_t zloop_ctl_write(struct file *file, const char __user *ubuf, 1658eb0570c7SDamien Le Moal size_t count, loff_t *pos) 1659eb0570c7SDamien Le Moal { 1660eb0570c7SDamien Le Moal struct zloop_options opts = { }; 1661eb0570c7SDamien Le Moal struct zloop_ctl_op *op; 1662eb0570c7SDamien Le Moal const char *buf, *opts_buf; 1663eb0570c7SDamien Le Moal int i, ret; 1664eb0570c7SDamien Le Moal 1665eb0570c7SDamien Le Moal if (count > PAGE_SIZE) 1666eb0570c7SDamien Le Moal return -ENOMEM; 1667eb0570c7SDamien Le Moal 1668eb0570c7SDamien Le Moal buf = memdup_user_nul(ubuf, count); 1669eb0570c7SDamien Le Moal if (IS_ERR(buf)) 1670eb0570c7SDamien Le Moal return PTR_ERR(buf); 1671eb0570c7SDamien Le Moal 1672eb0570c7SDamien Le Moal for (i = 0; i < ARRAY_SIZE(zloop_ctl_ops); i++) { 1673eb0570c7SDamien Le Moal op = &zloop_ctl_ops[i]; 1674eb0570c7SDamien Le Moal if (!op->name) { 1675eb0570c7SDamien Le Moal pr_err("Invalid operation\n"); 1676eb0570c7SDamien Le Moal ret = -EINVAL; 1677eb0570c7SDamien Le Moal goto out; 1678eb0570c7SDamien Le Moal } 1679eb0570c7SDamien Le Moal if (!strncmp(buf, op->name, strlen(op->name))) 1680eb0570c7SDamien Le Moal break; 1681eb0570c7SDamien Le Moal } 1682eb0570c7SDamien Le Moal 1683eb0570c7SDamien Le Moal if (count <= strlen(op->name)) 1684eb0570c7SDamien Le Moal opts_buf = NULL; 1685eb0570c7SDamien Le Moal else 1686eb0570c7SDamien Le Moal opts_buf = buf + strlen(op->name); 1687eb0570c7SDamien Le Moal 1688eb0570c7SDamien Le Moal ret = zloop_parse_options(&opts, opts_buf); 1689eb0570c7SDamien Le Moal if (ret) { 1690eb0570c7SDamien Le Moal pr_err("Failed to parse options\n"); 1691eb0570c7SDamien Le Moal goto out; 1692eb0570c7SDamien Le Moal } 1693eb0570c7SDamien Le Moal 1694eb0570c7SDamien Le Moal switch (op->code) { 1695eb0570c7SDamien Le Moal case ZLOOP_CTL_ADD: 1696eb0570c7SDamien Le Moal ret = zloop_ctl_add(&opts); 1697eb0570c7SDamien Le Moal break; 1698eb0570c7SDamien Le Moal case ZLOOP_CTL_REMOVE: 1699eb0570c7SDamien Le Moal ret = zloop_ctl_remove(&opts); 1700eb0570c7SDamien Le Moal break; 1701eb0570c7SDamien Le Moal default: 1702eb0570c7SDamien Le Moal pr_err("Invalid operation\n"); 1703eb0570c7SDamien Le Moal ret = -EINVAL; 1704eb0570c7SDamien Le Moal goto out; 1705eb0570c7SDamien Le Moal } 1706eb0570c7SDamien Le Moal 1707eb0570c7SDamien Le Moal out: 1708eb0570c7SDamien Le Moal kfree(opts.base_dir); 1709eb0570c7SDamien Le Moal kfree(buf); 1710eb0570c7SDamien Le Moal return ret ? ret : count; 1711eb0570c7SDamien Le Moal } 1712eb0570c7SDamien Le Moal 1713eb0570c7SDamien Le Moal static int zloop_ctl_show(struct seq_file *seq_file, void *private) 1714eb0570c7SDamien Le Moal { 1715eb0570c7SDamien Le Moal const struct match_token *tok; 1716eb0570c7SDamien Le Moal int i; 1717eb0570c7SDamien Le Moal 1718eb0570c7SDamien Le Moal /* Add operation */ 1719eb0570c7SDamien Le Moal seq_printf(seq_file, "%s ", zloop_ctl_ops[0].name); 1720eb0570c7SDamien Le Moal for (i = 0; i < ARRAY_SIZE(zloop_opt_tokens); i++) { 1721eb0570c7SDamien Le Moal tok = &zloop_opt_tokens[i]; 1722eb0570c7SDamien Le Moal if (!tok->pattern) 1723eb0570c7SDamien Le Moal break; 1724eb0570c7SDamien Le Moal if (i) 1725eb0570c7SDamien Le Moal seq_putc(seq_file, ','); 1726eb0570c7SDamien Le Moal seq_puts(seq_file, tok->pattern); 1727eb0570c7SDamien Le Moal } 1728eb0570c7SDamien Le Moal seq_putc(seq_file, '\n'); 1729eb0570c7SDamien Le Moal 1730eb0570c7SDamien Le Moal /* Remove operation */ 1731eb0570c7SDamien Le Moal seq_puts(seq_file, zloop_ctl_ops[1].name); 1732eb0570c7SDamien Le Moal seq_puts(seq_file, " id=%d\n"); 1733eb0570c7SDamien Le Moal 1734eb0570c7SDamien Le Moal return 0; 1735eb0570c7SDamien Le Moal } 1736eb0570c7SDamien Le Moal 1737eb0570c7SDamien Le Moal static int zloop_ctl_open(struct inode *inode, struct file *file) 1738eb0570c7SDamien Le Moal { 1739eb0570c7SDamien Le Moal file->private_data = NULL; 1740eb0570c7SDamien Le Moal return single_open(file, zloop_ctl_show, NULL); 1741eb0570c7SDamien Le Moal } 1742eb0570c7SDamien Le Moal 1743eb0570c7SDamien Le Moal static int zloop_ctl_release(struct inode *inode, struct file *file) 1744eb0570c7SDamien Le Moal { 1745eb0570c7SDamien Le Moal return single_release(inode, file); 1746eb0570c7SDamien Le Moal } 1747eb0570c7SDamien Le Moal 1748eb0570c7SDamien Le Moal static const struct file_operations zloop_ctl_fops = { 1749eb0570c7SDamien Le Moal .owner = THIS_MODULE, 1750eb0570c7SDamien Le Moal .open = zloop_ctl_open, 1751eb0570c7SDamien Le Moal .release = zloop_ctl_release, 1752eb0570c7SDamien Le Moal .write = zloop_ctl_write, 1753eb0570c7SDamien Le Moal .read = seq_read, 1754eb0570c7SDamien Le Moal }; 1755eb0570c7SDamien Le Moal 1756eb0570c7SDamien Le Moal static struct miscdevice zloop_misc = { 1757eb0570c7SDamien Le Moal .minor = MISC_DYNAMIC_MINOR, 1758eb0570c7SDamien Le Moal .name = "zloop-control", 1759eb0570c7SDamien Le Moal .fops = &zloop_ctl_fops, 1760eb0570c7SDamien Le Moal }; 1761eb0570c7SDamien Le Moal 1762eb0570c7SDamien Le Moal static int __init zloop_init(void) 1763eb0570c7SDamien Le Moal { 1764eb0570c7SDamien Le Moal int ret; 1765eb0570c7SDamien Le Moal 1766eb0570c7SDamien Le Moal ret = misc_register(&zloop_misc); 1767eb0570c7SDamien Le Moal if (ret) { 1768eb0570c7SDamien Le Moal pr_err("Failed to register misc device: %d\n", ret); 1769eb0570c7SDamien Le Moal return ret; 1770eb0570c7SDamien Le Moal } 1771eb0570c7SDamien Le Moal pr_info("Module loaded\n"); 1772eb0570c7SDamien Le Moal 1773eb0570c7SDamien Le Moal return 0; 1774eb0570c7SDamien Le Moal } 1775eb0570c7SDamien Le Moal 1776eb0570c7SDamien Le Moal static void __exit zloop_exit(void) 1777eb0570c7SDamien Le Moal { 1778eb0570c7SDamien Le Moal misc_deregister(&zloop_misc); 1779eb0570c7SDamien Le Moal idr_destroy(&zloop_index_idr); 1780eb0570c7SDamien Le Moal } 1781eb0570c7SDamien Le Moal 1782eb0570c7SDamien Le Moal module_init(zloop_init); 1783eb0570c7SDamien Le Moal module_exit(zloop_exit); 1784eb0570c7SDamien Le Moal 1785eb0570c7SDamien Le Moal MODULE_DESCRIPTION("Zoned loopback device"); 1786eb0570c7SDamien Le Moal MODULE_LICENSE("GPL"); 1787