futex: Allow automatic allocation of process wide futex hash
authorSebastian Andrzej Siewior <bigeasy@linutronix.de>
Wed, 16 Apr 2025 16:29:13 +0000 (18:29 +0200)
committerPeter Zijlstra <peterz@infradead.org>
Sat, 3 May 2025 10:02:08 +0000 (12:02 +0200)
Allocate a private futex hash with 16 slots if a task forks its first
thread.

Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lore.kernel.org/r/20250416162921.513656-14-bigeasy@linutronix.de
include/linux/futex.h
kernel/fork.c
kernel/futex/core.c

index 8f1be08bef18db258202d0bd79e97dc8f51433cd..1d3f7555825ecf08a238070067ef249017d82c04 100644 (file)
@@ -80,6 +80,7 @@ long do_futex(u32 __user *uaddr, int op, u32 val, ktime_t *timeout,
 int futex_hash_prctl(unsigned long arg2, unsigned long arg3, unsigned long arg4);
 
 #ifdef CONFIG_FUTEX_PRIVATE_HASH
+int futex_hash_allocate_default(void);
 void futex_hash_free(struct mm_struct *mm);
 
 static inline void futex_mm_init(struct mm_struct *mm)
@@ -88,6 +89,7 @@ static inline void futex_mm_init(struct mm_struct *mm)
 }
 
 #else /* !CONFIG_FUTEX_PRIVATE_HASH */
+static inline int futex_hash_allocate_default(void) { return 0; }
 static inline void futex_hash_free(struct mm_struct *mm) { }
 static inline void futex_mm_init(struct mm_struct *mm) { }
 #endif /* CONFIG_FUTEX_PRIVATE_HASH */
@@ -107,6 +109,10 @@ static inline int futex_hash_prctl(unsigned long arg2, unsigned long arg3, unsig
 {
        return -EINVAL;
 }
+static inline int futex_hash_allocate_default(void)
+{
+       return 0;
+}
 static inline void futex_hash_free(struct mm_struct *mm) { }
 static inline void futex_mm_init(struct mm_struct *mm) { }
 
index 831dfec450544436fd5fda9772af58c33f86553e..1f5d8083eeb25d565dc3fc719b9da400bf99e359 100644 (file)
@@ -2164,6 +2164,13 @@ static void rv_task_fork(struct task_struct *p)
 #define rv_task_fork(p) do {} while (0)
 #endif
 
+static bool need_futex_hash_allocate_default(u64 clone_flags)
+{
+       if ((clone_flags & (CLONE_THREAD | CLONE_VM)) != (CLONE_THREAD | CLONE_VM))
+               return false;
+       return true;
+}
+
 /*
  * This creates a new process as a copy of the old one,
  * but does not actually start it yet.
@@ -2544,6 +2551,21 @@ __latent_entropy struct task_struct *copy_process(
        if (retval)
                goto bad_fork_cancel_cgroup;
 
+       /*
+        * Allocate a default futex hash for the user process once the first
+        * thread spawns.
+        */
+       if (need_futex_hash_allocate_default(clone_flags)) {
+               retval = futex_hash_allocate_default();
+               if (retval)
+                       goto bad_fork_core_free;
+               /*
+                * If we fail beyond this point we don't free the allocated
+                * futex hash map. We assume that another thread will be created
+                * and makes use of it. The hash map will be freed once the main
+                * thread terminates.
+                */
+       }
        /*
         * From this point on we must avoid any synchronous user-space
         * communication until we take the tasklist-lock. In particular, we do
index 818df7420a1a9054e73f9be5b499948e92df241a..53b3a00a92539b38f49da65883b822cd5f76d80d 100644 (file)
@@ -1294,6 +1294,17 @@ static int futex_hash_allocate(unsigned int hash_slots, bool custom)
        return 0;
 }
 
+int futex_hash_allocate_default(void)
+{
+       if (!current->mm)
+               return 0;
+
+       if (current->mm->futex_phash)
+               return 0;
+
+       return futex_hash_allocate(16, false);
+}
+
 static int futex_hash_get_slots(void)
 {
        struct futex_private_hash *fph;