GNU Linux-libre 4.19.281-gnu1
[releases.git] / drivers / block / null_blk_zoned.c
1 // SPDX-License-Identifier: GPL-2.0
2 #include <linux/vmalloc.h>
3 #include <linux/sizes.h>
4 #include "null_blk.h"
5
6 #define MB_TO_SECTS(mb) (((sector_t)mb * SZ_1M) >> SECTOR_SHIFT)
7
8 static inline unsigned int null_zone_no(struct nullb_device *dev, sector_t sect)
9 {
10         return sect >> ilog2(dev->zone_size_sects);
11 }
12
13 int null_zone_init(struct nullb_device *dev)
14 {
15         sector_t dev_capacity_sects;
16         sector_t sector = 0;
17         unsigned int i;
18
19         if (!is_power_of_2(dev->zone_size)) {
20                 pr_err("null_blk: zone_size must be power-of-two\n");
21                 return -EINVAL;
22         }
23         if (dev->zone_size > dev->size) {
24                 pr_err("Zone size larger than device capacity\n");
25                 return -EINVAL;
26         }
27
28         dev_capacity_sects = MB_TO_SECTS(dev->size);
29         dev->zone_size_sects = MB_TO_SECTS(dev->zone_size);
30         dev->nr_zones = dev_capacity_sects >> ilog2(dev->zone_size_sects);
31         if (dev_capacity_sects & (dev->zone_size_sects - 1))
32                 dev->nr_zones++;
33
34         dev->zones = kvmalloc_array(dev->nr_zones, sizeof(struct blk_zone),
35                         GFP_KERNEL | __GFP_ZERO);
36         if (!dev->zones)
37                 return -ENOMEM;
38
39         for (i = 0; i < dev->nr_zones; i++) {
40                 struct blk_zone *zone = &dev->zones[i];
41
42                 zone->start = zone->wp = sector;
43                 if (zone->start + dev->zone_size_sects > dev_capacity_sects)
44                         zone->len = dev_capacity_sects - zone->start;
45                 else
46                         zone->len = dev->zone_size_sects;
47                 zone->type = BLK_ZONE_TYPE_SEQWRITE_REQ;
48                 zone->cond = BLK_ZONE_COND_EMPTY;
49
50                 sector += dev->zone_size_sects;
51         }
52
53         return 0;
54 }
55
56 void null_zone_exit(struct nullb_device *dev)
57 {
58         kvfree(dev->zones);
59         dev->zones = NULL;
60 }
61
62 static void null_zone_fill_bio(struct nullb_device *dev, struct bio *bio,
63                                unsigned int zno, unsigned int nr_zones)
64 {
65         struct blk_zone_report_hdr *hdr = NULL;
66         struct bio_vec bvec;
67         struct bvec_iter iter;
68         void *addr;
69         unsigned int zones_to_cpy;
70
71         bio_for_each_segment(bvec, bio, iter) {
72                 addr = kmap_atomic(bvec.bv_page);
73
74                 zones_to_cpy = bvec.bv_len / sizeof(struct blk_zone);
75
76                 if (!hdr) {
77                         hdr = (struct blk_zone_report_hdr *)addr;
78                         hdr->nr_zones = nr_zones;
79                         zones_to_cpy--;
80                         addr += sizeof(struct blk_zone_report_hdr);
81                 }
82
83                 zones_to_cpy = min_t(unsigned int, zones_to_cpy, nr_zones);
84
85                 memcpy(addr, &dev->zones[zno],
86                                 zones_to_cpy * sizeof(struct blk_zone));
87
88                 kunmap_atomic(addr);
89
90                 nr_zones -= zones_to_cpy;
91                 zno += zones_to_cpy;
92
93                 if (!nr_zones)
94                         break;
95         }
96 }
97
98 blk_status_t null_zone_report(struct nullb *nullb, struct bio *bio)
99 {
100         struct nullb_device *dev = nullb->dev;
101         unsigned int zno = null_zone_no(dev, bio->bi_iter.bi_sector);
102         unsigned int nr_zones = dev->nr_zones - zno;
103         unsigned int max_zones;
104
105         max_zones = (bio->bi_iter.bi_size / sizeof(struct blk_zone)) - 1;
106         nr_zones = min_t(unsigned int, nr_zones, max_zones);
107         null_zone_fill_bio(nullb->dev, bio, zno, nr_zones);
108
109         return BLK_STS_OK;
110 }
111
112 void null_zone_write(struct nullb_cmd *cmd, sector_t sector,
113                      unsigned int nr_sectors)
114 {
115         struct nullb_device *dev = cmd->nq->dev;
116         unsigned int zno = null_zone_no(dev, sector);
117         struct blk_zone *zone = &dev->zones[zno];
118
119         switch (zone->cond) {
120         case BLK_ZONE_COND_FULL:
121                 /* Cannot write to a full zone */
122                 cmd->error = BLK_STS_IOERR;
123                 break;
124         case BLK_ZONE_COND_EMPTY:
125         case BLK_ZONE_COND_IMP_OPEN:
126                 /* Writes must be at the write pointer position */
127                 if (sector != zone->wp) {
128                         cmd->error = BLK_STS_IOERR;
129                         break;
130                 }
131
132                 if (zone->cond == BLK_ZONE_COND_EMPTY)
133                         zone->cond = BLK_ZONE_COND_IMP_OPEN;
134
135                 zone->wp += nr_sectors;
136                 if (zone->wp == zone->start + zone->len)
137                         zone->cond = BLK_ZONE_COND_FULL;
138                 break;
139         default:
140                 /* Invalid zone condition */
141                 cmd->error = BLK_STS_IOERR;
142                 break;
143         }
144 }
145
146 void null_zone_reset(struct nullb_cmd *cmd, sector_t sector)
147 {
148         struct nullb_device *dev = cmd->nq->dev;
149         unsigned int zno = null_zone_no(dev, sector);
150         struct blk_zone *zone = &dev->zones[zno];
151
152         zone->cond = BLK_ZONE_COND_EMPTY;
153         zone->wp = zone->start;
154 }