linux/drivers/block/null_blk_zoned.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2#include <linux/vmalloc.h>
   3#include "null_blk.h"
   4
   5/* zone_size in MBs to sectors. */
   6#define ZONE_SIZE_SHIFT         11
   7
   8static inline unsigned int null_zone_no(struct nullb_device *dev, sector_t sect)
   9{
  10        return sect >> ilog2(dev->zone_size_sects);
  11}
  12
  13int null_zone_init(struct nullb_device *dev)
  14{
  15        sector_t dev_size = (sector_t)dev->size * 1024 * 1024;
  16        sector_t sector = 0;
  17        unsigned int i;
  18
  19        if (!is_power_of_2(dev->zone_size)) {
  20                pr_err("zone_size must be power-of-two\n");
  21                return -EINVAL;
  22        }
  23
  24        dev->zone_size_sects = dev->zone_size << ZONE_SIZE_SHIFT;
  25        dev->nr_zones = dev_size >>
  26                                (SECTOR_SHIFT + ilog2(dev->zone_size_sects));
  27        dev->zones = kvmalloc_array(dev->nr_zones, sizeof(struct blk_zone),
  28                        GFP_KERNEL | __GFP_ZERO);
  29        if (!dev->zones)
  30                return -ENOMEM;
  31
  32        if (dev->zone_nr_conv >= dev->nr_zones) {
  33                dev->zone_nr_conv = dev->nr_zones - 1;
  34                pr_info("changed the number of conventional zones to %u",
  35                        dev->zone_nr_conv);
  36        }
  37
  38        for (i = 0; i <  dev->zone_nr_conv; i++) {
  39                struct blk_zone *zone = &dev->zones[i];
  40
  41                zone->start = sector;
  42                zone->len = dev->zone_size_sects;
  43                zone->wp = zone->start + zone->len;
  44                zone->type = BLK_ZONE_TYPE_CONVENTIONAL;
  45                zone->cond = BLK_ZONE_COND_NOT_WP;
  46
  47                sector += dev->zone_size_sects;
  48        }
  49
  50        for (i = dev->zone_nr_conv; i < dev->nr_zones; i++) {
  51                struct blk_zone *zone = &dev->zones[i];
  52
  53                zone->start = zone->wp = sector;
  54                zone->len = dev->zone_size_sects;
  55                zone->type = BLK_ZONE_TYPE_SEQWRITE_REQ;
  56                zone->cond = BLK_ZONE_COND_EMPTY;
  57
  58                sector += dev->zone_size_sects;
  59        }
  60
  61        return 0;
  62}
  63
  64void null_zone_exit(struct nullb_device *dev)
  65{
  66        kvfree(dev->zones);
  67}
  68
  69int null_zone_report(struct gendisk *disk, sector_t sector,
  70                     struct blk_zone *zones, unsigned int *nr_zones)
  71{
  72        struct nullb *nullb = disk->private_data;
  73        struct nullb_device *dev = nullb->dev;
  74        unsigned int zno, nrz = 0;
  75
  76        zno = null_zone_no(dev, sector);
  77        if (zno < dev->nr_zones) {
  78                nrz = min_t(unsigned int, *nr_zones, dev->nr_zones - zno);
  79                memcpy(zones, &dev->zones[zno], nrz * sizeof(struct blk_zone));
  80        }
  81
  82        *nr_zones = nrz;
  83
  84        return 0;
  85}
  86
  87static blk_status_t null_zone_write(struct nullb_cmd *cmd, sector_t sector,
  88                     unsigned int nr_sectors)
  89{
  90        struct nullb_device *dev = cmd->nq->dev;
  91        unsigned int zno = null_zone_no(dev, sector);
  92        struct blk_zone *zone = &dev->zones[zno];
  93
  94        switch (zone->cond) {
  95        case BLK_ZONE_COND_FULL:
  96                /* Cannot write to a full zone */
  97                cmd->error = BLK_STS_IOERR;
  98                return BLK_STS_IOERR;
  99        case BLK_ZONE_COND_EMPTY:
 100        case BLK_ZONE_COND_IMP_OPEN:
 101                /* Writes must be at the write pointer position */
 102                if (sector != zone->wp)
 103                        return BLK_STS_IOERR;
 104
 105                if (zone->cond == BLK_ZONE_COND_EMPTY)
 106                        zone->cond = BLK_ZONE_COND_IMP_OPEN;
 107
 108                zone->wp += nr_sectors;
 109                if (zone->wp == zone->start + zone->len)
 110                        zone->cond = BLK_ZONE_COND_FULL;
 111                break;
 112        case BLK_ZONE_COND_NOT_WP:
 113                break;
 114        default:
 115                /* Invalid zone condition */
 116                return BLK_STS_IOERR;
 117        }
 118        return BLK_STS_OK;
 119}
 120
 121static blk_status_t null_zone_reset(struct nullb_cmd *cmd, sector_t sector)
 122{
 123        struct nullb_device *dev = cmd->nq->dev;
 124        unsigned int zno = null_zone_no(dev, sector);
 125        struct blk_zone *zone = &dev->zones[zno];
 126        size_t i;
 127
 128        switch (req_op(cmd->rq)) {
 129        case REQ_OP_ZONE_RESET_ALL:
 130                for (i = 0; i < dev->nr_zones; i++) {
 131                        if (zone[i].type == BLK_ZONE_TYPE_CONVENTIONAL)
 132                                continue;
 133                        zone[i].cond = BLK_ZONE_COND_EMPTY;
 134                        zone[i].wp = zone[i].start;
 135                }
 136                break;
 137        case REQ_OP_ZONE_RESET:
 138                if (zone->type == BLK_ZONE_TYPE_CONVENTIONAL)
 139                        return BLK_STS_IOERR;
 140
 141                zone->cond = BLK_ZONE_COND_EMPTY;
 142                zone->wp = zone->start;
 143                break;
 144        default:
 145                return BLK_STS_NOTSUPP;
 146        }
 147        return BLK_STS_OK;
 148}
 149
 150blk_status_t null_handle_zoned(struct nullb_cmd *cmd, enum req_opf op,
 151                               sector_t sector, sector_t nr_sectors)
 152{
 153        switch (op) {
 154        case REQ_OP_WRITE:
 155                return null_zone_write(cmd, sector, nr_sectors);
 156        case REQ_OP_ZONE_RESET:
 157        case REQ_OP_ZONE_RESET_ALL:
 158                return null_zone_reset(cmd, sector);
 159        default:
 160                return BLK_STS_OK;
 161        }
 162}
 163