Add zoned storage support to ublk: report_zones and operations: - REQ_OP_ZONE_OPEN - REQ_OP_ZONE_CLOSE - REQ_OP_ZONE_FINISH - REQ_OP_ZONE_RESET - REQ_OP_ZONE_APPEND The zone append feature uses the `addr` field of `struct ublksrv_io_cmd` to communicate ALBA back to the kernel. Therefore ublk must be used with the user copy feature (UBLK_F_USER_COPY) for zoned storage support to be available. Without this feature, ublk will not allow zoned storage support.
Unified · Split
| @@ -56,16 +56,21 @@ · 55 unmodified lines | ||
| 56 | 56 | | UBLK_F_USER_RECOVERY_REISSUE \ |
| 57 | 57 | | UBLK_F_UNPRIVILEGED_DEV \ |
| 58 | 58 | | UBLK_F_CMD_IOCTL_ENCODE \ |
| 59 | − | UBLK_F_USER_COPY) | |
| 59 | + | UBLK_F_USER_COPY \ | |
| 60 | + | UBLK_F_ZONED) | |
| 60 | 61 | |
| 61 | 62 | /* All UBLK_PARAM_TYPE_* should be included here */ |
| 62 | −#define UBLK_PARAM_TYPE_ALL (UBLK_PARAM_TYPE_BASIC | \ | |
| 63 | − UBLK_PARAM_TYPE_DISCARD | UBLK_PARAM_TYPE_DEVT) | |
| 63 | +#define UBLK_PARAM_TYPE_ALL \ | |
| 64 | + (UBLK_PARAM_TYPE_BASIC | UBLK_PARAM_TYPE_DISCARD | \ | |
| 65 | + UBLK_PARAM_TYPE_DEVT | UBLK_PARAM_TYPE_ZONED) | |
| 64 | 66 | |
| 65 | 67 | struct ublk_rq_data { |
| 66 | 68 | struct llist_node node; |
| 67 | 69 | |
| 68 | 70 | struct kref ref; |
| 71 | + __u64 sector; | |
| 72 | + __u32 operation; | |
| 73 | + __u32 nr_zones; | |
| 69 | 74 | }; |
| 70 | 75 | |
| 71 | 76 | struct ublk_uring_cmd_pdu { |
| @@ -185,11 +190,263 @@ struct ublk_params_header { · 113 unmodified lines | ||
| 185 | 190 | __u32 types; |
| 186 | 191 | }; |
| 187 | 192 | |
| 193 | +static inline unsigned int ublk_req_build_flags(struct request *req); | |
| 194 | +static inline struct ublksrv_io_desc *ublk_get_iod(struct ublk_queue *ubq, | |
| 195 | + int tag); | |
| 196 | + | |
| 188 | 197 | static inline bool ublk_dev_is_user_copy(const struct ublk_device *ub) |
| 189 | 198 | { |
| 190 | 199 | return ub->dev_info.flags & UBLK_F_USER_COPY; |
| 191 | 200 | } |
| 192 | 201 | |
| 202 | +static inline bool ublk_dev_is_zoned(const struct ublk_device *ub) | |
| 203 | +{ | |
| 204 | + return ub->dev_info.flags & UBLK_F_ZONED; | |
| 205 | +} | |
| 206 | + | |
| 207 | +static inline bool ublk_queue_is_zoned(struct ublk_queue *ubq) | |
| 208 | +{ | |
| 209 | + return ubq->flags & UBLK_F_ZONED; | |
| 210 | +} | |
| 211 | + | |
| 212 | +#ifdef CONFIG_BLK_DEV_ZONED | |
| 213 | + | |
| 214 | +static int ublk_get_nr_zones(const struct ublk_device *ub) | |
| 215 | +{ | |
| 216 | + const struct ublk_param_basic *p = &ub->params.basic; | |
| 217 | + | |
| 218 | + /* Zone size is a power of 2 */ | |
| 219 | + return p->dev_sectors >> ilog2(p->chunk_sectors); | |
| 220 | +} | |
| 221 | + | |
| 222 | +static int ublk_revalidate_disk_zones(struct ublk_device *ub) | |
| 223 | +{ | |
| 224 | + return blk_revalidate_disk_zones(ub->ub_disk, NULL); | |
| 225 | +} | |
| 226 | + | |
| 227 | +static int ublk_dev_param_zoned_validate(const struct ublk_device *ub) | |
| 228 | +{ | |
| 229 | + const struct ublk_param_zoned *p = &ub->params.zoned; | |
| 230 | + int nr_zones; | |
| 231 | + | |
| 232 | + if (!ublk_dev_is_zoned(ub)) | |
| 233 | + return -EINVAL; | |
| 234 | + | |
| 235 | + if (!p->max_zone_append_sectors) | |
| 236 | + return -EINVAL; | |
| 237 | + | |
| 238 | + nr_zones = ublk_get_nr_zones(ub); | |
| 239 | + | |
| 240 | + if (p->max_active_zones > nr_zones) | |
| 241 | + return -EINVAL; | |
| 242 | + | |
| 243 | + if (p->max_open_zones > nr_zones) | |
| 244 | + return -EINVAL; | |
| 245 | + | |
| 246 | + return 0; | |
| 247 | +} | |
| 248 | + | |
| 249 | +static int ublk_dev_param_zoned_apply(struct ublk_device *ub) | |
| 250 | +{ | |
| 251 | + const struct ublk_param_zoned *p = &ub->params.zoned; | |
| 252 | + | |
| 253 | + disk_set_zoned(ub->ub_disk, BLK_ZONED_HM); | |
| 254 | + blk_queue_required_elevator_features(ub->ub_disk->queue, | |
| 255 | + ELEVATOR_F_ZBD_SEQ_WRITE); | |
| 256 | + disk_set_max_active_zones(ub->ub_disk, p->max_active_zones); | |
| 257 | + disk_set_max_open_zones(ub->ub_disk, p->max_open_zones); | |
| 258 | + blk_queue_max_zone_append_sectors(ub->ub_disk->queue, p->max_zone_append_sectors); | |
| 259 | + | |
| 260 | + ub->ub_disk->nr_zones = ublk_get_nr_zones(ub); | |
| 261 | + | |
| 262 | + return 0; | |
| 263 | +} | |
| 264 | + | |
| 265 | +/* Based on virtblk_alloc_report_buffer */ | |
| 266 | +static void *ublk_alloc_report_buffer(struct ublk_device *ublk, | |
| 267 | + unsigned int nr_zones, size_t *buflen) | |
| 268 | +{ | |
| 269 | + struct request_queue *q = ublk->ub_disk->queue; | |
| 270 | + size_t bufsize; | |
| 271 | + void *buf; | |
| 272 | + | |
| 273 | + nr_zones = min_t(unsigned int, nr_zones, | |
| 274 | + ublk->ub_disk->nr_zones); | |
| 275 | + | |
| 276 | + bufsize = nr_zones * sizeof(struct blk_zone); | |
| 277 | + bufsize = | |
| 278 | + min_t(size_t, bufsize, queue_max_hw_sectors(q) << SECTOR_SHIFT); | |
| 279 | + | |
| 280 | + while (bufsize >= sizeof(struct blk_zone)) { | |
| 281 | + buf = kvmalloc(bufsize, GFP_KERNEL | __GFP_NORETRY); | |
| 282 | + if (buf) { | |
| 283 | + *buflen = bufsize; | |
| 284 | + return buf; | |
| 285 | + } | |
| 286 | + bufsize >>= 1; | |
| 287 | + } | |
| 288 | + | |
| 289 | + *buflen = 0; | |
| 290 | + return NULL; | |
| 291 | +} | |
| 292 | + | |
| 293 | +static int ublk_report_zones(struct gendisk *disk, sector_t sector, | |
| 294 | + unsigned int nr_zones, report_zones_cb cb, void *data) | |
| 295 | +{ | |
| 296 | + struct ublk_device *ub = disk->private_data; | |
| 297 | + unsigned int zone_size_sectors = disk->queue->limits.chunk_sectors; | |
| 298 | + unsigned int first_zone = sector >> ilog2(zone_size_sectors); | |
| 299 | + unsigned int done_zones = 0; | |
| 300 | + unsigned int max_zones_per_request; | |
| 301 | + int ret; | |
| 302 | + struct blk_zone *buffer; | |
| 303 | + size_t buffer_length; | |
| 304 | + | |
| 305 | + nr_zones = min_t(unsigned int, ub->ub_disk->nr_zones - first_zone, | |
| 306 | + nr_zones); | |
| 307 | + | |
| 308 | + buffer = ublk_alloc_report_buffer(ub, nr_zones, &buffer_length); | |
| 309 | + if (!buffer) | |
| 310 | + return -ENOMEM; | |
| 311 | + | |
| 312 | + max_zones_per_request = buffer_length / sizeof(struct blk_zone); | |
| 313 | + | |
| 314 | + while (done_zones < nr_zones) { | |
| 315 | + unsigned int remaining_zones = nr_zones - done_zones; | |
| 316 | + unsigned int zones_in_request = | |
| 317 | + min_t(unsigned int, remaining_zones, max_zones_per_request); | |
| 318 | + struct request *req; | |
| 319 | + struct ublk_rq_data *pdu; | |
| 320 | + blk_status_t status; | |
| 321 | + | |
| 322 | + memset(buffer, 0, buffer_length); | |
| 323 | + | |
| 324 | + req = blk_mq_alloc_request(disk->queue, REQ_OP_DRV_IN, 0); | |
| 325 | + if (IS_ERR(req)) { | |
| 326 | + ret = PTR_ERR(req); | |
| 327 | + goto out; | |
| 328 | + } | |
| 329 | + | |
| 330 | + pdu = blk_mq_rq_to_pdu(req); | |
| 331 | + pdu->operation = UBLK_IO_OP_REPORT_ZONES; | |
| 332 | + pdu->sector = sector; | |
| 333 | + pdu->nr_zones = zones_in_request; | |
| 334 | + | |
| 335 | + ret = blk_rq_map_kern(disk->queue, req, buffer, buffer_length, | |
| 336 | + GFP_KERNEL); | |
| 337 | + if (ret) { | |
| 338 | + blk_mq_free_request(req); | |
| 339 | + goto out; | |
| 340 | + } | |
| 341 | + | |
| 342 | + status = blk_execute_rq(req, 0); | |
| 343 | + ret = blk_status_to_errno(status); | |
| 344 | + blk_mq_free_request(req); | |
| 345 | + if (ret) | |
| 346 | + goto out; | |
| 347 | + | |
| 348 | + for (unsigned int i = 0; i < zones_in_request; i++) { | |
| 349 | + struct blk_zone *zone = buffer + i; | |
| 350 | + | |
| 351 | + /* A zero length zone means no more zones in this response */ | |
| 352 | + if (!zone->len) | |
| 353 | + break; | |
| 354 | + | |
| 355 | + ret = cb(zone, i, data); | |
| 356 | + if (ret) | |
| 357 | + goto out; | |
| 358 | + | |
| 359 | + done_zones++; | |
| 360 | + sector += zone_size_sectors; | |
| 361 | + | |
| 362 | + } | |
| 363 | + } | |
| 364 | + | |
| 365 | + ret = done_zones; | |
| 366 | + | |
| 367 | +out: | |
| 368 | + kvfree(buffer); | |
| 369 | + return ret; | |
| 370 | +} | |
| 371 | + | |
| 372 | +static blk_status_t ublk_setup_iod_zoned(struct ublk_queue *ubq, | |
| 373 | + struct request *req) | |
| 374 | +{ | |
| 375 | + struct ublksrv_io_desc *iod = ublk_get_iod(ubq, req->tag); | |
| 376 | + struct ublk_io *io = &ubq->ios[req->tag]; | |
| 377 | + struct ublk_rq_data *pdu = blk_mq_rq_to_pdu(req); | |
| 378 | + u32 ublk_op; | |
| 379 | + | |
| 380 | + switch (req_op(req)) { | |
| 381 | + case REQ_OP_ZONE_OPEN: | |
| 382 | + ublk_op = UBLK_IO_OP_ZONE_OPEN; | |
| 383 | + break; | |
| 384 | + case REQ_OP_ZONE_CLOSE: | |
| 385 | + ublk_op = UBLK_IO_OP_ZONE_CLOSE; | |
| 386 | + break; | |
| 387 | + case REQ_OP_ZONE_FINISH: | |
| 388 | + ublk_op = UBLK_IO_OP_ZONE_FINISH; | |
| 389 | + break; | |
| 390 | + case REQ_OP_ZONE_RESET: | |
| 391 | + ublk_op = UBLK_IO_OP_ZONE_RESET; | |
| 392 | + break; | |
| 393 | + case REQ_OP_ZONE_APPEND: | |
| 394 | + ublk_op = UBLK_IO_OP_ZONE_APPEND; | |
| 395 | + break; | |
| 396 | + case REQ_OP_DRV_IN: | |
| 397 | + ublk_op = pdu->operation; | |
| 398 | + switch (ublk_op) { | |
| 399 | + case UBLK_IO_OP_REPORT_ZONES: | |
| 400 | + iod->op_flags = ublk_op | ublk_req_build_flags(req); | |
| 401 | + iod->nr_zones = pdu->nr_zones; | |
| 402 | + iod->start_sector = pdu->sector; | |
| 403 | + return BLK_STS_OK; | |
| 404 | + default: | |
| 405 | + return BLK_STS_IOERR; | |
| 406 | + } | |
| 407 | + case REQ_OP_ZONE_RESET_ALL: | |
| 408 | + case REQ_OP_DRV_OUT: | |
| 409 | + /* We do not support reset_all and drv_out */ | |
| 410 | + return BLK_STS_NOTSUPP; | |
| 411 | + default: | |
| 412 | + return BLK_STS_IOERR; | |
| 413 | + } | |
| 414 | + | |
| 415 | + iod->op_flags = ublk_op | ublk_req_build_flags(req); | |
| 416 | + iod->nr_sectors = blk_rq_sectors(req); | |
| 417 | + iod->start_sector = blk_rq_pos(req); | |
| 418 | + iod->addr = io->addr; | |
| 419 | + | |
| 420 | + return BLK_STS_OK; | |
| 421 | +} | |
| 422 | + | |
| 423 | +#else | |
| 424 | + | |
| 425 | +#define ublk_report_zones (NULL) | |
| 426 | + | |
| 427 | +static int ublk_dev_param_zoned_validate(const struct ublk_device *ub) | |
| 428 | +{ | |
| 429 | + return -EOPNOTSUPP; | |
| 430 | +} | |
| 431 | + | |
| 432 | +static int ublk_dev_param_zoned_apply(struct ublk_device *ub) | |
| 433 | +{ | |
| 434 | + return -EOPNOTSUPP; | |
| 435 | +} | |
| 436 | + | |
| 437 | +static int ublk_revalidate_disk_zones(struct ublk_device *ub) | |
| 438 | +{ | |
| 439 | + return 0; | |
| 440 | +} | |
| 441 | + | |
| 442 | +static blk_status_t ublk_setup_iod_zoned(struct ublk_queue *ubq, | |
| 443 | + struct request *req) | |
| 444 | +{ | |
| 445 | + return -EOPNOTSUPP; | |
| 446 | +} | |
| 447 | + | |
| 448 | +#endif | |
| 449 | + | |
| 193 | 450 | static inline void __ublk_complete_rq(struct request *req); |
| 194 | 451 | static void ublk_complete_rq(struct kref *ref); |
| 195 | 452 | |
| @@ -286,6 +543,9 @@ static int ublk_validate_params(const struct ublk_device *ub) · 90 unmodified lines | ||
| 286 | 543 | |
| 287 | 544 | if (p->max_sectors > (ub->dev_info.max_io_buf_bytes >> 9)) |
| 288 | 545 | return -EINVAL; |
| 546 | + | |
| 547 | + if (ublk_dev_is_zoned(ub) && !p->chunk_sectors) | |
| 548 | + return -EINVAL; | |
| 289 | 549 | } else |
| 290 | 550 | return -EINVAL; |
| 291 | 551 | |
| @@ -304,6 +564,11 @@ static int ublk_validate_params(const struct ublk_device *ub) · 12 unmodified lines | ||
| 304 | 564 | if (ub->params.types & UBLK_PARAM_TYPE_DEVT) |
| 305 | 565 | return -EINVAL; |
| 306 | 566 | |
| 567 | + if (ub->params.types & UBLK_PARAM_TYPE_ZONED) | |
| 568 | + return ublk_dev_param_zoned_validate(ub); | |
| 569 | + else if (ublk_dev_is_zoned(ub)) | |
| 570 | + return -EINVAL; | |
| 571 | + | |
| 307 | 572 | return 0; |
| 308 | 573 | } |
| 309 | 574 | |
| @@ -317,6 +582,9 @@ static int ublk_apply_params(struct ublk_device *ub) · 7 unmodified lines | ||
| 317 | 582 | if (ub->params.types & UBLK_PARAM_TYPE_DISCARD) |
| 318 | 583 | ublk_dev_param_discard_apply(ub); |
| 319 | 584 | |
| 585 | + if (ub->params.types & UBLK_PARAM_TYPE_ZONED) | |
| 586 | + return ublk_dev_param_zoned_apply(ub); | |
| 587 | + | |
| 320 | 588 | return 0; |
| 321 | 589 | } |
| 322 | 590 | |
| @@ -487,6 +755,7 @@ static const struct block_device_operations ub_fops = { · 164 unmodified lines | ||
| 487 | 755 | .owner = THIS_MODULE, |
| 488 | 756 | .open = ublk_open, |
| 489 | 757 | .free_disk = ublk_free_disk, |
| 758 | + .report_zones = ublk_report_zones, | |
| 490 | 759 | }; |
| 491 | 760 | |
| 492 | 761 | #define UBLK_MAX_PIN_PAGES 32 |
| @@ -601,7 +870,8 @@ static inline bool ublk_need_map_req(const struct request *req) · 108 unmodified lines | ||
| 601 | 870 | |
| 602 | 871 | static inline bool ublk_need_unmap_req(const struct request *req) |
| 603 | 872 | { |
| 604 | − return ublk_rq_has_data(req) && req_op(req) == REQ_OP_READ; | |
| 873 | + return ublk_rq_has_data(req) && | |
| 874 | + (req_op(req) == REQ_OP_READ || req_op(req) == REQ_OP_DRV_IN); | |
| 605 | 875 | } |
| 606 | 876 | |
| 607 | 877 | static int ublk_map_io(const struct ublk_queue *ubq, const struct request *req, |
| @@ -685,8 +955,13 @@ static blk_status_t ublk_setup_iod(struct ublk_queue *ubq, struct request *req) · 77 unmodified lines | ||
| 685 | 955 | { |
| 686 | 956 | struct ublksrv_io_desc *iod = ublk_get_iod(ubq, req->tag); |
| 687 | 957 | struct ublk_io *io = &ubq->ios[req->tag]; |
| 958 | + enum req_op op = req_op(req); | |
| 688 | 959 | u32 ublk_op; |
| 689 | 960 | |
| 961 | + if (!ublk_queue_is_zoned(ubq) && | |
| 962 | + (op_is_zone_mgmt(op) || op == REQ_OP_ZONE_APPEND)) | |
| 963 | + return -EIO; | |
| 964 | + | |
| 690 | 965 | switch (req_op(req)) { |
| 691 | 966 | case REQ_OP_READ: |
| 692 | 967 | ublk_op = UBLK_IO_OP_READ; |
| @@ -704,6 +979,8 @@ static blk_status_t ublk_setup_iod(struct ublk_queue *ubq, struct request *req) · 11 unmodified lines | ||
| 704 | 979 | ublk_op = UBLK_IO_OP_WRITE_ZEROES; |
| 705 | 980 | break; |
| 706 | 981 | default: |
| 982 | + if (ublk_queue_is_zoned(ubq)) | |
| 983 | + return ublk_setup_iod_zoned(ubq, req); | |
| 707 | 984 | return BLK_STS_IOERR; |
| 708 | 985 | } |
| 709 | 986 | |
| @@ -756,7 +1033,8 @@ static inline void __ublk_complete_rq(struct request *req) · 46 unmodified lines | ||
| 756 | 1033 | * |
| 757 | 1034 | * Both the two needn't unmap. |
| 758 | 1035 | */ |
| 759 | − if (req_op(req) != REQ_OP_READ && req_op(req) != REQ_OP_WRITE) | |
| 1036 | + if (req_op(req) != REQ_OP_READ && req_op(req) != REQ_OP_WRITE && | |
| 1037 | + req_op(req) != REQ_OP_DRV_IN) | |
| 760 | 1038 | goto exit; |
| 761 | 1039 | |
| 762 | 1040 | /* for READ request, writing data in iod->addr to rq buffers */ |
| @@ -1120,6 +1398,9 @@ static void ublk_commit_completion(struct ublk_device *ub, · 357 unmodified lines | ||
| 1120 | 1398 | /* find the io request and complete */ |
| 1121 | 1399 | req = blk_mq_tag_to_rq(ub->tag_set.tags[qid], tag); |
| 1122 | 1400 | |
| 1401 | + if (req_op(req) == REQ_OP_ZONE_APPEND) | |
| 1402 | + req->__sector = ub_cmd->zone_append_lba; | |
| 1403 | + | |
| 1123 | 1404 | if (req && likely(!blk_should_fake_timeout(req->q))) |
| 1124 | 1405 | ublk_put_req_ref(ubq, req); |
| 1125 | 1406 | } |
| @@ -1468,8 +1749,11 @@ static int __ublk_ch_uring_cmd(struct io_uring_cmd *cmd, · 342 unmodified lines | ||
| 1468 | 1749 | if (!ub_cmd->addr && (!ublk_need_get_data(ubq) || |
| 1469 | 1750 | req_op(req) == REQ_OP_READ)) |
| 1470 | 1751 | goto out; |
| 1471 | − } else if (ub_cmd->addr) { | |
| 1472 | − /* User copy requires addr to be unset */ | |
| 1752 | + } else if (req_op(req) != REQ_OP_ZONE_APPEND && ub_cmd->addr) { | |
| 1753 | + /* | |
| 1754 | + * User copy requires addr to be unset when command is | |
| 1755 | + * not zone append | |
| 1756 | + */ | |
| 1473 | 1757 | ret = -EINVAL; |
| 1474 | 1758 | goto out; |
| 1475 | 1759 | } |
| @@ -1546,11 +1830,14 @@ static inline bool ublk_check_ubuf_dir(const struct request *req, · 70 unmodified lines | ||
| 1546 | 1830 | int ubuf_dir) |
| 1547 | 1831 | { |
| 1548 | 1832 | /* copy ubuf to request pages */ |
| 1549 | − if (req_op(req) == REQ_OP_READ && ubuf_dir == ITER_SOURCE) | |
| 1833 | + if ((req_op(req) == REQ_OP_READ || req_op(req) == REQ_OP_DRV_IN) && | |
| 1834 | + ubuf_dir == ITER_SOURCE) | |
| 1550 | 1835 | return true; |
| 1551 | 1836 | |
| 1552 | 1837 | /* copy request pages to ubuf */ |
| 1553 | − if (req_op(req) == REQ_OP_WRITE && ubuf_dir == ITER_DEST) | |
| 1838 | + if ((req_op(req) == REQ_OP_WRITE || | |
| 1839 | + req_op(req) == REQ_OP_ZONE_APPEND) && | |
| 1840 | + ubuf_dir == ITER_DEST) | |
| 1554 | 1841 | return true; |
| 1555 | 1842 | |
| 1556 | 1843 | return false; |
| @@ -1889,17 +2176,24 @@ static int ublk_ctrl_start_dev(struct ublk_device *ub, struct io_uring_cmd *cmd) · 332 unmodified lines | ||
| 1889 | 2176 | |
| 1890 | 2177 | get_device(&ub->cdev_dev); |
| 1891 | 2178 | ub->dev_info.state = UBLK_S_DEV_LIVE; |
| 2179 | + | |
| 2180 | + if (ublk_dev_is_zoned(ub)) { | |
| 2181 | + ret = ublk_revalidate_disk_zones(ub); | |
| 2182 | + if (ret) | |
| 2183 | + goto out_put_cdev; | |
| 2184 | + } | |
| 2185 | + | |
| 1892 | 2186 | ret = add_disk(disk); |
| 2187 | + if (ret) | |
| 2188 | + goto out_put_cdev; | |
| 2189 | + | |
| 2190 | + set_bit(UB_STATE_USED, &ub->state); | |
| 2191 | + | |
| 2192 | +out_put_cdev: | |
| 1893 | 2193 | if (ret) { |
| 1894 | − /* | |
| 1895 | − * Has to drop the reference since ->free_disk won't be | |
| 1896 | − * called in case of add_disk failure. | |
| 1897 | − */ | |
| 1898 | 2194 | ub->dev_info.state = UBLK_S_DEV_DEAD; |
| 1899 | 2195 | ublk_put_device(ub); |
| 1900 | − goto out_put_disk; | |
| 1901 | 2196 | } |
| 1902 | − set_bit(UB_STATE_USED, &ub->state); | |
| 1903 | 2197 | out_put_disk: |
| 1904 | 2198 | if (ret) |
| 1905 | 2199 | put_disk(disk); |
| @@ -2049,6 +2343,13 @@ static int ublk_ctrl_add_dev(struct io_uring_cmd *cmd) · 143 unmodified lines | ||
| 2049 | 2343 | if (ublk_dev_is_user_copy(ub)) |
| 2050 | 2344 | ub->dev_info.flags &= ~UBLK_F_NEED_GET_DATA; |
| 2051 | 2345 | |
| 2346 | + /* Zoned storage support requires user copy feature */ | |
| 2347 | + if (ublk_dev_is_zoned(ub) && | |
| 2348 | + (!IS_ENABLED(CONFIG_BLK_DEV_ZONED) || !ublk_dev_is_user_copy(ub))) { | |
| 2349 | + ret = -EINVAL; | |
| 2350 | + goto out_free_dev_number; | |
| 2351 | + } | |
| 2352 | + | |
| 2052 | 2353 | /* We are not ready to support zero copy */ |
| 2053 | 2354 | ub->dev_info.flags &= ~UBLK_F_SUPPORT_ZERO_COPY; |
| 2054 | 2355 | |
| @@ -176,6 +176,12 @@ · 175 unmodified lines | ||
| 176 | 176 | /* Copy between request and user buffer by pread()/pwrite() */ |
| 177 | 177 | #define UBLK_F_USER_COPY (1UL << 7) |
| 178 | 178 | |
| 179 | +/* | |
| 180 | + * User space sets this flag when setting up the device to request zoned storage support. Kernel may | |
| 181 | + * deny the request by returning an error. | |
| 182 | + */ | |
| 183 | +#define UBLK_F_ZONED (1ULL << 8) | |
| 184 | + | |
| 179 | 185 | /* device state */ |
| 180 | 186 | #define UBLK_S_DEV_DEAD 0 |
| 181 | 187 | #define UBLK_S_DEV_LIVE 1 |
| @@ -232,9 +238,26 @@ struct ublksrv_ctrl_dev_info { · 50 unmodified lines | ||
| 232 | 238 | #define UBLK_IO_OP_READ 0 |
| 233 | 239 | #define UBLK_IO_OP_WRITE 1 |
| 234 | 240 | #define UBLK_IO_OP_FLUSH 2 |
| 235 | −#define UBLK_IO_OP_DISCARD 3 | |
| 236 | −#define UBLK_IO_OP_WRITE_SAME 4 | |
| 237 | −#define UBLK_IO_OP_WRITE_ZEROES 5 | |
| 241 | +#define UBLK_IO_OP_DISCARD 3 | |
| 242 | +#define UBLK_IO_OP_WRITE_SAME 4 | |
| 243 | +#define UBLK_IO_OP_WRITE_ZEROES 5 | |
| 244 | +#define UBLK_IO_OP_ZONE_OPEN 10 | |
| 245 | +#define UBLK_IO_OP_ZONE_CLOSE 11 | |
| 246 | +#define UBLK_IO_OP_ZONE_FINISH 12 | |
| 247 | +#define UBLK_IO_OP_ZONE_APPEND 13 | |
| 248 | +#define UBLK_IO_OP_ZONE_RESET 15 | |
| 249 | +/* | |
| 250 | + * Construct a zone report. The report request is carried in `struct | |
| 251 | + * ublksrv_io_desc`. The `start_sector` field must be the first sector of a zone | |
| 252 | + * and shall indicate the first zone of the report. The `nr_zones` shall | |
| 253 | + * indicate how many zones should be reported at most. The report shall be | |
| 254 | + * delivered as a `struct blk_zone` array. To report fewer zones than requested, | |
| 255 | + * zero the last entry of the returned array. | |
| 256 | + * | |
| 257 | + * Related definitions(blk_zone, blk_zone_cond, blk_zone_type, ...) in | |
| 258 | + * include/uapi/linux/blkzoned.h are part of ublk UAPI. | |
| 259 | + */ | |
| 260 | +#define UBLK_IO_OP_REPORT_ZONES 18 | |
| 238 | 261 | |
| 239 | 262 | #define UBLK_IO_F_FAILFAST_DEV (1U << 8) |
| 240 | 263 | #define UBLK_IO_F_FAILFAST_TRANSPORT (1U << 9) |
| @@ -255,7 +278,10 @@ struct ublksrv_io_desc { · 14 unmodified lines | ||
| 255 | 278 | /* op: bit 0-7, flags: bit 8-31 */ |
| 256 | 279 | __u32 op_flags; |
| 257 | 280 | |
| 258 | − __u32 nr_sectors; | |
| 281 | + union { | |
| 282 | + __u32 nr_sectors; | |
| 283 | + __u32 nr_zones; /* for UBLK_IO_OP_REPORT_ZONES */ | |
| 284 | + }; | |
| 259 | 285 | |
| 260 | 286 | /* start sector for this io */ |
| 261 | 287 | __u64 start_sector; |
| @@ -284,11 +310,21 @@ struct ublksrv_io_cmd { · 22 unmodified lines | ||
| 284 | 310 | /* io result, it is valid for COMMIT* command only */ |
| 285 | 311 | __s32 result; |
| 286 | 312 | |
| 287 | − /* | |
| 288 | − * userspace buffer address in ublksrv daemon process, valid for | |
| 289 | − * FETCH* command only | |
| 290 | − */ | |
| 291 | − __u64 addr; | |
| 313 | + union { | |
| 314 | + /* | |
| 315 | + * userspace buffer address in ublksrv daemon process, valid for | |
| 316 | + * FETCH* command only | |
| 317 | + * | |
| 318 | + * `addr` should not be used when UBLK_F_USER_COPY is enabled, | |
| 319 | + * because userspace handles data copy by pread()/pwrite() over | |
| 320 | + * /dev/ublkcN. But in case of UBLK_F_ZONED, this union is | |
| 321 | + * re-used to pass back the allocated LBA for | |
| 322 | + * UBLK_IO_OP_ZONE_APPEND which actually depends on | |
| 323 | + * UBLK_F_USER_COPY | |
| 324 | + */ | |
| 325 | + __u64 addr; | |
| 326 | + __u64 zone_append_lba; | |
| 327 | + }; | |
| 292 | 328 | }; |
| 293 | 329 | |
| 294 | 330 | struct ublk_param_basic { |
| @@ -331,6 +367,13 @@ struct ublk_param_devt { · 36 unmodified lines | ||
| 331 | 367 | __u32 disk_minor; |
| 332 | 368 | }; |
| 333 | 369 | |
| 370 | +struct ublk_param_zoned { | |
| 371 | + __u32 max_open_zones; | |
| 372 | + __u32 max_active_zones; | |
| 373 | + __u32 max_zone_append_sectors; | |
| 374 | + __u8 reserved[20]; | |
| 375 | +}; | |
| 376 | + | |
| 334 | 377 | struct ublk_params { |
| 335 | 378 | /* |
| 336 | 379 | * Total length of parameters, userspace has to set 'len' for both |
| @@ -342,11 +385,13 @@ struct ublk_params { · 5 unmodified lines | ||
| 342 | 385 | #define UBLK_PARAM_TYPE_BASIC (1 << 0) |
| 343 | 386 | #define UBLK_PARAM_TYPE_DISCARD (1 << 1) |
| 344 | 387 | #define UBLK_PARAM_TYPE_DEVT (1 << 2) |
| 388 | +#define UBLK_PARAM_TYPE_ZONED (1 << 3) | |
| 345 | 389 | __u32 types; /* types of parameter included */ |
| 346 | 390 | |
| 347 | 391 | struct ublk_param_basic basic; |
| 348 | 392 | struct ublk_param_discard discard; |
| 349 | 393 | struct ublk_param_devt devt; |
| 394 | + struct ublk_param_zoned zoned; | |
| 350 | 395 | }; |
| 351 | 396 | |
| 352 | 397 | #endif |