]>
Commit | Line | Data |
---|---|---|
ca4b2a01 MB |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <linux/vmalloc.h> | |
3 | #include "null_blk.h" | |
4 | ||
766c3297 CK |
5 | #define CREATE_TRACE_POINTS |
6 | #include "null_blk_trace.h" | |
7 | ||
ca4b2a01 MB |
8 | /* zone_size in MBs to sectors. */ |
9 | #define ZONE_SIZE_SHIFT 11 | |
10 | ||
11 | static inline unsigned int null_zone_no(struct nullb_device *dev, sector_t sect) | |
12 | { | |
13 | return sect >> ilog2(dev->zone_size_sects); | |
14 | } | |
15 | ||
d205bde7 | 16 | int null_init_zoned_dev(struct nullb_device *dev, struct request_queue *q) |
ca4b2a01 MB |
17 | { |
18 | sector_t dev_size = (sector_t)dev->size * 1024 * 1024; | |
19 | sector_t sector = 0; | |
20 | unsigned int i; | |
21 | ||
22 | if (!is_power_of_2(dev->zone_size)) { | |
9c7eddf1 | 23 | pr_err("zone_size must be power-of-two\n"); |
ca4b2a01 MB |
24 | return -EINVAL; |
25 | } | |
26 | ||
27 | dev->zone_size_sects = dev->zone_size << ZONE_SIZE_SHIFT; | |
28 | dev->nr_zones = dev_size >> | |
29 | (SECTOR_SHIFT + ilog2(dev->zone_size_sects)); | |
30 | dev->zones = kvmalloc_array(dev->nr_zones, sizeof(struct blk_zone), | |
31 | GFP_KERNEL | __GFP_ZERO); | |
32 | if (!dev->zones) | |
33 | return -ENOMEM; | |
34 | ||
ea2c18e1 MS |
35 | if (dev->zone_nr_conv >= dev->nr_zones) { |
36 | dev->zone_nr_conv = dev->nr_zones - 1; | |
9c7eddf1 | 37 | pr_info("changed the number of conventional zones to %u", |
ea2c18e1 MS |
38 | dev->zone_nr_conv); |
39 | } | |
40 | ||
41 | for (i = 0; i < dev->zone_nr_conv; i++) { | |
42 | struct blk_zone *zone = &dev->zones[i]; | |
43 | ||
44 | zone->start = sector; | |
45 | zone->len = dev->zone_size_sects; | |
46 | zone->wp = zone->start + zone->len; | |
47 | zone->type = BLK_ZONE_TYPE_CONVENTIONAL; | |
48 | zone->cond = BLK_ZONE_COND_NOT_WP; | |
49 | ||
50 | sector += dev->zone_size_sects; | |
51 | } | |
52 | ||
53 | for (i = dev->zone_nr_conv; i < dev->nr_zones; i++) { | |
ca4b2a01 MB |
54 | struct blk_zone *zone = &dev->zones[i]; |
55 | ||
56 | zone->start = zone->wp = sector; | |
57 | zone->len = dev->zone_size_sects; | |
58 | zone->type = BLK_ZONE_TYPE_SEQWRITE_REQ; | |
59 | zone->cond = BLK_ZONE_COND_EMPTY; | |
60 | ||
61 | sector += dev->zone_size_sects; | |
62 | } | |
63 | ||
d205bde7 DLM |
64 | q->limits.zoned = BLK_ZONED_HM; |
65 | blk_queue_flag_set(QUEUE_FLAG_ZONE_RESETALL, q); | |
66 | blk_queue_required_elevator_features(q, ELEVATOR_F_ZBD_SEQ_WRITE); | |
67 | ||
68 | return 0; | |
69 | } | |
70 | ||
71 | int null_register_zoned_dev(struct nullb *nullb) | |
72 | { | |
73 | struct request_queue *q = nullb->q; | |
74 | ||
75 | if (queue_is_mq(q)) | |
76 | return blk_revalidate_disk_zones(nullb->disk); | |
77 | ||
78 | blk_queue_chunk_sectors(q, nullb->dev->zone_size_sects); | |
79 | q->nr_zones = blkdev_nr_zones(nullb->disk); | |
80 | ||
ca4b2a01 MB |
81 | return 0; |
82 | } | |
83 | ||
d205bde7 | 84 | void null_free_zoned_dev(struct nullb_device *dev) |
ca4b2a01 MB |
85 | { |
86 | kvfree(dev->zones); | |
87 | } | |
88 | ||
7fc8fb51 | 89 | int null_report_zones(struct gendisk *disk, sector_t sector, |
d4100351 | 90 | unsigned int nr_zones, report_zones_cb cb, void *data) |
ca4b2a01 | 91 | { |
e76239a3 CH |
92 | struct nullb *nullb = disk->private_data; |
93 | struct nullb_device *dev = nullb->dev; | |
d4100351 CH |
94 | unsigned int first_zone, i; |
95 | struct blk_zone zone; | |
96 | int error; | |
ca4b2a01 | 97 | |
d4100351 CH |
98 | first_zone = null_zone_no(dev, sector); |
99 | if (first_zone >= dev->nr_zones) | |
100 | return 0; | |
ca4b2a01 | 101 | |
d4100351 | 102 | nr_zones = min(nr_zones, dev->nr_zones - first_zone); |
766c3297 CK |
103 | trace_nullb_report_zones(nullb, nr_zones); |
104 | ||
d4100351 CH |
105 | for (i = 0; i < nr_zones; i++) { |
106 | /* | |
107 | * Stacked DM target drivers will remap the zone information by | |
108 | * modifying the zone information passed to the report callback. | |
109 | * So use a local copy to avoid corruption of the device zone | |
110 | * array. | |
111 | */ | |
112 | memcpy(&zone, &dev->zones[first_zone + i], | |
113 | sizeof(struct blk_zone)); | |
114 | error = cb(&zone, i, data); | |
115 | if (error) | |
116 | return error; | |
117 | } | |
ca4b2a01 | 118 | |
d4100351 | 119 | return nr_zones; |
ca4b2a01 MB |
120 | } |
121 | ||
dd85b492 AJ |
122 | size_t null_zone_valid_read_len(struct nullb *nullb, |
123 | sector_t sector, unsigned int len) | |
124 | { | |
125 | struct nullb_device *dev = nullb->dev; | |
126 | struct blk_zone *zone = &dev->zones[null_zone_no(dev, sector)]; | |
127 | unsigned int nr_sectors = len >> SECTOR_SHIFT; | |
128 | ||
129 | /* Read must be below the write pointer position */ | |
130 | if (zone->type == BLK_ZONE_TYPE_CONVENTIONAL || | |
131 | sector + nr_sectors <= zone->wp) | |
132 | return len; | |
133 | ||
134 | if (sector > zone->wp) | |
135 | return 0; | |
136 | ||
137 | return (zone->wp - sector) << SECTOR_SHIFT; | |
138 | } | |
139 | ||
fceb5d1b | 140 | static blk_status_t null_zone_write(struct nullb_cmd *cmd, sector_t sector, |
b228ba1c | 141 | unsigned int nr_sectors) |
ca4b2a01 MB |
142 | { |
143 | struct nullb_device *dev = cmd->nq->dev; | |
ca4b2a01 MB |
144 | unsigned int zno = null_zone_no(dev, sector); |
145 | struct blk_zone *zone = &dev->zones[zno]; | |
9dd44c7e DLM |
146 | blk_status_t ret; |
147 | ||
148 | trace_nullb_zone_op(cmd, zno, zone->cond); | |
149 | ||
150 | if (zone->type == BLK_ZONE_TYPE_CONVENTIONAL) | |
151 | return null_process_cmd(cmd, REQ_OP_WRITE, sector, nr_sectors); | |
ca4b2a01 MB |
152 | |
153 | switch (zone->cond) { | |
154 | case BLK_ZONE_COND_FULL: | |
155 | /* Cannot write to a full zone */ | |
fceb5d1b | 156 | return BLK_STS_IOERR; |
ca4b2a01 MB |
157 | case BLK_ZONE_COND_EMPTY: |
158 | case BLK_ZONE_COND_IMP_OPEN: | |
16c731fe DLM |
159 | case BLK_ZONE_COND_EXP_OPEN: |
160 | case BLK_ZONE_COND_CLOSED: | |
ca4b2a01 | 161 | /* Writes must be at the write pointer position */ |
fceb5d1b CK |
162 | if (sector != zone->wp) |
163 | return BLK_STS_IOERR; | |
ca4b2a01 | 164 | |
16c731fe | 165 | if (zone->cond != BLK_ZONE_COND_EXP_OPEN) |
ca4b2a01 MB |
166 | zone->cond = BLK_ZONE_COND_IMP_OPEN; |
167 | ||
9dd44c7e DLM |
168 | ret = null_process_cmd(cmd, REQ_OP_WRITE, sector, nr_sectors); |
169 | if (ret != BLK_STS_OK) | |
170 | return ret; | |
171 | ||
b228ba1c | 172 | zone->wp += nr_sectors; |
ca4b2a01 MB |
173 | if (zone->wp == zone->start + zone->len) |
174 | zone->cond = BLK_ZONE_COND_FULL; | |
9dd44c7e | 175 | return BLK_STS_OK; |
ca4b2a01 MB |
176 | default: |
177 | /* Invalid zone condition */ | |
fceb5d1b | 178 | return BLK_STS_IOERR; |
ca4b2a01 MB |
179 | } |
180 | } | |
181 | ||
da644b2c AJ |
182 | static blk_status_t null_zone_mgmt(struct nullb_cmd *cmd, enum req_opf op, |
183 | sector_t sector) | |
ca4b2a01 MB |
184 | { |
185 | struct nullb_device *dev = cmd->nq->dev; | |
766c3297 CK |
186 | unsigned int zone_no = null_zone_no(dev, sector); |
187 | struct blk_zone *zone = &dev->zones[zone_no]; | |
a61dbfb1 CK |
188 | size_t i; |
189 | ||
da644b2c | 190 | switch (op) { |
a61dbfb1 CK |
191 | case REQ_OP_ZONE_RESET_ALL: |
192 | for (i = 0; i < dev->nr_zones; i++) { | |
193 | if (zone[i].type == BLK_ZONE_TYPE_CONVENTIONAL) | |
194 | continue; | |
195 | zone[i].cond = BLK_ZONE_COND_EMPTY; | |
196 | zone[i].wp = zone[i].start; | |
197 | } | |
198 | break; | |
199 | case REQ_OP_ZONE_RESET: | |
fceb5d1b CK |
200 | if (zone->type == BLK_ZONE_TYPE_CONVENTIONAL) |
201 | return BLK_STS_IOERR; | |
ca4b2a01 | 202 | |
a61dbfb1 CK |
203 | zone->cond = BLK_ZONE_COND_EMPTY; |
204 | zone->wp = zone->start; | |
205 | break; | |
da644b2c AJ |
206 | case REQ_OP_ZONE_OPEN: |
207 | if (zone->type == BLK_ZONE_TYPE_CONVENTIONAL) | |
208 | return BLK_STS_IOERR; | |
209 | if (zone->cond == BLK_ZONE_COND_FULL) | |
210 | return BLK_STS_IOERR; | |
211 | ||
212 | zone->cond = BLK_ZONE_COND_EXP_OPEN; | |
213 | break; | |
214 | case REQ_OP_ZONE_CLOSE: | |
215 | if (zone->type == BLK_ZONE_TYPE_CONVENTIONAL) | |
216 | return BLK_STS_IOERR; | |
217 | if (zone->cond == BLK_ZONE_COND_FULL) | |
218 | return BLK_STS_IOERR; | |
219 | ||
c7d776f8 DLM |
220 | if (zone->wp == zone->start) |
221 | zone->cond = BLK_ZONE_COND_EMPTY; | |
222 | else | |
223 | zone->cond = BLK_ZONE_COND_CLOSED; | |
da644b2c AJ |
224 | break; |
225 | case REQ_OP_ZONE_FINISH: | |
226 | if (zone->type == BLK_ZONE_TYPE_CONVENTIONAL) | |
227 | return BLK_STS_IOERR; | |
228 | ||
229 | zone->cond = BLK_ZONE_COND_FULL; | |
230 | zone->wp = zone->start + zone->len; | |
231 | break; | |
a61dbfb1 | 232 | default: |
79a85e21 | 233 | return BLK_STS_NOTSUPP; |
ea2c18e1 | 234 | } |
766c3297 CK |
235 | |
236 | trace_nullb_zone_op(cmd, zone_no, zone->cond); | |
fceb5d1b CK |
237 | return BLK_STS_OK; |
238 | } | |
239 | ||
9dd44c7e DLM |
240 | blk_status_t null_process_zoned_cmd(struct nullb_cmd *cmd, enum req_opf op, |
241 | sector_t sector, sector_t nr_sectors) | |
fceb5d1b CK |
242 | { |
243 | switch (op) { | |
244 | case REQ_OP_WRITE: | |
245 | return null_zone_write(cmd, sector, nr_sectors); | |
246 | case REQ_OP_ZONE_RESET: | |
247 | case REQ_OP_ZONE_RESET_ALL: | |
da644b2c AJ |
248 | case REQ_OP_ZONE_OPEN: |
249 | case REQ_OP_ZONE_CLOSE: | |
250 | case REQ_OP_ZONE_FINISH: | |
251 | return null_zone_mgmt(cmd, op, sector); | |
fceb5d1b | 252 | default: |
9dd44c7e | 253 | return null_process_cmd(cmd, op, sector, nr_sectors); |
fceb5d1b | 254 | } |
ca4b2a01 | 255 | } |