transfer_surpluses() warns and then keeps going when the donation
calculation produced invalid weights.  The following propagation can then
divide by a zero hweight_inuse, so the warning is immediately followed by a
divide error.

When the check trips, abandon this period's donation transfer and release
the walk lists.  This trades one period without donation for not crashing;
the invalid state is not expected on the normal path.

Developed with assistance from Claude Code (Anthropic): the assistant
analysed the failure path, drafted the change and ran the checks listed
below.  The author reviewed the result and remains responsible for it.

Fixes: e08d02aa5fc9 ("blk-iocost: implement Andy's method for donation weight 
updates")
Cc: [email protected]
Signed-off-by: Kunhai Dai <[email protected]>
---
The failure was originally observed on a vendor 4.18 kernel and reproduced
on v7.3-rc7 by a kprobe widening the current_hweight() publication window.
The guard is a fail-safe: a hand-constructed input can trigger the divide
at the function level, but runtime reachability without a race is not
confirmed, so this patch does not claim to fix a reachable no-race divide.
---
 block/blk-iocost.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/block/blk-iocost.c b/block/blk-iocost.c
index 2745bffcd..7b8af094a 100644
--- a/block/blk-iocost.c
+++ b/block/blk-iocost.c
@@ -1956,6 +1956,7 @@ static void transfer_surpluses(struct list_head 
*surpluses, struct ioc_now *now)
                        pr_cont(": active=%u donating=%u after=%u\n",
                                iocg->hweight_active, iocg->hweight_donating,
                                iocg->hweight_after_donation);
+                       goto out;
                }
        }
 
@@ -2054,6 +2055,7 @@ static void transfer_surpluses(struct list_head 
*surpluses, struct ioc_now *now)
                __propagate_weights(iocg, iocg->active, inuse, true, now);
        }
 
+out:
        /* walk list should be dissolved after use */
        list_for_each_entry_safe(iocg, tiocg, &inner_walk, walk_list)
                list_del_init(&iocg->walk_list);
-- 
2.27.0


Reply via email to