[Deepin-Kernel-SIG] [linux 6.6-y] [Upstream] block: improve struct request_queue layout && md: remove rcu protection to access rdev from conf#1996
Conversation
mainline inclusion from mainline-v6.8-rc1 category: performance It's clearly been a while since someone looked at this, so I gave it a quick shot. There are few issues in here: - Random bundling of members that are mostly read-only and often written - Random holes that need not be there This moves the most frequently used bits into cacheline 1 and 2, with the 2nd one being more write intensive than the first one, which is basically read-only. Outside of making this work a bit more efficiently, it also reduces the size of struct request_queue for my test setup from 864 bytes (spanning 14 cachelines!) to 832 bytes and 13 cachelines. Reviewed-by: Christoph Hellwig <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Jens Axboe <[email protected]> (cherry picked from commit 0c734c5) Signed-off-by: Wentao Guan <[email protected]>
mainline inclusion from mainline-v6.8-rc1 categroy: other Allow using a few symbols with IS_ENABLED instead of #idef by moving the declarations out of #idef CONFIG_BLK_DEV_ZONED, and move bdev_nr_zones into the remaining #idef CONFIG_BLK_DEV_ZONED, #else block below. Signed-off-by: Christoph Hellwig <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Jens Axboe <[email protected]> (cherry picked from commit 668bfee) Signed-off-by: Wentao Guan <[email protected]>
mainline inclusion from mainline-v6.7-rc1 category: other There are no functional changes, just to make the code simpler and prepare to delay remove_and_add_spares() to md_start_sync(). Signed-off-by: Yu Kuai <[email protected]> Signed-off-by: Song Liu <[email protected]> Link: https://lore.kernel.org/r/[email protected] (cherry picked from commit 3389d57) Signed-off-by: Wentao Guan <[email protected]>
mainline inclusion
from mainline-v6.8-rc1
category: other
rcu is not used correctly here, because synchronize_rcu() is called
before replacing old value, for example:
remove_and_add_spares // other path
synchronize_rcu
// called before replacing old value
set_bit(RemoveSynchronized)
rcu_read_lock()
rdev = conf->mirros[].rdev
pers->hot_remove_disk
conf->mirros[].rdev = NULL;
if (!test_bit(RemoveSynchronized))
synchronize_rcu
/*
* won't be called, and won't wait
* for concurrent readers to be done.
*/
// access rdev after remove_and_add_spares()
rcu_read_unlock()
Fortunately, there is a separate rcu protection to prevent such rdev
to be freed:
md_kick_rdev_from_array //other path
rcu_read_lock()
rdev = conf->mirros[].rdev
list_del_rcu(&rdev->same_set)
rcu_read_unlock()
/*
* rdev can be removed from conf, but
* rdev won't be freed.
*/
synchronize_rcu()
free rdev
Hence remove this useless flag and prepare to remove rcu protection to
access rdev from 'conf'.
Signed-off-by: Yu Kuai <[email protected]>
Signed-off-by: Song Liu <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
(cherry picked from commit c891f1f)
Signed-off-by: Wentao Guan <[email protected]>
mainline inclusion from mainline-v6.8-rc1 category: other Because it's safe to accees rdev from conf: - If any spinlock is held, because synchronize_rcu() from md_kick_rdev_from_array() will prevent 'rdev' to be freed until spinlock is released; - If 'reconfig_lock' is held, because rdev can't be added or removed from array; - If there is normal IO inflight, because mddev_suspend() will prevent rdev to be added or removed from array; - If there is sync IO inflight, because 'MD_RECOVERY_RUNNING' is checked in remove_and_add_spares(). And these will cover all the scenarios in raid10. This patch also cleanup the code to handle the case that replacement replace rdev while IO is still inflight. Signed-off-by: Yu Kuai <[email protected]> Signed-off-by: Song Liu <[email protected]> Link: https://lore.kernel.org/r/[email protected] (cherry picked from commit a448af2) Signed-off-by: Wentao Guan <[email protected]>
mainline inclusion from mainline-v6.8-rc1 category: other Because it's safe to accees rdev from conf: - If any spinlock is held, because synchronize_rcu() from md_kick_rdev_from_array() will prevent 'rdev' to be freed until spinlock is released; - If 'reconfig_lock' is held, because rdev can't be added or removed from array; - If there is normal IO inflight, because mddev_suspend() will prevent rdev to be added or removed from array; - If there is sync IO inflight, because 'MD_RECOVERY_RUNNING' is checked in remove_and_add_spares(). And these will cover all the scenarios in raid1. Signed-off-by: Yu Kuai <[email protected]> Signed-off-by: Song Liu <[email protected]> Link: https://lore.kernel.org/r/[email protected] (cherry picked from commit 2d32777) Signed-off-by: Wentao Guan <[email protected]>
mainline inclusion from mainline-v6.8-rc1 category: other Because it's safe to accees rdev from conf: - If any spinlock is held, because synchronize_rcu() from md_kick_rdev_from_array() will prevent 'rdev' to be freed until spinlock is released; - If 'reconfig_lock' is held, because rdev can't be added or removed from array; - If there is normal IO inflight, because mddev_suspend() will prevent rdev to be added or removed from array; - If there is sync IO inflight, because 'MD_RECOVERY_RUNNING' is checked in remove_and_add_spares(). And these will cover all the scenarios in raid456. Signed-off-by: Yu Kuai <[email protected]> Signed-off-by: Song Liu <[email protected]> Link: https://lore.kernel.org/r/[email protected] (cherry picked from commit ad86067) Signed-off-by: Wentao Guan <[email protected]>
mainline inclusion from mainline-v6.8-rc1 category: other Because it's safe to accees rdev from conf: - If any spinlock is held, because synchronize_rcu() from md_kick_rdev_from_array() will prevent 'rdev' to be freed until spinlock is released; - If there is normal IO inflight, because mddev_suspend() will prevent rdev to be added or removed from array; And these will cover all the scenarios in md-multipath. Signed-off-by: Yu Kuai <[email protected]> Signed-off-by: Song Liu <[email protected]> Link: https://lore.kernel.org/r/[email protected] (cherry picked from commit 7ecab28) Signed-off-by: Wentao Guan <[email protected]>
mainline inclusion from mainline-v6.11-rc1 category: bugfix As commit ad86067 ("md/raid5: remove rcu protection to access rdev from conf") explains, rcu protection can be removed, however, there are three places left, there won't be any real problems. drivers/md/raid5.c:8071:24: error: incompatible types in comparison expression (different address spaces): drivers/md/raid5.c:8071:24: struct md_rdev [noderef] __rcu * drivers/md/raid5.c:8071:24: struct md_rdev * drivers/md/raid5.c:7569:25: error: incompatible types in comparison expression (different address spaces): drivers/md/raid5.c:7569:25: struct md_rdev [noderef] __rcu * drivers/md/raid5.c:7569:25: struct md_rdev * drivers/md/raid5.c:7573:25: error: incompatible types in comparison expression (different address spaces): drivers/md/raid5.c:7573:25: struct md_rdev [noderef] __rcu * drivers/md/raid5.c:7573:25: struct md_rdev * Fixes: ad86067 ("md/raid5: remove rcu protection to access rdev from conf") Cc: [email protected] Signed-off-by: Yu Kuai <[email protected]> Signed-off-by: Song Liu <[email protected]> Link: https://lore.kernel.org/r/[email protected] (cherry picked from commit 2314c2e) Signed-off-by: Wentao Guan <[email protected]>
Reviewer's GuideRefactors md RAID personalities and block queue structures to remove RCU-based protection for accessing rdevs from in-memory configurations, relying instead on higher-level locking and inflight IO guarantees, while simplifying request_queue layout and zoned-block APIs. Sequence diagram for rdev lifecycle and removal without RCU from confsequenceDiagram
participant U as User_or_daemon
participant mddev as mddev
participant rdev as md_rdev
participant conf as md_personality_conf
U->>mddev: md_import_device()
activate mddev
mddev->>mddev: mddev_lock()
mddev->>rdev: kzalloc(md_rdev)
mddev->>rdev: rdev->bdev = blkdev_get_by_dev()
mddev->>mddev: mddev_unlock()
deactivate mddev
U->>mddev: bind_rdev_to_array()
activate mddev
mddev->>mddev: mddev_lock()
mddev->>mddev: kobject_add(rdev->kobj, mddev->kobj)
mddev->>mddev: list_add_rcu(rdev->same_set, mddev->disks)
mddev->>mddev: mddev_unlock()
deactivate mddev
U->>mddev: remove_and_add_spares()
activate mddev
mddev->>mddev: mddev_lock()
loop for_each_rdev
mddev->>mddev: rdev_removeable(rdev)
alt removable
mddev->>mddev: pers->hot_remove_disk(mddev, rdev)
mddev->>conf: /* conf->rdev/replacement updated with WRITE_ONCE, no RCU */
mddev->>mddev: sysfs_unlink_rdev(mddev, rdev)
mddev->>rdev: rdev->saved_raid_disk = rdev->raid_disk
mddev->>rdev: rdev->raid_disk = -1
end
end
mddev->>mddev: mddev_unlock()
deactivate mddev
U->>mddev: md_kick_rdev_from_array()
activate mddev
mddev->>mddev: mddev_lock()
mddev->>mddev: list_del_rcu(rdev->same_set)
mddev->>mddev: synchronize_rcu()
mddev->>mddev: list_add(rdev->same_set, mddev->deleting)
mddev->>mddev: export_rdev(rdev)
mddev->>mddev: mddev_unlock()
deactivate mddev
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
There was a problem hiding this comment.
Hey - I've left some high level feedback:
- Now that rdev/replacement pointers are no longer RCU-protected, consider consistently using READ_ONCE/WRITE_ONCE for all unsynchronized pointer loads/stores (not just status paths) to make the concurrency assumptions explicit and avoid accidental compiler reordering.
- The new helper is named rdev_removeable(); for clarity and consistency with existing terminology, consider renaming it to rdev_removable() (and updating call sites) to avoid the spelling mismatch.
- You’ve added lockdep_assert_held() in some paths that rely on mddev->lock or reconfig_mutex instead of RCU; it may be worth auditing other code that now directly dereferences conf->*rdev fields and adding similar assertions where lock-based protection is assumed, to document and verify those invariants.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Now that rdev/replacement pointers are no longer RCU-protected, consider consistently using READ_ONCE/WRITE_ONCE for all unsynchronized pointer loads/stores (not just status paths) to make the concurrency assumptions explicit and avoid accidental compiler reordering.
- The new helper is named rdev_removeable(); for clarity and consistency with existing terminology, consider renaming it to rdev_removable() (and updating call sites) to avoid the spelling mismatch.
- You’ve added lockdep_assert_held() in some paths that rely on mddev->lock or reconfig_mutex instead of RCU; it may be worth auditing other code that now directly dereferences conf->*rdev fields and adding similar assertions where lock-based protection is assumed, to document and verify those invariants.Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
There was a problem hiding this comment.
Pull request overview
Note
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
This PR removes RCU-based protection around conf->rdev access across multiple md personalities (raid1/10/5 and multipath), replaces it with direct / READ_ONCE / WRITE_ONCE accesses plus lockdep assertions, and also reorganizes struct request_queue layout and zoned block prototypes.
Changes:
- Remove
RemoveSynchronizedflag and simplify md core spare remove logic via a newrdev_removeable()helper. - Drop
rcu_read_lock()/rcu_dereference()usage forconf->{rdev,replacement}across raid1/10/5/multipath; addlockdep_assert_held()in config/status printing. - Rework block layer header layout: reorganize
struct request_queuefields and adjust zoned API declarations/guards.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| include/linux/blkdev.h | Reorders struct request_queue fields; adjusts zoned API declarations/guards. |
| drivers/md/md.h | Removes RemoveSynchronized flag bit. |
| drivers/md/md.c | Adds rdev_removeable() helper; simplifies remove_and_add_spares() logic. |
| drivers/md/md-multipath.c | Removes RCU around conf->multipaths[].rdev accesses; switches to READ_ONCE/WRITE_ONCE and lockdep assertions. |
| drivers/md/raid1.c | Removes RCU around conf->mirrors[].rdev accesses; adds lockdep assertions; uses WRITE_ONCE in updates. |
| drivers/md/raid10.c | Removes RCU and ordering barriers around mirrors[].{rdev,replacement} access and transitions; adds lockdep assertions. |
| drivers/md/raid5.h | Converts disk_info.{rdev,replacement} from __rcu pointers to plain pointers. |
| drivers/md/raid5.c | Removes RCU dereferences/locks across many paths; uses READ_ONCE/WRITE_ONCE in some updates; removes helper deref wrappers and ordering barriers. |
| drivers/md/raid5-cache.c | Removes RCU sections around disk pointer access during recovery and cache checks. |
| drivers/md/raid5-ppl.c | Removes RCU dereference usage in PPL recovery/init paths. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| rdev = replacement ? conf->mirrors[devnum].replacement : | ||
| conf->mirrors[devnum].rdev; | ||
|
|
||
| mbio = bio_alloc_clone(rdev->bdev, bio, GFP_NOIO, &mddev->bio_set); |
| rdev = repl ? conf->mirrors[dev].replacement : | ||
| conf->mirrors[dev].rdev; | ||
|
|
||
| raid_end_discard_bio(r10_bio); | ||
| rdev_dec_pending(rdev, conf->mddev); |
| rdev = repl ? conf->mirrors[d].replacement : | ||
| conf->mirrors[d].rdev; | ||
|
|
||
| if (bio->bi_status) { |
| WRITE_ONCE(*rdevp, NULL); | ||
| if (!err) { | ||
| err = log_modify(conf, rdev, false); | ||
| if (err) | ||
| goto abort; | ||
| } | ||
|
|
||
| tmp = rcu_access_pointer(p->replacement); | ||
| tmp = p->replacement; | ||
| if (tmp) { | ||
| /* We must have just cleared 'rdev' */ | ||
| rcu_assign_pointer(p->rdev, tmp); | ||
| WRITE_ONCE(p->rdev, tmp); | ||
| clear_bit(Replacement, &tmp->flags); | ||
| smp_mb(); /* Make sure other CPUs may see both as identical | ||
| * but will never see neither - if they are careful | ||
| */ | ||
| rcu_assign_pointer(p->replacement, NULL); | ||
| WRITE_ONCE(p->replacement, NULL); |
| rdev = conf->disks[i].rdev; | ||
| rrdev = conf->disks[i].replacement; |
| } | ||
| EXPORT_SYMBOL_GPL(md_do_sync); | ||
|
|
||
| static bool rdev_removeable(struct md_rdev *rdev) |
| if (test_bit(Blocked, &rdev->flags)) | ||
| return false; | ||
|
|
||
| /* Fautly rdev is not used, it's safe to remove it. */ |
Link: https://lore.kernel.org/all/[email protected]/#r
From: Yu Kuai [email protected]
Changes in v3:
while 'reconfig_mutex' is held, it's safe to remove
rcu_read_lock/unlock() directly.
Changes in v2:
The lifetime of rdev:
md_import_device() generate a rdev based on underlying disk;
mddev_lock()
rdev = kzalloc();
rdev->bdev = blkdev_get_by_dev();
mddev_unlock()
bind_rdev_to_array() add this rdev to mddev->disks;
mddev_lock()
kobject_add(&rdev->kobj, &mddev->kobj, ...);
list_add_rcu(&rdev->same_set, &mddev->disks);
mddev_unlock()
remove_and_add_spares() add this rdev to conf;
mddev_lock()
rdev_addable();
pers->hot_add_disk();
rcu_assign_pointer(conf->rdev, rdev);
mddev_unlock()
Use this array with rdev;
remove_and_add_spares() remove rdev from conf;
// triggered by sysfs/ioctl
mddev_lock()
rdev_removeable();
pers->hot_remove_disk();
rcu_assign_pointer(conf->rdev, NULL);
synchronize_rcu();
mddev_unlock()
// triggered by daemon
mddev_lock()
rdev_removeable();
synchronize_rcu(); -> this can't protect accessing rdev from conf
pers->hot_remove_disk();
rcu_assign_pointer(conf->rdev, NULL);
mddev_unlock()
md_kick_rdev_from_array() remove rdev from mddev->disks;
mddev_lock()
list_del_rcu(&rdev->same_set);
synchronize_rcu();
list_add(&rdev->same_set, &mddev->deleting)
mddev_unlock()
export_rdev
There are two separate rcu protection for rdev, and this pathset remove
the protection of conf(step 3 and 5), because it's safe to access rdev
from conf in following cases:
conf;
IO to be done and prevent rdev to be added or removed to conf;
called from daemon thread when sync thread is done, and
'MD_RECOVERY_RUNNING' is also checked for ioctl/sysfs;
from step 6 prevent rdev to be freed until spinlock is released or
rcu_read_unlock();
Yu Kuai (5):
md: remove flag RemoveSynchronized
md/raid10: remove rcu protection to access rdev from conf
md/raid1: remove rcu protection to access rdev from conf
md/raid5: remove rcu protection to access rdev from conf
md/md-multipath: remove rcu protection to access rdev from conf
drivers/md/md-multipath.c | 32 +++---
drivers/md/md.c | 37 ++-----
drivers/md/md.h | 5 -
drivers/md/raid1.c | 71 ++++--------
drivers/md/raid10.c | 222 ++++++++++----------------------------
drivers/md/raid5-cache.c | 11 +-
drivers/md/raid5-ppl.c | 16 +--
drivers/md/raid5.c | 191 +++++++++++---------------------
drivers/md/raid5.h | 4 +-
9 files changed, 168 insertions(+), 421 deletions(-)
Summary by Sourcery
Simplify md RAID and multipath device handling by removing RCU-based protection around conf->rdev access and reworking device removal rules, while also tightening block queue and zoned block device interfaces.
Bug Fixes:
Enhancements: