Commit | Line | Data |
---|---|---|
ca4b2a01 MB |
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 | ||
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_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("null_blk: 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 | ||
ea2c18e1 MS |
32 | if (dev->zone_nr_conv >= dev->nr_zones) { |
33 | dev->zone_nr_conv = dev->nr_zones - 1; | |
34 | pr_info("null_blk: 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++) { | |
ca4b2a01 MB |
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 | ||
64 | void null_zone_exit(struct nullb_device *dev) | |
65 | { | |
66 | kvfree(dev->zones); | |
67 | } | |
68 | ||
e76239a3 | 69 | int null_zone_report(struct gendisk *disk, sector_t sector, |
bd976e52 | 70 | struct blk_zone *zones, unsigned int *nr_zones) |
ca4b2a01 | 71 | { |
e76239a3 CH |
72 | struct nullb *nullb = disk->private_data; |
73 | struct nullb_device *dev = nullb->dev; | |
74 | unsigned int zno, nrz = 0; | |
ca4b2a01 | 75 | |
e76239a3 CH |
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)); | |
ca4b2a01 | 80 | } |
ca4b2a01 | 81 | |
e76239a3 | 82 | *nr_zones = nrz; |
ca4b2a01 | 83 | |
e76239a3 | 84 | return 0; |
ca4b2a01 MB |
85 | } |
86 | ||
b228ba1c JA |
87 | void null_zone_write(struct nullb_cmd *cmd, sector_t sector, |
88 | unsigned int nr_sectors) | |
ca4b2a01 MB |
89 | { |
90 | struct nullb_device *dev = cmd->nq->dev; | |
ca4b2a01 MB |
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 | break; | |
99 | case BLK_ZONE_COND_EMPTY: | |
100 | case BLK_ZONE_COND_IMP_OPEN: | |
101 | /* Writes must be at the write pointer position */ | |
b228ba1c | 102 | if (sector != zone->wp) { |
ca4b2a01 MB |
103 | cmd->error = BLK_STS_IOERR; |
104 | break; | |
105 | } | |
106 | ||
107 | if (zone->cond == BLK_ZONE_COND_EMPTY) | |
108 | zone->cond = BLK_ZONE_COND_IMP_OPEN; | |
109 | ||
b228ba1c | 110 | zone->wp += nr_sectors; |
ca4b2a01 MB |
111 | if (zone->wp == zone->start + zone->len) |
112 | zone->cond = BLK_ZONE_COND_FULL; | |
113 | break; | |
ea2c18e1 MS |
114 | case BLK_ZONE_COND_NOT_WP: |
115 | break; | |
ca4b2a01 MB |
116 | default: |
117 | /* Invalid zone condition */ | |
118 | cmd->error = BLK_STS_IOERR; | |
119 | break; | |
120 | } | |
121 | } | |
122 | ||
b228ba1c | 123 | void null_zone_reset(struct nullb_cmd *cmd, sector_t sector) |
ca4b2a01 MB |
124 | { |
125 | struct nullb_device *dev = cmd->nq->dev; | |
b228ba1c | 126 | unsigned int zno = null_zone_no(dev, sector); |
ca4b2a01 MB |
127 | struct blk_zone *zone = &dev->zones[zno]; |
128 | ||
ea2c18e1 MS |
129 | if (zone->type == BLK_ZONE_TYPE_CONVENTIONAL) { |
130 | cmd->error = BLK_STS_IOERR; | |
131 | return; | |
132 | } | |
133 | ||
ca4b2a01 MB |
134 | zone->cond = BLK_ZONE_COND_EMPTY; |
135 | zone->wp = zone->start; | |
136 | } |