Commit | Line | Data |
---|---|---|
a98c5b19 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
89d94756 HR |
2 | /* |
3 | * SCSI Zoned Block commands | |
4 | * | |
5 | * Copyright (C) 2014-2015 SUSE Linux GmbH | |
6 | * Written by: Hannes Reinecke <hare@suse.de> | |
7 | * Modified by: Damien Le Moal <damien.lemoal@hgst.com> | |
8 | * Modified by: Shaun Tancheff <shaun.tancheff@seagate.com> | |
89d94756 HR |
9 | */ |
10 | ||
11 | #include <linux/blkdev.h> | |
b091ac61 DLM |
12 | #include <linux/vmalloc.h> |
13 | #include <linux/sched/mm.h> | |
5795eb44 | 14 | #include <linux/mutex.h> |
89d94756 HR |
15 | |
16 | #include <asm/unaligned.h> | |
17 | ||
18 | #include <scsi/scsi.h> | |
19 | #include <scsi/scsi_cmnd.h> | |
89d94756 HR |
20 | |
21 | #include "sd.h" | |
89d94756 | 22 | |
5795eb44 JT |
23 | static unsigned int sd_zbc_get_zone_wp_offset(struct blk_zone *zone) |
24 | { | |
25 | if (zone->type == ZBC_ZONE_TYPE_CONV) | |
26 | return 0; | |
27 | ||
28 | switch (zone->cond) { | |
29 | case BLK_ZONE_COND_IMP_OPEN: | |
30 | case BLK_ZONE_COND_EXP_OPEN: | |
31 | case BLK_ZONE_COND_CLOSED: | |
32 | return zone->wp - zone->start; | |
33 | case BLK_ZONE_COND_FULL: | |
34 | return zone->len; | |
35 | case BLK_ZONE_COND_EMPTY: | |
36 | case BLK_ZONE_COND_OFFLINE: | |
37 | case BLK_ZONE_COND_READONLY: | |
38 | default: | |
39 | /* | |
40 | * Offline and read-only zones do not have a valid | |
41 | * write pointer. Use 0 as for an empty zone. | |
42 | */ | |
43 | return 0; | |
44 | } | |
45 | } | |
46 | ||
d4100351 CH |
47 | static int sd_zbc_parse_report(struct scsi_disk *sdkp, u8 *buf, |
48 | unsigned int idx, report_zones_cb cb, void *data) | |
89d94756 HR |
49 | { |
50 | struct scsi_device *sdp = sdkp->device; | |
d4100351 | 51 | struct blk_zone zone = { 0 }; |
5795eb44 | 52 | int ret; |
89d94756 | 53 | |
d4100351 CH |
54 | zone.type = buf[0] & 0x0f; |
55 | zone.cond = (buf[1] >> 4) & 0xf; | |
89d94756 | 56 | if (buf[1] & 0x01) |
d4100351 | 57 | zone.reset = 1; |
89d94756 | 58 | if (buf[1] & 0x02) |
d4100351 CH |
59 | zone.non_seq = 1; |
60 | ||
61 | zone.len = logical_to_sectors(sdp, get_unaligned_be64(&buf[8])); | |
82394db7 | 62 | zone.capacity = zone.len; |
d4100351 CH |
63 | zone.start = logical_to_sectors(sdp, get_unaligned_be64(&buf[16])); |
64 | zone.wp = logical_to_sectors(sdp, get_unaligned_be64(&buf[24])); | |
65 | if (zone.type != ZBC_ZONE_TYPE_CONV && | |
66 | zone.cond == ZBC_ZONE_COND_FULL) | |
67 | zone.wp = zone.start + zone.len; | |
68 | ||
5795eb44 JT |
69 | ret = cb(&zone, idx, data); |
70 | if (ret) | |
71 | return ret; | |
72 | ||
73 | if (sdkp->rev_wp_offset) | |
74 | sdkp->rev_wp_offset[idx] = sd_zbc_get_zone_wp_offset(&zone); | |
75 | ||
76 | return 0; | |
89d94756 HR |
77 | } |
78 | ||
79 | /** | |
e76239a3 | 80 | * sd_zbc_do_report_zones - Issue a REPORT ZONES scsi command. |
e98f42bc | 81 | * @sdkp: The target disk |
b091ac61 | 82 | * @buf: vmalloc-ed buffer to use for the reply |
e98f42bc DLM |
83 | * @buflen: the buffer size |
84 | * @lba: Start LBA of the report | |
d2e428e4 | 85 | * @partial: Do partial report |
e98f42bc DLM |
86 | * |
87 | * For internal use during device validation. | |
d2e428e4 DLM |
88 | * Using partial=true can significantly speed up execution of a report zones |
89 | * command because the disk does not have to count all possible report matching | |
90 | * zones and will only report the count of zones fitting in the command reply | |
91 | * buffer. | |
89d94756 | 92 | */ |
e76239a3 CH |
93 | static int sd_zbc_do_report_zones(struct scsi_disk *sdkp, unsigned char *buf, |
94 | unsigned int buflen, sector_t lba, | |
95 | bool partial) | |
89d94756 HR |
96 | { |
97 | struct scsi_device *sdp = sdkp->device; | |
98 | const int timeout = sdp->request_queue->rq_timeout; | |
99 | struct scsi_sense_hdr sshdr; | |
100 | unsigned char cmd[16]; | |
101 | unsigned int rep_len; | |
102 | int result; | |
103 | ||
104 | memset(cmd, 0, 16); | |
105 | cmd[0] = ZBC_IN; | |
106 | cmd[1] = ZI_REPORT_ZONES; | |
107 | put_unaligned_be64(lba, &cmd[2]); | |
108 | put_unaligned_be32(buflen, &cmd[10]); | |
d2e428e4 DLM |
109 | if (partial) |
110 | cmd[14] = ZBC_REPORT_ZONE_PARTIAL; | |
89d94756 HR |
111 | |
112 | result = scsi_execute_req(sdp, cmd, DMA_FROM_DEVICE, | |
113 | buf, buflen, &sshdr, | |
114 | timeout, SD_MAX_RETRIES, NULL); | |
115 | if (result) { | |
116 | sd_printk(KERN_ERR, sdkp, | |
a35989a0 DLM |
117 | "REPORT ZONES start lba %llu failed\n", lba); |
118 | sd_print_result(sdkp, "REPORT ZONES", result); | |
119 | if (driver_byte(result) == DRIVER_SENSE && | |
120 | scsi_sense_valid(&sshdr)) | |
121 | sd_print_sense_hdr(sdkp, &sshdr); | |
89d94756 HR |
122 | return -EIO; |
123 | } | |
124 | ||
125 | rep_len = get_unaligned_be32(&buf[0]); | |
126 | if (rep_len < 64) { | |
127 | sd_printk(KERN_ERR, sdkp, | |
128 | "REPORT ZONES report invalid length %u\n", | |
129 | rep_len); | |
130 | return -EIO; | |
131 | } | |
132 | ||
133 | return 0; | |
134 | } | |
135 | ||
b091ac61 DLM |
136 | /** |
137 | * Allocate a buffer for report zones reply. | |
138 | * @sdkp: The target disk | |
139 | * @nr_zones: Maximum number of zones to report | |
140 | * @buflen: Size of the buffer allocated | |
141 | * | |
142 | * Try to allocate a reply buffer for the number of requested zones. | |
143 | * The size of the buffer allocated may be smaller than requested to | |
144 | * satify the device constraint (max_hw_sectors, max_segments, etc). | |
145 | * | |
146 | * Return the address of the allocated buffer and update @buflen with | |
147 | * the size of the allocated buffer. | |
148 | */ | |
149 | static void *sd_zbc_alloc_report_buffer(struct scsi_disk *sdkp, | |
150 | unsigned int nr_zones, size_t *buflen) | |
151 | { | |
152 | struct request_queue *q = sdkp->disk->queue; | |
153 | size_t bufsize; | |
154 | void *buf; | |
155 | ||
156 | /* | |
157 | * Report zone buffer size should be at most 64B times the number of | |
158 | * zones requested plus the 64B reply header, but should be at least | |
159 | * SECTOR_SIZE for ATA devices. | |
160 | * Make sure that this size does not exceed the hardware capabilities. | |
161 | * Furthermore, since the report zone command cannot be split, make | |
162 | * sure that the allocated buffer can always be mapped by limiting the | |
163 | * number of pages allocated to the HBA max segments limit. | |
164 | */ | |
23a50861 DLM |
165 | nr_zones = min(nr_zones, sdkp->nr_zones); |
166 | bufsize = roundup((nr_zones + 1) * 64, SECTOR_SIZE); | |
b091ac61 DLM |
167 | bufsize = min_t(size_t, bufsize, |
168 | queue_max_hw_sectors(q) << SECTOR_SHIFT); | |
169 | bufsize = min_t(size_t, bufsize, queue_max_segments(q) << PAGE_SHIFT); | |
170 | ||
23a50861 DLM |
171 | while (bufsize >= SECTOR_SIZE) { |
172 | buf = __vmalloc(bufsize, | |
88dca4ca | 173 | GFP_KERNEL | __GFP_ZERO | __GFP_NORETRY); |
23a50861 DLM |
174 | if (buf) { |
175 | *buflen = bufsize; | |
176 | return buf; | |
177 | } | |
178 | bufsize >>= 1; | |
179 | } | |
b091ac61 | 180 | |
23a50861 | 181 | return NULL; |
b091ac61 DLM |
182 | } |
183 | ||
e98f42bc | 184 | /** |
d4100351 CH |
185 | * sd_zbc_zone_sectors - Get the device zone size in number of 512B sectors. |
186 | * @sdkp: The target disk | |
e98f42bc | 187 | */ |
d4100351 CH |
188 | static inline sector_t sd_zbc_zone_sectors(struct scsi_disk *sdkp) |
189 | { | |
190 | return logical_to_sectors(sdkp->device, sdkp->zone_blocks); | |
191 | } | |
192 | ||
e76239a3 | 193 | int sd_zbc_report_zones(struct gendisk *disk, sector_t sector, |
d4100351 | 194 | unsigned int nr_zones, report_zones_cb cb, void *data) |
89d94756 | 195 | { |
e76239a3 | 196 | struct scsi_disk *sdkp = scsi_disk(disk); |
51fdaa04 | 197 | sector_t capacity = logical_to_sectors(sdkp->device, sdkp->capacity); |
d4100351 | 198 | unsigned int nr, i; |
e76239a3 | 199 | unsigned char *buf; |
d4100351 CH |
200 | size_t offset, buflen = 0; |
201 | int zone_idx = 0; | |
202 | int ret; | |
89d94756 HR |
203 | |
204 | if (!sd_is_zoned(sdkp)) | |
205 | /* Not a zoned device */ | |
e76239a3 | 206 | return -EOPNOTSUPP; |
89d94756 | 207 | |
51fdaa04 DLM |
208 | if (!capacity) |
209 | /* Device gone or invalid */ | |
210 | return -ENODEV; | |
211 | ||
d4100351 | 212 | buf = sd_zbc_alloc_report_buffer(sdkp, nr_zones, &buflen); |
e76239a3 CH |
213 | if (!buf) |
214 | return -ENOMEM; | |
89d94756 | 215 | |
51fdaa04 | 216 | while (zone_idx < nr_zones && sector < capacity) { |
d4100351 CH |
217 | ret = sd_zbc_do_report_zones(sdkp, buf, buflen, |
218 | sectors_to_logical(sdkp->device, sector), true); | |
219 | if (ret) | |
220 | goto out; | |
221 | ||
222 | offset = 0; | |
223 | nr = min(nr_zones, get_unaligned_be32(&buf[0]) / 64); | |
224 | if (!nr) | |
225 | break; | |
226 | ||
227 | for (i = 0; i < nr && zone_idx < nr_zones; i++) { | |
228 | offset += 64; | |
229 | ret = sd_zbc_parse_report(sdkp, buf + offset, zone_idx, | |
230 | cb, data); | |
231 | if (ret) | |
232 | goto out; | |
233 | zone_idx++; | |
234 | } | |
89d94756 | 235 | |
d4100351 | 236 | sector += sd_zbc_zone_sectors(sdkp) * i; |
e76239a3 | 237 | } |
89d94756 | 238 | |
d4100351 | 239 | ret = zone_idx; |
b091ac61 DLM |
240 | out: |
241 | kvfree(buf); | |
e76239a3 | 242 | return ret; |
89d94756 HR |
243 | } |
244 | ||
02494d35 JT |
245 | static blk_status_t sd_zbc_cmnd_checks(struct scsi_cmnd *cmd) |
246 | { | |
247 | struct request *rq = cmd->request; | |
248 | struct scsi_disk *sdkp = scsi_disk(rq->rq_disk); | |
249 | sector_t sector = blk_rq_pos(rq); | |
250 | ||
251 | if (!sd_is_zoned(sdkp)) | |
252 | /* Not a zoned device */ | |
253 | return BLK_STS_IOERR; | |
254 | ||
255 | if (sdkp->device->changed) | |
256 | return BLK_STS_IOERR; | |
257 | ||
258 | if (sector & (sd_zbc_zone_sectors(sdkp) - 1)) | |
259 | /* Unaligned request */ | |
260 | return BLK_STS_IOERR; | |
261 | ||
262 | return BLK_STS_OK; | |
263 | } | |
264 | ||
5795eb44 JT |
265 | #define SD_ZBC_INVALID_WP_OFST (~0u) |
266 | #define SD_ZBC_UPDATING_WP_OFST (SD_ZBC_INVALID_WP_OFST - 1) | |
267 | ||
268 | static int sd_zbc_update_wp_offset_cb(struct blk_zone *zone, unsigned int idx, | |
269 | void *data) | |
270 | { | |
271 | struct scsi_disk *sdkp = data; | |
272 | ||
273 | lockdep_assert_held(&sdkp->zones_wp_offset_lock); | |
274 | ||
275 | sdkp->zones_wp_offset[idx] = sd_zbc_get_zone_wp_offset(zone); | |
276 | ||
277 | return 0; | |
278 | } | |
279 | ||
280 | static void sd_zbc_update_wp_offset_workfn(struct work_struct *work) | |
281 | { | |
282 | struct scsi_disk *sdkp; | |
283 | unsigned int zno; | |
284 | int ret; | |
285 | ||
286 | sdkp = container_of(work, struct scsi_disk, zone_wp_offset_work); | |
287 | ||
288 | spin_lock_bh(&sdkp->zones_wp_offset_lock); | |
289 | for (zno = 0; zno < sdkp->nr_zones; zno++) { | |
290 | if (sdkp->zones_wp_offset[zno] != SD_ZBC_UPDATING_WP_OFST) | |
291 | continue; | |
292 | ||
293 | spin_unlock_bh(&sdkp->zones_wp_offset_lock); | |
294 | ret = sd_zbc_do_report_zones(sdkp, sdkp->zone_wp_update_buf, | |
295 | SD_BUF_SIZE, | |
296 | zno * sdkp->zone_blocks, true); | |
297 | spin_lock_bh(&sdkp->zones_wp_offset_lock); | |
298 | if (!ret) | |
299 | sd_zbc_parse_report(sdkp, sdkp->zone_wp_update_buf + 64, | |
300 | zno, sd_zbc_update_wp_offset_cb, | |
301 | sdkp); | |
302 | } | |
303 | spin_unlock_bh(&sdkp->zones_wp_offset_lock); | |
304 | ||
305 | scsi_device_put(sdkp->device); | |
306 | } | |
307 | ||
308 | /** | |
309 | * sd_zbc_prepare_zone_append() - Prepare an emulated ZONE_APPEND command. | |
310 | * @cmd: the command to setup | |
311 | * @lba: the LBA to patch | |
312 | * @nr_blocks: the number of LBAs to be written | |
313 | * | |
314 | * Called from sd_setup_read_write_cmnd() for REQ_OP_ZONE_APPEND. | |
315 | * @sd_zbc_prepare_zone_append() handles the necessary zone wrote locking and | |
316 | * patching of the lba for an emulated ZONE_APPEND command. | |
317 | * | |
318 | * In case the cached write pointer offset is %SD_ZBC_INVALID_WP_OFST it will | |
319 | * schedule a REPORT ZONES command and return BLK_STS_IOERR. | |
320 | */ | |
321 | blk_status_t sd_zbc_prepare_zone_append(struct scsi_cmnd *cmd, sector_t *lba, | |
322 | unsigned int nr_blocks) | |
323 | { | |
324 | struct request *rq = cmd->request; | |
325 | struct scsi_disk *sdkp = scsi_disk(rq->rq_disk); | |
326 | unsigned int wp_offset, zno = blk_rq_zone_no(rq); | |
327 | blk_status_t ret; | |
328 | ||
329 | ret = sd_zbc_cmnd_checks(cmd); | |
330 | if (ret != BLK_STS_OK) | |
331 | return ret; | |
332 | ||
333 | if (!blk_rq_zone_is_seq(rq)) | |
334 | return BLK_STS_IOERR; | |
335 | ||
336 | /* Unlock of the write lock will happen in sd_zbc_complete() */ | |
337 | if (!blk_req_zone_write_trylock(rq)) | |
338 | return BLK_STS_ZONE_RESOURCE; | |
339 | ||
340 | spin_lock_bh(&sdkp->zones_wp_offset_lock); | |
341 | wp_offset = sdkp->zones_wp_offset[zno]; | |
342 | switch (wp_offset) { | |
343 | case SD_ZBC_INVALID_WP_OFST: | |
344 | /* | |
345 | * We are about to schedule work to update a zone write pointer | |
346 | * offset, which will cause the zone append command to be | |
347 | * requeued. So make sure that the scsi device does not go away | |
348 | * while the work is being processed. | |
349 | */ | |
350 | if (scsi_device_get(sdkp->device)) { | |
351 | ret = BLK_STS_IOERR; | |
352 | break; | |
353 | } | |
354 | sdkp->zones_wp_offset[zno] = SD_ZBC_UPDATING_WP_OFST; | |
355 | schedule_work(&sdkp->zone_wp_offset_work); | |
356 | fallthrough; | |
357 | case SD_ZBC_UPDATING_WP_OFST: | |
358 | ret = BLK_STS_DEV_RESOURCE; | |
359 | break; | |
360 | default: | |
361 | wp_offset = sectors_to_logical(sdkp->device, wp_offset); | |
362 | if (wp_offset + nr_blocks > sdkp->zone_blocks) { | |
363 | ret = BLK_STS_IOERR; | |
364 | break; | |
365 | } | |
366 | ||
367 | *lba += wp_offset; | |
368 | } | |
369 | spin_unlock_bh(&sdkp->zones_wp_offset_lock); | |
370 | if (ret) | |
371 | blk_req_zone_write_unlock(rq); | |
372 | return ret; | |
373 | } | |
374 | ||
e98f42bc | 375 | /** |
ad512f20 AJ |
376 | * sd_zbc_setup_zone_mgmt_cmnd - Prepare a zone ZBC_OUT command. The operations |
377 | * can be RESET WRITE POINTER, OPEN, CLOSE or FINISH. | |
e98f42bc | 378 | * @cmd: the command to setup |
ad512f20 AJ |
379 | * @op: Operation to be performed |
380 | * @all: All zones control | |
e98f42bc | 381 | * |
ad512f20 AJ |
382 | * Called from sd_init_command() for REQ_OP_ZONE_RESET, REQ_OP_ZONE_RESET_ALL, |
383 | * REQ_OP_ZONE_OPEN, REQ_OP_ZONE_CLOSE or REQ_OP_ZONE_FINISH requests. | |
e98f42bc | 384 | */ |
ad512f20 AJ |
385 | blk_status_t sd_zbc_setup_zone_mgmt_cmnd(struct scsi_cmnd *cmd, |
386 | unsigned char op, bool all) | |
89d94756 HR |
387 | { |
388 | struct request *rq = cmd->request; | |
89d94756 | 389 | sector_t sector = blk_rq_pos(rq); |
02494d35 | 390 | struct scsi_disk *sdkp = scsi_disk(rq->rq_disk); |
89d94756 | 391 | sector_t block = sectors_to_logical(sdkp->device, sector); |
02494d35 | 392 | blk_status_t ret; |
89d94756 | 393 | |
02494d35 JT |
394 | ret = sd_zbc_cmnd_checks(cmd); |
395 | if (ret != BLK_STS_OK) | |
396 | return ret; | |
89d94756 | 397 | |
89d94756 HR |
398 | cmd->cmd_len = 16; |
399 | memset(cmd->cmnd, 0, cmd->cmd_len); | |
400 | cmd->cmnd[0] = ZBC_OUT; | |
ad512f20 | 401 | cmd->cmnd[1] = op; |
d81e9d49 CK |
402 | if (all) |
403 | cmd->cmnd[14] = 0x1; | |
404 | else | |
405 | put_unaligned_be64(block, &cmd->cmnd[2]); | |
89d94756 HR |
406 | |
407 | rq->timeout = SD_TIMEOUT; | |
408 | cmd->sc_data_direction = DMA_NONE; | |
409 | cmd->transfersize = 0; | |
410 | cmd->allowed = 0; | |
411 | ||
159b2cbf | 412 | return BLK_STS_OK; |
89d94756 HR |
413 | } |
414 | ||
5795eb44 JT |
415 | static bool sd_zbc_need_zone_wp_update(struct request *rq) |
416 | { | |
417 | switch (req_op(rq)) { | |
418 | case REQ_OP_ZONE_APPEND: | |
419 | case REQ_OP_ZONE_FINISH: | |
420 | case REQ_OP_ZONE_RESET: | |
421 | case REQ_OP_ZONE_RESET_ALL: | |
422 | return true; | |
423 | case REQ_OP_WRITE: | |
424 | case REQ_OP_WRITE_ZEROES: | |
425 | case REQ_OP_WRITE_SAME: | |
426 | return blk_rq_zone_is_seq(rq); | |
427 | default: | |
428 | return false; | |
429 | } | |
430 | } | |
431 | ||
432 | /** | |
433 | * sd_zbc_zone_wp_update - Update cached zone write pointer upon cmd completion | |
434 | * @cmd: Completed command | |
435 | * @good_bytes: Command reply bytes | |
436 | * | |
437 | * Called from sd_zbc_complete() to handle the update of the cached zone write | |
438 | * pointer value in case an update is needed. | |
439 | */ | |
440 | static unsigned int sd_zbc_zone_wp_update(struct scsi_cmnd *cmd, | |
441 | unsigned int good_bytes) | |
442 | { | |
443 | int result = cmd->result; | |
444 | struct request *rq = cmd->request; | |
445 | struct scsi_disk *sdkp = scsi_disk(rq->rq_disk); | |
446 | unsigned int zno = blk_rq_zone_no(rq); | |
447 | enum req_opf op = req_op(rq); | |
448 | ||
449 | /* | |
450 | * If we got an error for a command that needs updating the write | |
451 | * pointer offset cache, we must mark the zone wp offset entry as | |
452 | * invalid to force an update from disk the next time a zone append | |
453 | * command is issued. | |
454 | */ | |
455 | spin_lock_bh(&sdkp->zones_wp_offset_lock); | |
456 | ||
457 | if (result && op != REQ_OP_ZONE_RESET_ALL) { | |
458 | if (op == REQ_OP_ZONE_APPEND) { | |
459 | /* Force complete completion (no retry) */ | |
460 | good_bytes = 0; | |
461 | scsi_set_resid(cmd, blk_rq_bytes(rq)); | |
462 | } | |
463 | ||
464 | /* | |
465 | * Force an update of the zone write pointer offset on | |
466 | * the next zone append access. | |
467 | */ | |
468 | if (sdkp->zones_wp_offset[zno] != SD_ZBC_UPDATING_WP_OFST) | |
469 | sdkp->zones_wp_offset[zno] = SD_ZBC_INVALID_WP_OFST; | |
470 | goto unlock_wp_offset; | |
471 | } | |
472 | ||
473 | switch (op) { | |
474 | case REQ_OP_ZONE_APPEND: | |
475 | rq->__sector += sdkp->zones_wp_offset[zno]; | |
476 | fallthrough; | |
477 | case REQ_OP_WRITE_ZEROES: | |
478 | case REQ_OP_WRITE_SAME: | |
479 | case REQ_OP_WRITE: | |
480 | if (sdkp->zones_wp_offset[zno] < sd_zbc_zone_sectors(sdkp)) | |
481 | sdkp->zones_wp_offset[zno] += | |
482 | good_bytes >> SECTOR_SHIFT; | |
483 | break; | |
484 | case REQ_OP_ZONE_RESET: | |
485 | sdkp->zones_wp_offset[zno] = 0; | |
486 | break; | |
487 | case REQ_OP_ZONE_FINISH: | |
488 | sdkp->zones_wp_offset[zno] = sd_zbc_zone_sectors(sdkp); | |
489 | break; | |
490 | case REQ_OP_ZONE_RESET_ALL: | |
491 | memset(sdkp->zones_wp_offset, 0, | |
492 | sdkp->nr_zones * sizeof(unsigned int)); | |
493 | break; | |
494 | default: | |
495 | break; | |
496 | } | |
497 | ||
498 | unlock_wp_offset: | |
499 | spin_unlock_bh(&sdkp->zones_wp_offset_lock); | |
500 | ||
501 | return good_bytes; | |
502 | } | |
503 | ||
e98f42bc DLM |
504 | /** |
505 | * sd_zbc_complete - ZBC command post processing. | |
506 | * @cmd: Completed command | |
507 | * @good_bytes: Command reply bytes | |
508 | * @sshdr: command sense header | |
509 | * | |
5795eb44 JT |
510 | * Called from sd_done() to handle zone commands errors and updates to the |
511 | * device queue zone write pointer offset cahce. | |
e98f42bc | 512 | */ |
5795eb44 | 513 | unsigned int sd_zbc_complete(struct scsi_cmnd *cmd, unsigned int good_bytes, |
89d94756 HR |
514 | struct scsi_sense_hdr *sshdr) |
515 | { | |
516 | int result = cmd->result; | |
517 | struct request *rq = cmd->request; | |
518 | ||
ad512f20 | 519 | if (op_is_zone_mgmt(req_op(rq)) && |
edc1f543 DLM |
520 | result && |
521 | sshdr->sense_key == ILLEGAL_REQUEST && | |
522 | sshdr->asc == 0x24) { | |
523 | /* | |
ad512f20 AJ |
524 | * INVALID FIELD IN CDB error: a zone management command was |
525 | * attempted on a conventional zone. Nothing to worry about, | |
526 | * so be quiet about the error. | |
edc1f543 DLM |
527 | */ |
528 | rq->rq_flags |= RQF_QUIET; | |
5795eb44 JT |
529 | } else if (sd_zbc_need_zone_wp_update(rq)) |
530 | good_bytes = sd_zbc_zone_wp_update(cmd, good_bytes); | |
531 | ||
532 | if (req_op(rq) == REQ_OP_ZONE_APPEND) | |
533 | blk_req_zone_write_unlock(rq); | |
534 | ||
535 | return good_bytes; | |
89d94756 HR |
536 | } |
537 | ||
538 | /** | |
7f9d35d2 | 539 | * sd_zbc_check_zoned_characteristics - Check zoned block device characteristics |
e98f42bc DLM |
540 | * @sdkp: Target disk |
541 | * @buf: Buffer where to store the VPD page data | |
542 | * | |
7f9d35d2 | 543 | * Read VPD page B6, get information and check that reads are unconstrained. |
89d94756 | 544 | */ |
7f9d35d2 DLM |
545 | static int sd_zbc_check_zoned_characteristics(struct scsi_disk *sdkp, |
546 | unsigned char *buf) | |
89d94756 HR |
547 | { |
548 | ||
549 | if (scsi_get_vpd_page(sdkp->device, 0xb6, buf, 64)) { | |
550 | sd_printk(KERN_NOTICE, sdkp, | |
7f9d35d2 | 551 | "Read zoned characteristics VPD page failed\n"); |
89d94756 HR |
552 | return -ENODEV; |
553 | } | |
554 | ||
555 | if (sdkp->device->type != TYPE_ZBC) { | |
556 | /* Host-aware */ | |
557 | sdkp->urswrz = 1; | |
4a109032 DLM |
558 | sdkp->zones_optimal_open = get_unaligned_be32(&buf[8]); |
559 | sdkp->zones_optimal_nonseq = get_unaligned_be32(&buf[12]); | |
89d94756 HR |
560 | sdkp->zones_max_open = 0; |
561 | } else { | |
562 | /* Host-managed */ | |
563 | sdkp->urswrz = buf[4] & 1; | |
564 | sdkp->zones_optimal_open = 0; | |
565 | sdkp->zones_optimal_nonseq = 0; | |
4a109032 | 566 | sdkp->zones_max_open = get_unaligned_be32(&buf[16]); |
89d94756 HR |
567 | } |
568 | ||
7f9d35d2 DLM |
569 | /* |
570 | * Check for unconstrained reads: host-managed devices with | |
571 | * constrained reads (drives failing read after write pointer) | |
572 | * are not supported. | |
573 | */ | |
574 | if (!sdkp->urswrz) { | |
575 | if (sdkp->first_scan) | |
576 | sd_printk(KERN_NOTICE, sdkp, | |
577 | "constrained reads devices are not supported\n"); | |
578 | return -ENODEV; | |
579 | } | |
580 | ||
89d94756 HR |
581 | return 0; |
582 | } | |
583 | ||
e98f42bc | 584 | /** |
dbfc5626 | 585 | * sd_zbc_check_capacity - Check the device capacity |
e98f42bc | 586 | * @sdkp: Target disk |
dbfc5626 | 587 | * @buf: command buffer |
8df513da | 588 | * @zblocks: zone size in number of blocks |
e98f42bc | 589 | * |
dbfc5626 DLM |
590 | * Get the device zone size and check that the device capacity as reported |
591 | * by READ CAPACITY matches the max_lba value (plus one) of the report zones | |
592 | * command reply for devices with RC_BASIS == 0. | |
ccce20fc | 593 | * |
dbfc5626 | 594 | * Returns 0 upon success or an error code upon failure. |
e98f42bc | 595 | */ |
dbfc5626 DLM |
596 | static int sd_zbc_check_capacity(struct scsi_disk *sdkp, unsigned char *buf, |
597 | u32 *zblocks) | |
89d94756 | 598 | { |
dbfc5626 | 599 | u64 zone_blocks; |
d9dd7308 | 600 | sector_t max_lba; |
89d94756 | 601 | unsigned char *rec; |
5f832a39 | 602 | int ret; |
b091ac61 | 603 | |
d9dd7308 DLM |
604 | /* Do a report zone to get max_lba and the size of the first zone */ |
605 | ret = sd_zbc_do_report_zones(sdkp, buf, SD_BUF_SIZE, 0, false); | |
4b433924 | 606 | if (ret) |
d9dd7308 | 607 | return ret; |
89d94756 | 608 | |
d2e428e4 DLM |
609 | if (sdkp->rc_basis == 0) { |
610 | /* The max_lba field is the capacity of this device */ | |
611 | max_lba = get_unaligned_be64(&buf[8]); | |
612 | if (sdkp->capacity != max_lba + 1) { | |
613 | if (sdkp->first_scan) | |
614 | sd_printk(KERN_WARNING, sdkp, | |
615 | "Changing capacity from %llu to max LBA+1 %llu\n", | |
616 | (unsigned long long)sdkp->capacity, | |
617 | (unsigned long long)max_lba + 1); | |
618 | sdkp->capacity = max_lba + 1; | |
619 | } | |
620 | } | |
621 | ||
dbfc5626 | 622 | /* Get the size of the first reported zone */ |
d9dd7308 DLM |
623 | rec = buf + 64; |
624 | zone_blocks = get_unaligned_be64(&rec[8]); | |
d9dd7308 | 625 | if (logical_to_sectors(sdkp->device, zone_blocks) > UINT_MAX) { |
89d94756 HR |
626 | if (sdkp->first_scan) |
627 | sd_printk(KERN_NOTICE, sdkp, | |
628 | "Zone size too large\n"); | |
d9dd7308 | 629 | return -EFBIG; |
89d94756 HR |
630 | } |
631 | ||
d9dd7308 | 632 | *zblocks = zone_blocks; |
89d94756 | 633 | |
d9dd7308 | 634 | return 0; |
89d94756 HR |
635 | } |
636 | ||
a3d8a257 DLM |
637 | static void sd_zbc_print_zones(struct scsi_disk *sdkp) |
638 | { | |
639 | if (!sd_is_zoned(sdkp) || !sdkp->capacity) | |
640 | return; | |
641 | ||
642 | if (sdkp->capacity & (sdkp->zone_blocks - 1)) | |
643 | sd_printk(KERN_NOTICE, sdkp, | |
644 | "%u zones of %u logical blocks + 1 runt zone\n", | |
645 | sdkp->nr_zones - 1, | |
646 | sdkp->zone_blocks); | |
647 | else | |
648 | sd_printk(KERN_NOTICE, sdkp, | |
649 | "%u zones of %u logical blocks\n", | |
650 | sdkp->nr_zones, | |
651 | sdkp->zone_blocks); | |
652 | } | |
653 | ||
5795eb44 JT |
654 | static void sd_zbc_revalidate_zones_cb(struct gendisk *disk) |
655 | { | |
656 | struct scsi_disk *sdkp = scsi_disk(disk); | |
657 | ||
658 | swap(sdkp->zones_wp_offset, sdkp->rev_wp_offset); | |
659 | } | |
660 | ||
a3d8a257 | 661 | int sd_zbc_revalidate_zones(struct scsi_disk *sdkp) |
5795eb44 JT |
662 | { |
663 | struct gendisk *disk = sdkp->disk; | |
a3d8a257 DLM |
664 | struct request_queue *q = disk->queue; |
665 | u32 zone_blocks = sdkp->rev_zone_blocks; | |
666 | unsigned int nr_zones = sdkp->rev_nr_zones; | |
667 | u32 max_append; | |
5795eb44 JT |
668 | int ret = 0; |
669 | ||
27ba3e8f DLM |
670 | /* |
671 | * There is nothing to do for regular disks, including host-aware disks | |
672 | * that have partitions. | |
673 | */ | |
674 | if (!blk_queue_is_zoned(q)) | |
a3d8a257 DLM |
675 | return 0; |
676 | ||
5795eb44 JT |
677 | /* |
678 | * Make sure revalidate zones are serialized to ensure exclusive | |
679 | * updates of the scsi disk data. | |
680 | */ | |
681 | mutex_lock(&sdkp->rev_mutex); | |
682 | ||
5795eb44 JT |
683 | if (sdkp->zone_blocks == zone_blocks && |
684 | sdkp->nr_zones == nr_zones && | |
685 | disk->queue->nr_zones == nr_zones) | |
686 | goto unlock; | |
687 | ||
a3d8a257 DLM |
688 | sdkp->zone_blocks = zone_blocks; |
689 | sdkp->nr_zones = nr_zones; | |
5795eb44 JT |
690 | sdkp->rev_wp_offset = kvcalloc(nr_zones, sizeof(u32), GFP_NOIO); |
691 | if (!sdkp->rev_wp_offset) { | |
692 | ret = -ENOMEM; | |
693 | goto unlock; | |
694 | } | |
695 | ||
696 | ret = blk_revalidate_disk_zones(disk, sd_zbc_revalidate_zones_cb); | |
697 | ||
698 | kvfree(sdkp->rev_wp_offset); | |
699 | sdkp->rev_wp_offset = NULL; | |
700 | ||
a3d8a257 DLM |
701 | if (ret) { |
702 | sdkp->zone_blocks = 0; | |
703 | sdkp->nr_zones = 0; | |
704 | sdkp->capacity = 0; | |
705 | goto unlock; | |
706 | } | |
707 | ||
708 | max_append = min_t(u32, logical_to_sectors(sdkp->device, zone_blocks), | |
709 | q->limits.max_segments << (PAGE_SHIFT - 9)); | |
710 | max_append = min_t(u32, max_append, queue_max_hw_sectors(q)); | |
711 | ||
712 | blk_queue_max_zone_append_sectors(q, max_append); | |
713 | ||
714 | sd_zbc_print_zones(sdkp); | |
715 | ||
5795eb44 JT |
716 | unlock: |
717 | mutex_unlock(&sdkp->rev_mutex); | |
718 | ||
719 | return ret; | |
720 | } | |
721 | ||
e98f42bc | 722 | int sd_zbc_read_zones(struct scsi_disk *sdkp, unsigned char *buf) |
89d94756 | 723 | { |
bf505456 | 724 | struct gendisk *disk = sdkp->disk; |
5795eb44 | 725 | struct request_queue *q = disk->queue; |
bf505456 | 726 | unsigned int nr_zones; |
0cdc5858 | 727 | u32 zone_blocks = 0; |
f7053240 | 728 | int ret; |
89d94756 HR |
729 | |
730 | if (!sd_is_zoned(sdkp)) | |
731 | /* | |
732 | * Device managed or normal SCSI disk, | |
733 | * no special handling required | |
734 | */ | |
735 | return 0; | |
736 | ||
7f9d35d2 DLM |
737 | /* Check zoned block device characteristics (unconstrained reads) */ |
738 | ret = sd_zbc_check_zoned_characteristics(sdkp, buf); | |
89d94756 HR |
739 | if (ret) |
740 | goto err; | |
741 | ||
dbfc5626 DLM |
742 | /* Check the device capacity reported by report zones */ |
743 | ret = sd_zbc_check_capacity(sdkp, buf, &zone_blocks); | |
5f832a39 | 744 | if (ret != 0) |
89d94756 HR |
745 | goto err; |
746 | ||
747 | /* The drive satisfies the kernel restrictions: set it up */ | |
5795eb44 JT |
748 | blk_queue_flag_set(QUEUE_FLAG_ZONE_RESETALL, q); |
749 | blk_queue_required_elevator_features(q, ELEVATOR_F_ZBD_SEQ_WRITE); | |
e15864f8 NC |
750 | if (sdkp->zones_max_open == U32_MAX) |
751 | blk_queue_max_open_zones(q, 0); | |
752 | else | |
753 | blk_queue_max_open_zones(q, sdkp->zones_max_open); | |
659bf827 | 754 | blk_queue_max_active_zones(q, 0); |
bf505456 DLM |
755 | nr_zones = round_up(sdkp->capacity, zone_blocks) >> ilog2(zone_blocks); |
756 | ||
757 | /* READ16/WRITE16 is mandatory for ZBC disks */ | |
758 | sdkp->device->use_16_for_rw = 1; | |
759 | sdkp->device->use_10_for_rw = 0; | |
760 | ||
a3d8a257 DLM |
761 | sdkp->rev_nr_zones = nr_zones; |
762 | sdkp->rev_zone_blocks = zone_blocks; | |
89d94756 HR |
763 | |
764 | return 0; | |
765 | ||
766 | err: | |
767 | sdkp->capacity = 0; | |
768 | ||
769 | return ret; | |
770 | } | |
771 | ||
5795eb44 JT |
772 | int sd_zbc_init_disk(struct scsi_disk *sdkp) |
773 | { | |
774 | if (!sd_is_zoned(sdkp)) | |
775 | return 0; | |
776 | ||
777 | sdkp->zones_wp_offset = NULL; | |
778 | spin_lock_init(&sdkp->zones_wp_offset_lock); | |
779 | sdkp->rev_wp_offset = NULL; | |
780 | mutex_init(&sdkp->rev_mutex); | |
781 | INIT_WORK(&sdkp->zone_wp_offset_work, sd_zbc_update_wp_offset_workfn); | |
782 | sdkp->zone_wp_update_buf = kzalloc(SD_BUF_SIZE, GFP_KERNEL); | |
783 | if (!sdkp->zone_wp_update_buf) | |
784 | return -ENOMEM; | |
785 | ||
786 | return 0; | |
787 | } | |
788 | ||
789 | void sd_zbc_release_disk(struct scsi_disk *sdkp) | |
790 | { | |
791 | kvfree(sdkp->zones_wp_offset); | |
792 | sdkp->zones_wp_offset = NULL; | |
793 | kfree(sdkp->zone_wp_update_buf); | |
794 | sdkp->zone_wp_update_buf = NULL; | |
795 | } |