Skip to contentSkip to conversation

sched/wait: avoid abort_exclusive_wait() in __wait_on_bit_lock()

PATCH Hillf Danton 2 participants 2 messages Aug 29, 2016 1 message not in the archive Scheduler

lore
Share & tools
lore

A message that is not in the archive (sent off-list or not yet received): [email protected]

#1 lore raw
Quoted 29 lines
Quoted 6 lines · Oleg Nesterov

We do not need anything tricky to avoid the race, we can just call finish_wait() if action() fails. test_and_set_bit() implies mb() so the lockless list_empty_careful() case is fine, we can not miss the condition if we race with unlock_page().

To simplify the review see the code with the patch applied:

int __sched
__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;
		}
	}
}

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);
 

--

#2 lore raw
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 -5
 			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 {

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 -2
 	finish_wait(wq, &q->wait);
-	return 0;
+	return ret;
 }
 EXPORT_SYMBOL(__wait_on_bit_lock);
 
--