sched/wait: avoid abort_exclusive_wait() in __wait_on_bit_lock()
A message that is not in the archive (sent off-list or not yet received): [email protected]
Quoted 29 lines
Quoted 6 lines · Oleg Nesterov
We do not need anything tricky to avoid the race, we can just call
finish_wait()ifaction()fails.test_and_set_bit()impliesmb()so the locklesslist_empty_careful()case is fine, we can not miss the condition if we race withunlock_page().To simplify the review see the code with the patch applied:
int
__sched
__wait_on_bit_lock(wait_queue_head_t *wq, structwait_bit_queue*q,
wait_bit_action_f*action, unsigned mode){ int ret = 0; for (;;) { prepare_to_wait_exclusive(wq, &q->wait, mode); if (test_bit(q->key.bit_nr, q->key.flags)) { ret = action(&q->key, mode); /* * Ensure that clear_bit() + wake_up() right after * test_and_set_bit() below can't see us; it should * wake up another exclusive waiter if we fail. */ if (ret) finish_wait(wq, &q->wait); } if (!test_and_set_bit(q->key.bit_nr, q->key.flags)) {
Quoted 9 lines
if (!ret)
finish_wait(wq, &q->wait);
return 0;
} else if (ret) {
return ret;
}
}
}Can we fold two bit operations into one?
thanks
Hillf
kernel/sched/wait.c +8 -11
@@ -425,20 +425,17 @@ int __sched __wait_on_bit_lock(wait_queue_head_t *wq, struct wait_bit_queue *q, wait_bit_action_f *action, unsigned mode) { -do { -int ret; +int ret =0 ; -prepare_to_wait_exclusive (wq, &q->wait, mode); -if (!test_bit (q->key.bit_nr, q->key.flags)) -continue ; +prepare_to_wait_exclusive (wq, &q->wait, mode); +do { +if (!test_and_set_bit (q->key.bit_nr, q->key.flags)) +break ; ret =action (&q->key, mode); -if (!ret) -continue ; -abort_exclusive_wait (wq, &q->wait, mode, &q->key); -return ret; - }while (test_and_set_bit (q->key.bit_nr, q->key.flags)); + }while (!ret); +finish_wait (wq, &q->wait); -return 0 ; +return ret; }EXPORT_SYMBOL (__wait_on_bit_lock);
--
Quoted 29 lines · Hillf Danton
Quoted 20 lines
Quoted 2 lines · Oleg Nesterov
__wait_on_bit_lock(wait_queue_head_t *wq, struct
wait_bit_queue*q,
wait_bit_action_f*action, unsigned mode){ int ret = 0; for (;;) { prepare_to_wait_exclusive(wq, &q->wait, mode); if (test_bit(q->key.bit_nr, q->key.flags)) { ret = action(&q->key, mode); /* * Ensure that clear_bit() + wake_up() right after * test_and_set_bit() below can't see us; it should * wake up another exclusive waiter if we fail. */ if (ret) finish_wait(wq, &q->wait); } if (!test_and_set_bit(q->key.bit_nr, q->key.flags)) {Quoted 9 lines
if (!ret) finish_wait(wq, &q->wait); return 0; } else if (ret) { return ret; } } }
Quoted 1 line
Can we fold two bit operations into one?
Yes, we can, but I am not sure we want. I guess the first test_bit() is optimization to avoid the locked test_and_set_bit() which should likely fail.
Quoted 12 lines
__wait_on_bit_lock(wait_queue_head_t *wq, struct
wait_bit_queue*q,kernel/sched/wait.c +3
-5wait_bit_action_f *action, unsigned mode) { - do { - int ret; + int ret = 0; - prepare_to_wait_exclusive(wq, &q->wait, mode); - if (!test_bit(q->key.bit_nr, q->key.flags)) - continue; +prepare_to_wait_exclusive (wq, &q->wait, mode); + do {
kernel/sched/wait.c int __sched · line 436
Quoted 10 lines
kernel/sched/wait.c +4
-5+if (!test_and_set_bit (q->key.bit_nr, q->key.flags)) +break ; ret =action (&q->key, mode); -if (!ret) -continue ; -abort_exclusive_wait (wq, &q->wait, mode, &q->key); -return ret; - }while (test_and_set_bit (q->key.bit_nr, q->key.flags)); + }while (!ret); +
No, no, this is wrong.
We can't simply remove abort_exclusive_wait(), please read the comment above this function and the comment added by this patch.
And we can't move prepare_to_wait_exclusive() outside of the main loop.
Oleg.
Quoted 8 lines
kernel/sched/wait.c +1
-2finish_wait (wq, &q->wait); -return 0 ; +return ret; }EXPORT_SYMBOL (__wait_on_bit_lock); --