summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-10-27 13:26:19 +0000
committerMark Brown <broonie@kernel.org>2026-10-27 13:26:19 +0000
commitd4be97694d2ff9a4c8db7658eb08da191a64f16d (patch)
treeee1c5ea904e1dd2a6d43568f3e90a9c9f04876af
parent565024bc4970d6ac541bf10bff413870c5aba160 (diff)
parent17c6912e81b271ba31a747f0f5493100bbaab7e0 (diff)
downloadlinux-next-d4be97694d2ff9a4c8db7658eb08da191a64f16d.tar.gz
linux-next-d4be97694d2ff9a4c8db7658eb08da191a64f16d.zip
Merge branch 'for-next' of https://git.kernel.org/pub/scm/linux/kernel/git/s390/linux.git
-rw-r--r--arch/s390/Kconfig1
-rw-r--r--arch/s390/appldata/appldata_base.c12
-rw-r--r--arch/s390/include/asm/futex_robust.h25
-rw-r--r--arch/s390/include/asm/ptrace.h4
-rw-r--r--arch/s390/include/asm/syscall.h1
-rw-r--r--arch/s390/kernel/ptrace.c28
-rw-r--r--arch/s390/kernel/syscall.c20
-rw-r--r--arch/s390/kernel/vdso.c19
-rw-r--r--arch/s390/kernel/vdso/Makefile7
-rwxr-xr-xarch/s390/kernel/vdso/gen_vdso_offsets.sh3
-rw-r--r--arch/s390/kernel/vdso/vdso.lds.S3
-rw-r--r--arch/s390/kernel/vdso/vfutex.S22
12 files changed, 118 insertions, 27 deletions
diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig
index 7138d923d86f..840d81f7a750 100644
--- a/arch/s390/Kconfig
+++ b/arch/s390/Kconfig
@@ -213,6 +213,7 @@ config S390
select HAVE_DYNAMIC_FTRACE_WITH_REGS
select HAVE_EBPF_JIT if HAVE_MARCH_Z196_FEATURES
select HAVE_EFFICIENT_UNALIGNED_ACCESS
+ select HAVE_FUTEX_ROBUST_UNLOCK
select HAVE_GENERIC_TIF_BITS
select HAVE_GUP_FAST
select HAVE_FENTRY
diff --git a/arch/s390/appldata/appldata_base.c b/arch/s390/appldata/appldata_base.c
index 836c51da18f4..f10b2f040cc2 100644
--- a/arch/s390/appldata/appldata_base.c
+++ b/arch/s390/appldata/appldata_base.c
@@ -360,21 +360,25 @@ appldata_generic_handler(const struct ctl_table *ctl, int write,
pr_err("Starting the data collection for %s "
"failed with rc=%d\n", ops->name, rc);
module_put(ops->owner);
+ rc = -EIO;
} else
ops->active = 1;
} else if (!active && (ops->active == 1)) {
- ops->active = 0;
rc = appldata_diag(ops->record_nr, APPLDATA_STOP_REC,
(unsigned long) ops->data, ops->size,
ops->mod_lvl);
- if (rc != 0)
+ if (rc != 0) {
pr_err("Stopping the data collection for %s "
"failed with rc=%d\n", ops->name, rc);
- module_put(ops->owner);
+ rc = -EIO;
+ } else {
+ ops->active = 0;
+ module_put(ops->owner);
+ }
}
mutex_unlock(&appldata_ops_mutex);
module_put(ops->owner);
- return 0;
+ return rc;
}
/*************************** /proc stuff <END> *******************************/
diff --git a/arch/s390/include/asm/futex_robust.h b/arch/s390/include/asm/futex_robust.h
new file mode 100644
index 000000000000..6a5fb2506192
--- /dev/null
+++ b/arch/s390/include/asm/futex_robust.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ASM_S390_FUTEX_ROBUST_H
+#define _ASM_S390_FUTEX_ROBUST_H
+
+#include <asm/ptrace.h>
+
+#define VDSO_FUTEX_ROBUST_POP_GPR 4
+
+static __always_inline void __user *s390_futex_robust_unlock_get_pop(struct pt_regs *regs)
+{
+ /*
+ * Within critical section of
+ * __vdso_futex_robust_list64_try_unlock(__u32 *lock, __u32 tid, __u64 *pop)
+ *
+ * If condition code is 0, cmpxchg succeeded, and the pending
+ * op pointer in gpr4 needs to be returned for clearing.
+ */
+ if (psw_bits(regs->psw).cc != 0)
+ return NULL;
+ return (void __user *)regs->gprs[VDSO_FUTEX_ROBUST_POP_GPR];
+}
+
+#define arch_futex_robust_unlock_get_pop(regs) s390_futex_robust_unlock_get_pop(regs)
+
+#endif /* _ASM_S390_FUTEX_ROBUST_H */
diff --git a/arch/s390/include/asm/ptrace.h b/arch/s390/include/asm/ptrace.h
index 3e56ef8c268b..d8f3a2dbe689 100644
--- a/arch/s390/include/asm/ptrace.h
+++ b/arch/s390/include/asm/ptrace.h
@@ -15,14 +15,14 @@
#define PIF_SYSCALL 0 /* inside a system call */
#define PIF_PSW_ADDR_ADJUSTED 1 /* psw address has been adjusted */
-#define PIF_SYSCALL_RET_SET 2 /* return value was set via ptrace */
+#define PIF_SYSCALL_ENTRY 2 /* ptrace gpr2 access redirected to syscall nr */
#define PIF_FTRACE_FULL_REGS 3 /* all register contents valid (ftrace) */
#define PIF_WARN_TRAP_SKIPPED 4 /* __WARN_trap() was skipped */
#define PIF_PGM_CHECK 5 /* inside a program check */
#define _PIF_SYSCALL BIT(PIF_SYSCALL)
#define _PIF_ADDR_PSW_ADJUSTED BIT(PIF_PSW_ADDR_ADJUSTED)
-#define _PIF_SYSCALL_RET_SET BIT(PIF_SYSCALL_RET_SET)
+#define _PIF_SYSCALL_ENTRY BIT(PIF_SYSCALL_ENTRY)
#define _PIF_FTRACE_FULL_REGS BIT(PIF_FTRACE_FULL_REGS)
#define _PIF_WARN_TRAP_SKIPPED BIT(PIF_WARN_TRAP_SKIPPED)
#define _PIF_PGM_CHECK BIT(PIF_PGM_CHECK)
diff --git a/arch/s390/include/asm/syscall.h b/arch/s390/include/asm/syscall.h
index 5f310caad1fc..47f66202977f 100644
--- a/arch/s390/include/asm/syscall.h
+++ b/arch/s390/include/asm/syscall.h
@@ -59,7 +59,6 @@ static inline void syscall_set_return_value(struct task_struct *task,
struct pt_regs *regs,
int error, long val)
{
- set_pt_regs_flag(regs, PIF_SYSCALL_RET_SET);
regs->gprs[2] = error ? error : val;
}
diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c
index 5202f9a2d939..577fb2c3d5a8 100644
--- a/arch/s390/kernel/ptrace.c
+++ b/arch/s390/kernel/ptrace.c
@@ -204,15 +204,24 @@ static unsigned long __peek_user(struct task_struct *child, addr_t addr)
addr_t offset, tmp;
if (addr < offsetof(struct user, regs.acrs)) {
- /*
- * psw and gprs are stored on the stack
- */
- tmp = *(addr_t *)((addr_t) &task_pt_regs(child)->psw + addr);
+ struct pt_regs *regs = task_pt_regs(child);
+
+ /* psw and gprs are on the stack. */
+ tmp = *(addr_t *)((addr_t)&regs->psw + addr);
if (addr == offsetof(struct user, regs.psw.mask)) {
/* Return a clean psw mask. */
tmp &= PSW_MASK_USER | PSW_MASK_RI;
tmp |= PSW_USER_BITS;
}
+ /*
+ * During system call entry gpr2 contains the return
+ * value. Redirect reads of gpr2 to int_code so that
+ * readers see the system call number.
+ */
+ if (addr == offsetof(struct user, regs.gprs[2])) {
+ if (test_pt_regs_flag(regs, PIF_SYSCALL_ENTRY))
+ tmp = syscall_get_nr(child, regs);
+ }
} else if (addr < offsetof(struct user, regs.orig_gpr2)) {
/*
@@ -347,8 +356,17 @@ static int __poke_user(struct task_struct *child, addr_t addr, addr_t data)
}
if (addr == offsetof(struct user, regs.gprs[2])) {
- if (test_pt_regs_flag(regs, PIF_SYSCALL))
+ if (test_pt_regs_flag(regs, PIF_SYSCALL_ENTRY)) {
+ /*
+ * System call entry path: only change system
+ * call number in int_code, but leave gpr2
+ * alone, which contains the return value.
+ */
syscall_set_nr(child, regs, data);
+ return 0;
+ } else if (test_pt_regs_flag(regs, PIF_SYSCALL)) {
+ syscall_set_nr(child, regs, data);
+ }
}
*(addr_t *)((addr_t) &regs->psw + addr) = data;
} else if (addr < offsetof(struct user, regs.orig_gpr2)) {
diff --git a/arch/s390/kernel/syscall.c b/arch/s390/kernel/syscall.c
index bcc0b76bd868..a94bfcef2e8c 100644
--- a/arch/s390/kernel/syscall.c
+++ b/arch/s390/kernel/syscall.c
@@ -117,24 +117,22 @@ void noinstr __do_syscall(struct pt_regs *regs, unsigned long flags)
regs->int_code &= ~0xffffUL;
regs->int_code |= nr;
}
- regs->gprs[2] = nr;
if (nr == __NR_restart_syscall && !(current->restart_block.arch_data & 1)) {
regs->psw.addr = current->restart_block.arch_data;
current->restart_block.arch_data = 1;
}
-
- permit = syscall_enter_from_user_mode_work(regs, &nr);
-
/*
- * In the s390 ptrace ABI, both the syscall number and the return value
- * use gpr2. However, userspace puts the syscall number either in the
- * svc instruction itself, or uses gpr1. To make at least skipping syscalls
- * work, the ptrace code sets PIF_SYSCALL_RET_SET, which is checked here
- * and if set, the syscall will be skipped.
+ * gpr2 is the return value register. Set it to -ENOSYS so that a
+ * skipped system call gets a defined default return value.
+ * Set PIF_SYSCALL_ENTRY so that ptrace reads and writes to gpr2
+ * are redirected to the system call number in int_code.
*/
- if (unlikely(test_and_clear_pt_regs_flag(regs, PIF_SYSCALL_RET_SET) || !permit))
+ syscall_set_return_value(current, regs, -ENOSYS, 0);
+ set_pt_regs_flag(regs, PIF_SYSCALL_ENTRY);
+ permit = syscall_enter_from_user_mode_work(regs, &nr);
+ clear_pt_regs_flag(regs, PIF_SYSCALL_ENTRY);
+ if (unlikely(!permit))
goto out;
- regs->gprs[2] = -ENOSYS;
if (likely(nr < NR_syscalls)) {
nr = array_index_nospec(nr, NR_syscalls);
regs->gprs[2] = sys_call_table[nr](regs);
diff --git a/arch/s390/kernel/vdso.c b/arch/s390/kernel/vdso.c
index a905f989f910..efe492364a93 100644
--- a/arch/s390/kernel/vdso.c
+++ b/arch/s390/kernel/vdso.c
@@ -12,6 +12,7 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/futex.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/smp.h>
@@ -19,14 +20,31 @@
#include <linux/vdso_datastore.h>
#include <vdso/datapage.h>
#include <asm/alternative.h>
+#include <asm/vdso/vsyscall.h>
+#include <asm/vdso-symbols.h>
#include <asm/vdso.h>
extern char vdso_start[], vdso_end[];
+static inline void vdso_futex_robust_unlock_update_ips(void)
+{
+#ifdef CONFIG_FUTEX_ROBUST_UNLOCK
+ unsigned long vdso = current->mm->context.vdso_base;
+ struct futex_mm_data *fd = &current->mm->futex;
+
+ futex_reset_cs_ranges(fd);
+ futex_set_vdso_cs_range(fd, 0,
+ vdso + vdso_offset___futex_list64_try_unlock_cs_start,
+ vdso + vdso_offset___futex_list64_try_unlock_cs_end,
+ false);
+#endif
+}
+
static int vdso_mremap(const struct vm_special_mapping *sm,
struct vm_area_struct *vma)
{
current->mm->context.vdso_base = vma->vm_start;
+ vdso_futex_robust_unlock_update_ips();
return 0;
}
@@ -73,6 +91,7 @@ static int map_vdso(unsigned long addr, unsigned long vdso_mapping_len)
rc = PTR_ERR(vma);
} else {
current->mm->context.vdso_base = vdso_text_start;
+ vdso_futex_robust_unlock_update_ips();
rc = 0;
}
out:
diff --git a/arch/s390/kernel/vdso/Makefile b/arch/s390/kernel/vdso/Makefile
index 54bcf2984ca1..35e4e3dd23c7 100644
--- a/arch/s390/kernel/vdso/Makefile
+++ b/arch/s390/kernel/vdso/Makefile
@@ -3,7 +3,8 @@
# Include the generic Makefile to check the built vdso.
include $(srctree)/lib/vdso/Makefile.include
-obj-vdso = vdso_user_wrapper.o note.o vgetrandom-chacha.o
+obj-vdso-y = vdso_user_wrapper.o note.o vgetrandom-chacha.o
+obj-vdso-$(CONFIG_FUTEX_ROBUST_UNLOCK) += vfutex.o
obj-cvdso = vdso_generic.o getcpu.o vgetrandom.o
VDSO_CFLAGS_REMOVE := -pg $(CC_FLAGS_FTRACE) $(CC_FLAGS_EXPOLINE)
CFLAGS_REMOVE_getcpu.o = $(VDSO_CFLAGS_REMOVE)
@@ -16,8 +17,8 @@ endif
# Build rules
-targets := $(obj-vdso) $(obj-cvdso) vdso.so vdso.so.dbg
-obj-vdso := $(addprefix $(obj)/, $(obj-vdso))
+targets := $(obj-vdso-y) $(obj-cvdso) vdso.so vdso.so.dbg
+obj-vdso := $(addprefix $(obj)/, $(obj-vdso-y))
obj-cvdso := $(addprefix $(obj)/, $(obj-cvdso))
KBUILD_AFLAGS_VDSO := $(KBUILD_AFLAGS) -DBUILD_VDSO
diff --git a/arch/s390/kernel/vdso/gen_vdso_offsets.sh b/arch/s390/kernel/vdso/gen_vdso_offsets.sh
index 359982fb002d..4039e6cfca51 100755
--- a/arch/s390/kernel/vdso/gen_vdso_offsets.sh
+++ b/arch/s390/kernel/vdso/gen_vdso_offsets.sh
@@ -12,4 +12,5 @@
#
LC_ALL=C
-sed -n 's/\([0-9a-f]*\) . __kernel_\(.*\)/\#define vdso_offset_\2\t0x\1/p'
+sed -n -e 's/\([0-9a-f]*\) . __kernel_\(.*\)/\#define vdso_offset_\2\t0x\1/p' \
+ -e 's/\([0-9a-f]*\) . \(__futex_.*\)/\#define vdso_offset_\2\t0x\1/p'
diff --git a/arch/s390/kernel/vdso/vdso.lds.S b/arch/s390/kernel/vdso/vdso.lds.S
index 6bcdb64315f2..1172f05f609d 100644
--- a/arch/s390/kernel/vdso/vdso.lds.S
+++ b/arch/s390/kernel/vdso/vdso.lds.S
@@ -110,6 +110,9 @@ VERSION
__kernel_rt_sigreturn;
__kernel_sigreturn;
__kernel_getrandom;
+#ifdef CONFIG_FUTEX_ROBUST_UNLOCK
+ __vdso_futex_robust_list64_try_unlock;
+#endif
local: *;
};
}
diff --git a/arch/s390/kernel/vdso/vfutex.S b/arch/s390/kernel/vdso/vfutex.S
new file mode 100644
index 000000000000..8a25bd4ecea4
--- /dev/null
+++ b/arch/s390/kernel/vdso/vfutex.S
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#include <linux/linkage.h>
+#include <asm/dwarf.h>
+
+/*
+ * __u32 __vdso_futex_robust_list64_try_unlock(__u32 *lock, __u32 tid, __u64 *pop)
+ *
+ * Returns tid on success, or lock value on failure.
+ */
+SYM_FUNC_START(__vdso_futex_robust_list64_try_unlock)
+ CFI_STARTPROC
+ lghi %r1,0
+ cs %r3,%r1,0(%r2)
+__futex_list64_try_unlock_cs_start:
+ jnz __futex_list64_try_unlock_cs_end
+__futex_list64_try_unlock_cs_success:
+ mvghi 0(%r4),0
+__futex_list64_try_unlock_cs_end:
+ llgfr %r2,%r3
+ br %r14
+ CFI_ENDPROC
+SYM_FUNC_END(__vdso_futex_robust_list64_try_unlock)