Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

block: Support atomic writes limits for stacked devices

Allow stacked devices to support atomic writes by aggregating the minimum
capability of all bottom devices.

Flag BLK_FEAT_ATOMIC_WRITES_STACKED is set for stacked devices which
have been enabled to support atomic writes.

Some things to note on the implementation:
- For simplicity, all bottom devices must have same atomic write boundary
value (if any)
- The atomic write boundary must be a power-of-2 already, but this
restriction could be relaxed. Furthermore, it is now required that the
chunk sectors for a top device must be aligned with this boundary.
- If a bottom device atomic write unit min/max are not aligned with the
top device chunk sectors, the top device atomic write unit min/max are
reduced to a value which works for the chunk sectors.

Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: John Garry <john.g.garry@oracle.com>
Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com>
Link: https://lore.kernel.org/r/20241118105018.1870052-3-john.g.garry@oracle.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>

authored by

John Garry and committed by
Jens Axboe
d7f36dc4 d00eea91

+119
+115
block/blk-settings.c
··· 501 501 return sectors; 502 502 } 503 503 504 + /* Check if second and later bottom devices are compliant */ 505 + static bool blk_stack_atomic_writes_tail(struct queue_limits *t, 506 + struct queue_limits *b) 507 + { 508 + /* We're not going to support different boundary sizes.. yet */ 509 + if (t->atomic_write_hw_boundary != b->atomic_write_hw_boundary) 510 + return false; 511 + 512 + /* Can't support this */ 513 + if (t->atomic_write_hw_unit_min > b->atomic_write_hw_unit_max) 514 + return false; 515 + 516 + /* Or this */ 517 + if (t->atomic_write_hw_unit_max < b->atomic_write_hw_unit_min) 518 + return false; 519 + 520 + t->atomic_write_hw_max = min(t->atomic_write_hw_max, 521 + b->atomic_write_hw_max); 522 + t->atomic_write_hw_unit_min = max(t->atomic_write_hw_unit_min, 523 + b->atomic_write_hw_unit_min); 524 + t->atomic_write_hw_unit_max = min(t->atomic_write_hw_unit_max, 525 + b->atomic_write_hw_unit_max); 526 + return true; 527 + } 528 + 529 + /* Check for valid boundary of first bottom device */ 530 + static bool blk_stack_atomic_writes_boundary_head(struct queue_limits *t, 531 + struct queue_limits *b) 532 + { 533 + /* 534 + * Ensure atomic write boundary is aligned with chunk sectors. Stacked 535 + * devices store chunk sectors in t->io_min. 536 + */ 537 + if (b->atomic_write_hw_boundary > t->io_min && 538 + b->atomic_write_hw_boundary % t->io_min) 539 + return false; 540 + if (t->io_min > b->atomic_write_hw_boundary && 541 + t->io_min % b->atomic_write_hw_boundary) 542 + return false; 543 + 544 + t->atomic_write_hw_boundary = b->atomic_write_hw_boundary; 545 + return true; 546 + } 547 + 548 + 549 + /* Check stacking of first bottom device */ 550 + static bool blk_stack_atomic_writes_head(struct queue_limits *t, 551 + struct queue_limits *b) 552 + { 553 + if (b->atomic_write_hw_boundary && 554 + !blk_stack_atomic_writes_boundary_head(t, b)) 555 + return false; 556 + 557 + if (t->io_min <= SECTOR_SIZE) { 558 + /* No chunk sectors, so use bottom device values directly */ 559 + t->atomic_write_hw_unit_max = b->atomic_write_hw_unit_max; 560 + t->atomic_write_hw_unit_min = b->atomic_write_hw_unit_min; 561 + t->atomic_write_hw_max = b->atomic_write_hw_max; 562 + return true; 563 + } 564 + 565 + /* 566 + * Find values for limits which work for chunk size. 567 + * b->atomic_write_hw_unit_{min, max} may not be aligned with chunk 568 + * size (t->io_min), as chunk size is not restricted to a power-of-2. 569 + * So we need to find highest power-of-2 which works for the chunk 570 + * size. 571 + * As an example scenario, we could have b->unit_max = 16K and 572 + * t->io_min = 24K. For this case, reduce t->unit_max to a value 573 + * aligned with both limits, i.e. 8K in this example. 574 + */ 575 + t->atomic_write_hw_unit_max = b->atomic_write_hw_unit_max; 576 + while (t->io_min % t->atomic_write_hw_unit_max) 577 + t->atomic_write_hw_unit_max /= 2; 578 + 579 + t->atomic_write_hw_unit_min = min(b->atomic_write_hw_unit_min, 580 + t->atomic_write_hw_unit_max); 581 + t->atomic_write_hw_max = min(b->atomic_write_hw_max, t->io_min); 582 + 583 + return true; 584 + } 585 + 586 + static void blk_stack_atomic_writes_limits(struct queue_limits *t, 587 + struct queue_limits *b) 588 + { 589 + if (!(t->features & BLK_FEAT_ATOMIC_WRITES_STACKED)) 590 + goto unsupported; 591 + 592 + if (!b->atomic_write_unit_min) 593 + goto unsupported; 594 + 595 + /* 596 + * If atomic_write_hw_max is set, we have already stacked 1x bottom 597 + * device, so check for compliance. 598 + */ 599 + if (t->atomic_write_hw_max) { 600 + if (!blk_stack_atomic_writes_tail(t, b)) 601 + goto unsupported; 602 + return; 603 + } 604 + 605 + if (!blk_stack_atomic_writes_head(t, b)) 606 + goto unsupported; 607 + return; 608 + 609 + unsupported: 610 + t->atomic_write_hw_max = 0; 611 + t->atomic_write_hw_unit_max = 0; 612 + t->atomic_write_hw_unit_min = 0; 613 + t->atomic_write_hw_boundary = 0; 614 + t->features &= ~BLK_FEAT_ATOMIC_WRITES_STACKED; 615 + } 616 + 504 617 /** 505 618 * blk_stack_limits - adjust queue_limits for stacked devices 506 619 * @t: the stacking driver limits (top device) ··· 774 661 t->zone_write_granularity = 0; 775 662 t->max_zone_append_sectors = 0; 776 663 } 664 + blk_stack_atomic_writes_limits(t, b); 665 + 777 666 return ret; 778 667 } 779 668 EXPORT_SYMBOL(blk_stack_limits);
+4
include/linux/blkdev.h
··· 333 333 #define BLK_FEAT_RAID_PARTIAL_STRIPES_EXPENSIVE \ 334 334 ((__force blk_features_t)(1u << 15)) 335 335 336 + /* stacked device can/does support atomic writes */ 337 + #define BLK_FEAT_ATOMIC_WRITES_STACKED \ 338 + ((__force blk_features_t)(1u << 16)) 339 + 336 340 /* 337 341 * Flags automatically inherited when stacking limits. 338 342 */