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