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