[PATCH v8 11/15] futex: Acquire a hash reference in futex_wait_multiple_setup().
From: Sebastian Andrzej Siewior
Date: Mon Feb 03 2025 - 09:03:04 EST
futex_wait_multiple_setup() changes task_struct::__state to
!TASK_RUNNING and then enqueues on multiple futexes. Every
futex_q_lock() acquires a reference on the global hash which is dropped
later.
If a rehash is in progress then the loop will block on
mm_struct::futex_hash_bucket for the rehash to complete and this will
lose the previously set task_struct::__state.
Acquire a reference on the local hash to avoiding blocking on
mm_struct::futex_hash_bucket.
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@xxxxxxxxxxxxx>
---
kernel/futex/core.c | 10 ++++++++++
kernel/futex/futex.h | 2 ++
kernel/futex/waitwake.c | 21 +++++++++++++++++++--
3 files changed, 31 insertions(+), 2 deletions(-)
diff --git a/kernel/futex/core.c b/kernel/futex/core.c
index b0fb2b10a387c..7130019aa9ec6 100644
--- a/kernel/futex/core.c
+++ b/kernel/futex/core.c
@@ -129,6 +129,11 @@ static struct futex_hash_bucket *futex_hash_private(union futex_key *key,
return &fhb[hash & hash_mask];
}
+struct futex_private_hash *futex_get_private_hash(void)
+{
+ return NULL;
+}
+
/**
* futex_hash - Return the hash bucket in the global or local hash
* @key: Pointer to the futex key for which the hash is calculated
@@ -152,6 +157,11 @@ struct futex_hash_bucket *futex_hash(union futex_key *key)
return &futex_queues[hash & (futex_hashsize - 1)];
}
+bool futex_put_private_hash(struct futex_private_hash *hb_p)
+{
+ return false;
+}
+
void futex_hash_put(struct futex_hash_bucket *hb)
{
}
diff --git a/kernel/futex/futex.h b/kernel/futex/futex.h
index 5b33016648ecd..d6fa6f663d9ad 100644
--- a/kernel/futex/futex.h
+++ b/kernel/futex/futex.h
@@ -205,6 +205,8 @@ futex_setup_timer(ktime_t *time, struct hrtimer_sleeper *timeout,
extern struct futex_hash_bucket *futex_hash(union futex_key *key);
extern void futex_hash_get(struct futex_hash_bucket *hb);
extern void futex_hash_put(struct futex_hash_bucket *hb);
+extern struct futex_private_hash *futex_get_private_hash(void);
+extern bool futex_put_private_hash(struct futex_private_hash *hb_p);
/**
* futex_match - Check whether two futex keys are equal
diff --git a/kernel/futex/waitwake.c b/kernel/futex/waitwake.c
index 98409ba9605a8..3d57b47692f57 100644
--- a/kernel/futex/waitwake.c
+++ b/kernel/futex/waitwake.c
@@ -395,7 +395,7 @@ int futex_unqueue_multiple(struct futex_vector *v, int count)
}
/**
- * futex_wait_multiple_setup - Prepare to wait and enqueue multiple futexes
+ * __futex_wait_multiple_setup - Prepare to wait and enqueue multiple futexes
* @vs: The futex list to wait on
* @count: The size of the list
* @woken: Index of the last woken futex, if any. Used to notify the
@@ -410,7 +410,7 @@ int futex_unqueue_multiple(struct futex_vector *v, int count)
* - 0 - Success
* - <0 - -EFAULT, -EWOULDBLOCK or -EINVAL
*/
-int futex_wait_multiple_setup(struct futex_vector *vs, int count, int *woken)
+static int __futex_wait_multiple_setup(struct futex_vector *vs, int count, int *woken)
{
struct futex_hash_bucket *hb;
bool retry = false;
@@ -499,6 +499,23 @@ int futex_wait_multiple_setup(struct futex_vector *vs, int count, int *woken)
return 0;
}
+int futex_wait_multiple_setup(struct futex_vector *vs, int count, int *woken)
+{
+ struct futex_private_hash *hb_p;
+ int ret;
+
+ /*
+ * Assume to have a private futex and acquire a reference on the private
+ * hash to avoid blocking on mm_struct::futex_hash_bucket during rehash
+ * after changing the task state.
+ */
+ hb_p = futex_get_private_hash();
+ ret = __futex_wait_multiple_setup(vs, count, woken);
+ if (hb_p)
+ futex_put_private_hash(hb_p);
+ return ret;
+}
+
/**
* futex_sleep_multiple - Check sleeping conditions and sleep
* @vs: List of futexes to wait for
--
2.47.2