summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Brauner <brauner@kernel.org>2026-10-13 21:03:13 +0200
committerChristian Brauner <brauner@kernel.org>2026-10-13 21:03:13 +0200
commit591dfa398de12e7e685bb3ec465ff3bf5cddff51 (patch)
tree159298c5e9c69109cabe0d28ad870adec31ba2aa
parent310de65e73ead75afb78379b644a025bfe0b50dd (diff)
parent290ebc69ec41e2fe26f6cee74a9efe953b08853f (diff)
downloadlinux-next-591dfa398de12e7e685bb3ec465ff3bf5cddff51.tar.gz
linux-next-591dfa398de12e7e685bb3ec465ff3bf5cddff51.zip
Merge branch 'vfs-7.4.mount' into vfs.all
Signed-off-by: Christian Brauner <brauner@kernel.org>
-rw-r--r--Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml1
-rw-r--r--Makefile2
-rw-r--r--arch/riscv/Kconfig5
-rw-r--r--arch/riscv/Kconfig.errata17
-rw-r--r--arch/riscv/Kconfig.socs1
-rw-r--r--arch/riscv/errata/sifive/errata.c20
-rw-r--r--arch/riscv/include/asm/cpufeature.h2
-rw-r--r--arch/riscv/include/asm/errata_list_vendors.h3
-rw-r--r--arch/riscv/include/asm/futex.h2
-rw-r--r--arch/riscv/include/asm/insn-def.h16
-rw-r--r--arch/riscv/include/asm/scs.h17
-rw-r--r--arch/riscv/include/asm/vector.h8
-rw-r--r--arch/riscv/include/uapi/asm/vendor/mips.h4
-rw-r--r--arch/riscv/kernel/cpufeature.c12
-rw-r--r--arch/riscv/kernel/entry.S2
-rw-r--r--arch/riscv/kernel/patch.c2
-rw-r--r--arch/riscv/kernel/ptrace.c8
-rw-r--r--arch/riscv/kernel/setup.c2
-rw-r--r--arch/riscv/kernel/smpboot.c2
-rw-r--r--arch/riscv/kernel/suspend.c2
-rw-r--r--arch/riscv/lib/strnlen.S20
-rw-r--r--arch/riscv/mm/extable.c4
-rw-r--r--arch/x86/include/asm/cpufeatures.h3
-rw-r--r--arch/x86/include/asm/tlb.h6
-rw-r--r--arch/x86/kernel/cpu/amd.c4
-rw-r--r--drivers/block/zloop.c31
-rw-r--r--drivers/input/joystick/xpad.c4
-rw-r--r--drivers/input/misc/ati_remote2.c12
-rw-r--r--drivers/nvme/host/core.c12
-rw-r--r--drivers/nvme/target/core.c2
-rw-r--r--drivers/nvme/target/pr.c4
-rw-r--r--drivers/regulator/ltc3676.c15
-rw-r--r--drivers/regulator/of_regulator.c29
-rw-r--r--drivers/regulator/tps6594-regulator.c6
-rw-r--r--drivers/scsi/ch.c29
-rw-r--r--drivers/scsi/qedi/qedi_main.c3
-rw-r--r--drivers/spi/spi-cadence-quadspi.c3
-rw-r--r--drivers/spi/spi-nxp-fspi.c22
-rw-r--r--drivers/spi/spi-sg2044-nor.c56
-rw-r--r--drivers/ufs/core/ufs-mcq.c30
-rw-r--r--drivers/ufs/core/ufshcd.c53
-rw-r--r--drivers/ufs/host/ufs-mediatek.c18
-rw-r--r--fs/namespace.c16
-rw-r--r--fs/xfs/libxfs/xfs_exchmaps.c93
-rw-r--r--fs/xfs/libxfs/xfs_log_format.h4
-rw-r--r--fs/xfs/xfs_bmap_util.c15
-rw-r--r--fs/xfs/xfs_exchrange.c51
-rw-r--r--include/linux/io_uring_types.h2
-rw-r--r--include/linux/nvme.h4
-rw-r--r--include/uapi/linux/mount.h1
-rw-r--r--io_uring/cancel.c7
-rw-r--r--io_uring/io_uring.c13
-rw-r--r--io_uring/kbuf.c6
-rw-r--r--io_uring/memmap.c10
-rw-r--r--io_uring/memmap.h2
-rw-r--r--io_uring/net.c4
-rw-r--r--io_uring/register.c21
-rw-r--r--io_uring/zcrx.c2
-rw-r--r--security/landlock/domain.h9
-rw-r--r--tools/testing/selftests/filesystems/open_tree_ns/open_tree_ns_test.c198
60 files changed, 680 insertions, 272 deletions
diff --git a/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml b/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml
index e8d6d5d858b6..41ec69f5dceb 100644
--- a/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml
+++ b/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml
@@ -77,6 +77,7 @@ properties:
- mscc,ocelot-spi
- mscc,jaguar2-spi
- sophgo,sg2042-spi
+ - syna,sl2610-spi
- thead,th1520-spi
- ultrarisc,dp1000-spi
- const: snps,dw-apb-ssi
diff --git a/Makefile b/Makefile
index 7c855e6fe544..0dc1a9f4e63e 100644
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@
VERSION = 7
PATCHLEVEL = 3
SUBLEVEL = 0
-EXTRAVERSION = -rc6
+EXTRAVERSION = -rc7
NAME = Baby Opossum Posse
# *DOCUMENTATION*
diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
index d6c2dbf8455c..5874ca30f4f6 100644
--- a/arch/riscv/Kconfig
+++ b/arch/riscv/Kconfig
@@ -453,9 +453,10 @@ config SMP
config NR_CPUS
int "Maximum number of CPUs (2-512)"
depends on SMP
- range 2 512 if !RISCV_SBI_V01
- range 2 32 if RISCV_SBI_V01 && 32BIT
+ range 2 512 if !RISCV_SBI_V01 && 64BIT
+ range 2 128 if !RISCV_SBI_V01 && 32BIT
range 2 64 if RISCV_SBI_V01 && 64BIT
+ range 2 32 if RISCV_SBI_V01 && 32BIT
default "32" if 32BIT
default "64" if RISCV_SBI_V01 && 64BIT
default "256" if !RISCV_SBI_V01 && 64BIT
diff --git a/arch/riscv/Kconfig.errata b/arch/riscv/Kconfig.errata
index 3c945d086c7d..622ade116a2d 100644
--- a/arch/riscv/Kconfig.errata
+++ b/arch/riscv/Kconfig.errata
@@ -46,7 +46,7 @@ config ERRATA_MIPS_P8700_PAUSE_OPCODE
config ERRATA_SIFIVE
bool "SiFive errata"
- depends on RISCV_ALTERNATIVE
+ depends on RISCV_ALTERNATIVE && 64BIT
help
All SiFive errata Kconfig depend on this Kconfig. Disabling
this Kconfig will disable all SiFive errata. Please say "Y"
@@ -96,6 +96,20 @@ config ERRATA_STARFIVE_JH7100
Say "Y" if you want to support the BeagleV Starlight and/or
StarFive VisionFive V1 boards.
+config ERRATA_SIFIVE_MAL_9092
+ bool "Apply SiFive HLV/HLVX/HSV fence errata"
+ depends on ERRATA_SIFIVE && 64BIT
+ default y
+ help
+ This will apply the SiFive MAL-9092 errata to add fence.rw
+ instructions around all HLV/HLVX/HSV instructions.
+
+ Note: HSTATUS.HU is cleared during KVM initialization. If future
+ changes enable HSTATUS.HU to allow hypervisor instructions in
+ U-mode, additional errata handling may be required.
+
+ If you don't know what to do here, say "Y".
+
config ERRATA_THEAD
bool "T-HEAD errata"
depends on RISCV_ALTERNATIVE
@@ -146,6 +160,7 @@ config ERRATA_THEAD_PMU
config ERRATA_THEAD_GHOSTWRITE
bool "Apply T-Head Ghostwrite errata"
depends on ERRATA_THEAD && RISCV_ISA_XTHEADVECTOR
+ select RISCV_ALTERNATIVE_EARLY
default y
help
The T-Head C9xx cores have a vulnerability in the xtheadvector
diff --git a/arch/riscv/Kconfig.socs b/arch/riscv/Kconfig.socs
index 429e07589306..cecbce0c4983 100644
--- a/arch/riscv/Kconfig.socs
+++ b/arch/riscv/Kconfig.socs
@@ -34,6 +34,7 @@ config ARCH_RENESAS
config ARCH_SIFIVE
bool "SiFive SoCs"
select ERRATA_SIFIVE
+ depends on 64BIT
help
This enables support for SiFive SoC platform hardware.
diff --git a/arch/riscv/errata/sifive/errata.c b/arch/riscv/errata/sifive/errata.c
index df80c9614df1..1a5d223ebf35 100644
--- a/arch/riscv/errata/sifive/errata.c
+++ b/arch/riscv/errata/sifive/errata.c
@@ -51,6 +51,22 @@ static bool errata_cip_1200_check_func(unsigned long arch_id, unsigned long imp
return true;
}
+static bool errata_mal_9092_check_func(unsigned long arch_id, unsigned long impid)
+{
+ /*
+ * Affected cores:
+ * Architecture ID: 0x8000000000000109
+ * Implementation ID: 0x19251031, 0x19251217, 0x19260320
+ */
+
+ if (arch_id != 0x8000000000000109)
+ return false;
+ if (impid != 0x19251031 && impid != 0x19251217 && impid != 0x19260320)
+ return false;
+
+ return true;
+}
+
static struct errata_info_t errata_list[ERRATA_SIFIVE_NUMBER] = {
{
.name = "cip-453",
@@ -60,6 +76,10 @@ static struct errata_info_t errata_list[ERRATA_SIFIVE_NUMBER] = {
.name = "cip-1200",
.check_func = errata_cip_1200_check_func
},
+ {
+ .name = "mal-9092",
+ .check_func = errata_mal_9092_check_func
+ },
};
static u32 __init_or_module sifive_errata_probe(unsigned long archid,
diff --git a/arch/riscv/include/asm/cpufeature.h b/arch/riscv/include/asm/cpufeature.h
index 739fcc84bf7b..5efa72823475 100644
--- a/arch/riscv/include/asm/cpufeature.h
+++ b/arch/riscv/include/asm/cpufeature.h
@@ -40,6 +40,8 @@ extern u32 thead_vlenb_of;
void __init riscv_user_isa_enable(void);
+void riscv_clear_hypervisor_csr(void);
+
#define _RISCV_ISA_EXT_DATA(_name, _id, _subset_exts, _subset_exts_size, _validate) { \
.name = #_name, \
.property = #_name, \
diff --git a/arch/riscv/include/asm/errata_list_vendors.h b/arch/riscv/include/asm/errata_list_vendors.h
index ec7eba373437..c62a82f60468 100644
--- a/arch/riscv/include/asm/errata_list_vendors.h
+++ b/arch/riscv/include/asm/errata_list_vendors.h
@@ -11,7 +11,8 @@
#ifdef CONFIG_ERRATA_SIFIVE
#define ERRATA_SIFIVE_CIP_453 0
#define ERRATA_SIFIVE_CIP_1200 1
-#define ERRATA_SIFIVE_NUMBER 2
+#define ERRATA_SIFIVE_MAL_9092 2
+#define ERRATA_SIFIVE_NUMBER 3
#endif
#ifdef CONFIG_ERRATA_THEAD
diff --git a/arch/riscv/include/asm/futex.h b/arch/riscv/include/asm/futex.h
index 90c86b115e00..6f3bc9bdb85e 100644
--- a/arch/riscv/include/asm/futex.h
+++ b/arch/riscv/include/asm/futex.h
@@ -40,6 +40,7 @@ arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr)
if (!access_ok(uaddr, sizeof(u32)))
return -EFAULT;
+ uaddr = untagged_addr(uaddr);
switch (op) {
case FUTEX_OP_SET:
@@ -82,6 +83,7 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
if (!access_ok(uaddr, sizeof(u32)))
return -EFAULT;
+ uaddr = untagged_addr(uaddr);
__enable_user_access();
__asm__ __volatile__ (
diff --git a/arch/riscv/include/asm/insn-def.h b/arch/riscv/include/asm/insn-def.h
index 7c6daf116756..8fcc848c196e 100644
--- a/arch/riscv/include/asm/insn-def.h
+++ b/arch/riscv/include/asm/insn-def.h
@@ -192,18 +192,28 @@
INSN_R(OPCODE_SYSTEM, FUNC3(0), FUNC7(49), \
__RD(0), RS1(gaddr), RS2(vmid))
+#define ALT_SIFIVE_MAL_9092_FENCE \
+ALTERNATIVE("nop", "fence rw, rw", SIFIVE_VENDOR_ID, \
+ ERRATA_SIFIVE_MAL_9092, CONFIG_ERRATA_SIFIVE_MAL_9092)
+
#define HLVX_HU(dest, addr) \
+ ALT_SIFIVE_MAL_9092_FENCE "\n" \
INSN_R(OPCODE_SYSTEM, FUNC3(4), FUNC7(50), \
- RD(dest), RS1(addr), __RS2(3))
+ RD(dest), RS1(addr), __RS2(3)) "\n" \
+ ALT_SIFIVE_MAL_9092_FENCE
#define HLV_W(dest, addr) \
+ ALT_SIFIVE_MAL_9092_FENCE "\n" \
INSN_R(OPCODE_SYSTEM, FUNC3(4), FUNC7(52), \
- RD(dest), RS1(addr), __RS2(0))
+ RD(dest), RS1(addr), __RS2(0)) "\n" \
+ ALT_SIFIVE_MAL_9092_FENCE
#ifdef CONFIG_64BIT
#define HLV_D(dest, addr) \
+ ALT_SIFIVE_MAL_9092_FENCE "\n" \
INSN_R(OPCODE_SYSTEM, FUNC3(4), FUNC7(54), \
- RD(dest), RS1(addr), __RS2(0))
+ RD(dest), RS1(addr), __RS2(0)) "\n" \
+ ALT_SIFIVE_MAL_9092_FENCE
#else
#define HLV_D(dest, addr) \
__ASM_STR(.error "hlv.d requires 64-bit support")
diff --git a/arch/riscv/include/asm/scs.h b/arch/riscv/include/asm/scs.h
index 023a412fe38d..49e40eef4ed7 100644
--- a/arch/riscv/include/asm/scs.h
+++ b/arch/riscv/include/asm/scs.h
@@ -4,6 +4,7 @@
#ifdef __ASSEMBLER__
#include <asm/asm-offsets.h>
+#include <asm/csr.h>
#ifdef CONFIG_SHADOW_CALL_STACK
@@ -22,9 +23,17 @@
REG_L gp, TASK_TI_SCS_SP(tp)
.endm
-/* Load task_scs_sp(current) to gp, but only if tp has changed. */
-.macro scs_load_current_if_task_changed prev
- beq \prev, tp, _skip_scs
+/*
+ * Load task_scs_sp(current) to gp, but only when the trap came from U-mode.
+ * The source privilege level is taken from the saved sstatus.SPP bit (written
+ * by hardware on trap entry), not by comparing tp. tp is a user-writable
+ * register, so gating the shadow call stack reload on it let a user task that
+ * set tp == &current skip the reload and run the kernel with an attacker
+ * controlled gp (the shadow call stack pointer).
+ */
+.macro scs_load_current_if_from_user status, tmp
+ andi \tmp, \status, SR_SPP
+ bnez \tmp, _skip_scs
scs_load_current
_skip_scs:
.endm
@@ -42,7 +51,7 @@ _skip_scs:
.endm
.macro scs_load_current
.endm
-.macro scs_load_current_if_task_changed prev
+.macro scs_load_current_if_from_user status, tmp
.endm
.macro scs_save_current
.endm
diff --git a/arch/riscv/include/asm/vector.h b/arch/riscv/include/asm/vector.h
index fffe72a77208..1f8dcae3af51 100644
--- a/arch/riscv/include/asm/vector.h
+++ b/arch/riscv/include/asm/vector.h
@@ -137,8 +137,8 @@ static __always_inline void __vstate_csr_save(struct __riscv_v_ext_state *dest)
"csrr %0, " __stringify(CSR_VSTART) "\n\t"
"csrr %1, " __stringify(CSR_VTYPE) "\n\t"
"csrr %2, " __stringify(CSR_VL) "\n\t"
- : "=r" (dest->vstart), "=r" (dest->vtype), "=r" (dest->vl),
- "=r" (dest->vcsr) : :);
+ : "=r" (dest->vstart), "=r" (dest->vtype), "=r" (dest->vl)
+ : :);
if (has_xtheadvector()) {
unsigned long status;
@@ -230,7 +230,7 @@ static inline void __riscv_v_vstate_save(struct __riscv_v_ext_state *save_to,
"add %1, %1, %0\n\t"
"vse8.v v24, (%1)\n\t"
".option pop\n\t"
- : "=&r" (vl) : "r" (datap) : "memory");
+ : "=&r" (vl), "+r" (datap) : : "memory");
}
riscv_v_disable();
}
@@ -266,7 +266,7 @@ static inline void __riscv_v_vstate_restore(struct __riscv_v_ext_state *restore_
"add %1, %1, %0\n\t"
"vle8.v v24, (%1)\n\t"
".option pop\n\t"
- : "=&r" (vl) : "r" (datap) : "memory");
+ : "=&r" (vl), "+r" (datap) : : "memory");
}
__vstate_csr_restore(restore_from);
riscv_v_disable();
diff --git a/arch/riscv/include/uapi/asm/vendor/mips.h b/arch/riscv/include/uapi/asm/vendor/mips.h
index e65ab268b265..a31c23afe74f 100644
--- a/arch/riscv/include/uapi/asm/vendor/mips.h
+++ b/arch/riscv/include/uapi/asm/vendor/mips.h
@@ -1,3 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-#define RISCV_HWPROBE_VENDOR_EXT_XMIPSEXECTL BIT(0)
+#include <linux/const.h>
+
+#define RISCV_HWPROBE_VENDOR_EXT_XMIPSEXECTL _BITULL(0)
diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c
index 61d21f714830..7329792978cc 100644
--- a/arch/riscv/kernel/cpufeature.c
+++ b/arch/riscv/kernel/cpufeature.c
@@ -1233,6 +1233,18 @@ void __init riscv_user_isa_enable(void)
pr_warn("Zicbop disabled as it is unavailable on some harts\n");
}
+void riscv_clear_hypervisor_csr(void)
+{
+ if (!riscv_has_extension_unlikely(RISCV_ISA_EXT_H))
+ return;
+
+ /*
+ * Clear HSTATUS.HU to restrict hypervisor instructions to HS-mode.
+ * This prevents user-mode from executing HLV/HSV instructions.
+ */
+ csr_clear(CSR_HSTATUS, HSTATUS_HU);
+}
+
#ifdef CONFIG_RISCV_ALTERNATIVE
/*
* Alternative patch sites consider 48 bits when determining when to patch
diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S
index d799c4e56f80..61b7a7716697 100644
--- a/arch/riscv/kernel/entry.S
+++ b/arch/riscv/kernel/entry.S
@@ -208,7 +208,7 @@ SYM_CODE_START(handle_exception)
load_global_pointer
/* Load the kernel shadow call stack pointer if coming from userspace */
- scs_load_current_if_task_changed s5
+ scs_load_current_if_from_user s1, s5
#ifdef CONFIG_RISCV_ISA_V_PREEMPTIVE
move a0, sp
diff --git a/arch/riscv/kernel/patch.c b/arch/riscv/kernel/patch.c
index 2239c28981bc..2b5adf01c550 100644
--- a/arch/riscv/kernel/patch.c
+++ b/arch/riscv/kernel/patch.c
@@ -48,7 +48,7 @@ static __always_inline void *patch_map(void *addr, const unsigned int fixmap)
if (!IS_ENABLED(CONFIG_STRICT_KERNEL_RWX))
return addr;
phys = __pa_symbol(addr);
- } else if (IS_ENABLED(CONFIG_STRICT_MODULE_RWX)) {
+ } else if (IS_ENABLED(CONFIG_EXECMEM)) {
struct page *page = vmalloc_to_page(addr);
BUG_ON(!page);
diff --git a/arch/riscv/kernel/ptrace.c b/arch/riscv/kernel/ptrace.c
index f336a183667e..a0055743d3ba 100644
--- a/arch/riscv/kernel/ptrace.c
+++ b/arch/riscv/kernel/ptrace.c
@@ -230,6 +230,7 @@ static int riscv_vr_set(struct task_struct *target,
return -ENODATA;
/* Copy rest of the vstate except datap */
+ memset(&ptrace_vstate, 0, sizeof(ptrace_vstate));
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &ptrace_vstate, 0,
sizeof(struct __riscv_v_regset_state));
if (unlikely(ret))
@@ -299,6 +300,9 @@ static int riscv_cfi_get(struct task_struct *target,
struct user_cfi_state user_cfi;
struct pt_regs *regs;
+ if (!is_user_lpad_enabled() && !is_user_shstk_enabled())
+ return -EINVAL;
+
memset(&user_cfi, 0, sizeof(user_cfi));
regs = task_pt_regs(target);
@@ -337,8 +341,12 @@ static int riscv_cfi_set(struct task_struct *target,
struct user_cfi_state user_cfi;
struct pt_regs *regs;
+ if (!is_user_lpad_enabled() && !is_user_shstk_enabled())
+ return -EINVAL;
+
regs = task_pt_regs(target);
+ memset(&user_cfi, 0, sizeof(user_cfi));
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &user_cfi, 0, -1);
if (ret)
return ret;
diff --git a/arch/riscv/kernel/setup.c b/arch/riscv/kernel/setup.c
index a32344bb220d..d79f99b86033 100644
--- a/arch/riscv/kernel/setup.c
+++ b/arch/riscv/kernel/setup.c
@@ -366,6 +366,8 @@ void __init setup_arch(char **cmdline_p)
if (!IS_ENABLED(CONFIG_RISCV_ISA_ZBB) || !riscv_isa_extension_available(NULL, ZBB))
static_branch_disable(&efficient_ffs_key);
+
+ riscv_clear_hypervisor_csr();
}
bool arch_cpu_is_hotpluggable(int cpu)
diff --git a/arch/riscv/kernel/smpboot.c b/arch/riscv/kernel/smpboot.c
index f6ef57930b50..a9de2dae804c 100644
--- a/arch/riscv/kernel/smpboot.c
+++ b/arch/riscv/kernel/smpboot.c
@@ -244,6 +244,8 @@ asmlinkage __visible void smp_callin(void)
numa_add_cpu(curr_cpuid);
+ riscv_clear_hypervisor_csr();
+
pr_debug("CPU%u: Booted secondary hartid %lu\n", curr_cpuid,
cpuid_to_hartid_map(curr_cpuid));
diff --git a/arch/riscv/kernel/suspend.c b/arch/riscv/kernel/suspend.c
index 3efbf7874f3b..db220966f782 100644
--- a/arch/riscv/kernel/suspend.c
+++ b/arch/riscv/kernel/suspend.c
@@ -43,6 +43,8 @@ void suspend_save_csrs(struct suspend_context *context)
void suspend_restore_csrs(struct suspend_context *context)
{
+ riscv_clear_hypervisor_csr();
+
csr_write(CSR_SCRATCH, 0);
if (riscv_has_extension_unlikely(RISCV_ISA_EXT_XLINUXENVCFG))
csr_write(CSR_ENVCFG, context->envcfg);
diff --git a/arch/riscv/lib/strnlen.S b/arch/riscv/lib/strnlen.S
index a8911605c248..5b3bbf0a3098 100644
--- a/arch/riscv/lib/strnlen.S
+++ b/arch/riscv/lib/strnlen.S
@@ -87,9 +87,25 @@ strnlen_zbb:
* Aligned boundary. Use the address of the last valid byte
* (s + count - 1) to avoid loading a word past the count
* boundary in the loop below. count == 0 is handled above.
+ *
+ * Saturate the boundary when s + count would wrap around (very
+ * large counts, e.g. SIZE_MAX passed by FORTIFY strcat/strlcat
+ * with a destination whose size is unknown at compile time).
+ * Without this, the wrapped boundary lands before s and the
+ * pre-loop guard below always exits, returning a truncated
+ * length.
+ *
+ * Clamping the increment to ~s (== SIZE_MAX - s) keeps the
+ * computation branchless and wrap-free:
+ *
+ * s + min(count - 1, ~s) == saturating_add(s, count - 1)
+ *
+ * Saturating makes the scan equivalent to strlen().
*/
- add t4, a0, a1
- addi t4, t4, -1
+ addi t4, a1, -1 /* count - 1 */
+ not t1, a0 /* SIZE_MAX - s */
+ minu t4, t4, t1 /* clamp, so s + t4 can never wrap */
+ add t4, a0, t4 /* saturated s + count - 1 */
andi t4, t4, -SZREG
/* Get the first word. */
diff --git a/arch/riscv/mm/extable.c b/arch/riscv/mm/extable.c
index dd1530af3ef1..e252eba55ede 100644
--- a/arch/riscv/mm/extable.c
+++ b/arch/riscv/mm/extable.c
@@ -68,8 +68,8 @@ ex_handler_load_unaligned_zeropad(const struct exception_table_entry *ex,
addr = regs_get_gpr(regs, reg_addr * sizeof(unsigned long));
- offset = addr & 0x7UL;
- addr &= ~0x7UL;
+ offset = addr & (sizeof(data) - 1);
+ addr &= ~(sizeof(data) - 1);
data = *(unsigned long *)addr >> (offset * 8);
diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h
index f70ee74b5f92..fe84cf4e97d3 100644
--- a/arch/x86/include/asm/cpufeatures.h
+++ b/arch/x86/include/asm/cpufeatures.h
@@ -477,6 +477,8 @@
#define X86_FEATURE_NO_NESTED_DATA_BP (20*32+ 0) /* No Nested Data Breakpoints */
#define X86_FEATURE_WRMSR_XX_BASE_NS (20*32+ 1) /* WRMSR to {FS,GS,KERNEL_GS}_BASE is non-serializing */
#define X86_FEATURE_LFENCE_RDTSC (20*32+ 2) /* LFENCE always serializing / synchronizes RDTSC */
+
+#define X86_FEATURE_TLBSYNC_SFW_NO (20*32+ 4) /* TLBI Erratum #1718 fixed in microcode */
#define X86_FEATURE_VERW_CLEAR (20*32+ 5) /* The memory form of VERW mitigates TSA */
#define X86_FEATURE_NULL_SEL_CLR_BASE (20*32+ 6) /* Null Selector Clears Base */
@@ -587,4 +589,5 @@
#define X86_BUG_TSA X86_BUG( 1*32+ 9) /* "tsa" CPU is affected by Transient Scheduler Attacks */
#define X86_BUG_VMSCAPE X86_BUG( 1*32+10) /* "vmscape" CPU is affected by VMSCAPE attacks from guests */
#define X86_BUG_SEAMRET_INVD_VMCS X86_BUG( 1*32+11) /* "seamret_invd_vmcs" SEAMRET from P-SEAMLDR clears the current VMCS */
+#define X86_BUG_TLBI X86_BUG( 1*32+12) /* "tlbi" AMD TLBI Erratum #1718 */
#endif /* _ASM_X86_CPUFEATURES_H */
diff --git a/arch/x86/include/asm/tlb.h b/arch/x86/include/asm/tlb.h
index 866ea78ba156..eba925c85f52 100644
--- a/arch/x86/include/asm/tlb.h
+++ b/arch/x86/include/asm/tlb.h
@@ -110,6 +110,12 @@ static inline void __tlbsync(void)
/* TLBSYNC: supported in binutils >= 0.36. */
asm volatile(".byte 0x0f, 0x01, 0xff" ::: "memory");
+
+ if (!static_cpu_has_bug(X86_BUG_TLBI))
+ return;
+
+ /* TLBSYNC: supported in binutils >= 0.36. */
+ asm volatile(".byte 0x0f, 0x01, 0xff" ::: "memory");
}
#else
/* Some compilers (I'm looking at you clang!) simply can't do DCE */
diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c
index 54e14ed276b5..40463bad1078 100644
--- a/arch/x86/kernel/cpu/amd.c
+++ b/arch/x86/kernel/cpu/amd.c
@@ -1049,6 +1049,10 @@ static void init_amd_zen5(struct cpuinfo_x86 *c)
msr_clear_bit(MSR_AMD64_CPUID_FN_7, 18);
pr_emerg_once("RDSEED32 is broken. Disabling the corresponding CPUID bit.\n");
}
+
+ if (cpu_has(c, X86_FEATURE_INVLPGB) &&
+ !cpu_has(c, X86_FEATURE_TLBSYNC_SFW_NO))
+ set_cpu_bug(c, X86_BUG_TLBI);
}
static void init_amd(struct cpuinfo_x86 *c)
diff --git a/drivers/block/zloop.c b/drivers/block/zloop.c
index f0ca221524db..394dc2408ed7 100644
--- a/drivers/block/zloop.c
+++ b/drivers/block/zloop.c
@@ -800,6 +800,27 @@ static void zloop_cmd_workfn(struct work_struct *work)
current->flags = orig_flags;
}
+static void zloop_fill_zero_rq(struct request *rq, unsigned int zero_from)
+{
+ struct req_iterator iter;
+ struct bio_vec bv;
+ unsigned int pos = 0;
+ unsigned int end, off;
+
+ rq_for_each_segment(bv, rq, iter) {
+ end = pos + bv.bv_len;
+ if (end > zero_from) {
+ if (pos < zero_from)
+ off = zero_from - pos;
+ else
+ off = 0;
+ memzero_page(bv.bv_page, bv.bv_offset + off,
+ bv.bv_len - off);
+ }
+ pos = end;
+ }
+}
+
static void zloop_complete_rq(struct request *rq)
{
struct zloop_cmd *cmd = blk_mq_rq_to_pdu(rq);
@@ -814,13 +835,9 @@ static void zloop_complete_rq(struct request *rq)
pr_err("Zone %u: failed read sector %llu, %llu sectors\n",
zone_no, cmd->sector, cmd->nr_sectors);
- if (cmd->ret >= 0 && cmd->ret != blk_rq_bytes(rq)) {
- /* short read */
- struct bio *bio;
-
- __rq_for_each_bio(bio, rq)
- zero_fill_bio(bio);
- }
+ /* Short read. Zero out the unread tail beyond cmd->ret. */
+ if (cmd->ret >= 0 && cmd->ret != blk_rq_bytes(rq))
+ zloop_fill_zero_rq(rq, cmd->ret);
break;
case REQ_OP_WRITE:
case REQ_OP_ZONE_APPEND:
diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
index 6406ceab988a..810e52782ae8 100644
--- a/drivers/input/joystick/xpad.c
+++ b/drivers/input/joystick/xpad.c
@@ -302,6 +302,8 @@ static const struct xpad_device {
{ 0x17ef, 0x6182, "Lenovo Legion Controller for Windows", 0, XTYPE_XBOX360 },
{ 0x1949, 0x041a, "Amazon Game Controller", 0, XTYPE_XBOX360 },
{ 0x1a86, 0xe310, "Legion Go S", 0, XTYPE_XBOX360 },
+ { 0x1b1c, 0x2b1f, "Corsair Novablade Pro (Wired)", 0, XTYPE_XBOX360 },
+ { 0x1b1c, 0x2b2b, "Corsair Novablade Pro (2.4 GHz Receiver)", 0, XTYPE_XBOX360 },
{ 0x1bad, 0x0002, "Harmonix Rock Band Guitar", 0, XTYPE_XBOX360 },
{ 0x1bad, 0x0003, "Harmonix Rock Band Drumkit", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 },
{ 0x1bad, 0x0130, "Ion Drum Rocker", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 },
@@ -379,6 +381,7 @@ static const struct xpad_device {
{ 0x2993, 0x2001, "TECNO Pocket Go", 0, XTYPE_XBOX360 },
{ 0x2dc8, 0x2000, "8BitDo Pro 2 Wired Controller fox Xbox", 0, XTYPE_XBOXONE },
{ 0x2dc8, 0x200f, "8BitDo Ultimate 3-mode Controller for Xbox", MAP_SHARE_BUTTON, XTYPE_XBOXONE },
+ { 0x2dc8, 0x201e, "8BitDo Pro 2 Wired Controller for Xbox", MAP_SHARE_BUTTON, XTYPE_XBOXONE },
{ 0x2dc8, 0x3106, "8BitDo Ultimate Wireless / Pro 2 Wired Controller", 0, XTYPE_XBOX360 },
{ 0x2dc8, 0x3109, "8BitDo Ultimate Wireless Bluetooth", 0, XTYPE_XBOX360 },
{ 0x2dc8, 0x310a, "8BitDo Ultimate 2C Wireless Controller", 0, XTYPE_XBOX360 },
@@ -545,6 +548,7 @@ static const struct usb_device_id xpad_table[] = {
XPAD_XBOX360_VENDOR(0x17ef), /* Lenovo */
XPAD_XBOX360_VENDOR(0x1949), /* Amazon controllers */
XPAD_XBOX360_VENDOR(0x1a86), /* Nanjing Qinheng Microelectronics (WCH) */
+ XPAD_XBOX360_VENDOR(0x1b1c), /* Corsair controllers */
XPAD_XBOX360_VENDOR(0x1bad), /* Harmonix Rock Band guitar and drums */
XPAD_XBOX360_VENDOR(0x1ee9), /* ZOTAC Technology Limited */
XPAD_XBOX360_VENDOR(0x20bc), /* BETOP wireless dongles */
diff --git a/drivers/input/misc/ati_remote2.c b/drivers/input/misc/ati_remote2.c
index 8db2dca84975..3e0f874e9b16 100644
--- a/drivers/input/misc/ati_remote2.c
+++ b/drivers/input/misc/ati_remote2.c
@@ -672,8 +672,7 @@ static ssize_t ati_remote2_show_channel_mask(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- struct usb_device *udev = to_usb_device(dev);
- struct usb_interface *intf = usb_ifnum_to_if(udev, 0);
+ struct usb_interface *intf = to_usb_interface(dev);
struct ati_remote2 *ar2 = usb_get_intfdata(intf);
return sprintf(buf, "0x%04x\n", ar2->channel_mask);
@@ -683,8 +682,7 @@ static ssize_t ati_remote2_store_channel_mask(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- struct usb_device *udev = to_usb_device(dev);
- struct usb_interface *intf = usb_ifnum_to_if(udev, 0);
+ struct usb_interface *intf = to_usb_interface(dev);
struct ati_remote2 *ar2 = usb_get_intfdata(intf);
unsigned int mask;
int r;
@@ -720,8 +718,7 @@ static ssize_t ati_remote2_show_mode_mask(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- struct usb_device *udev = to_usb_device(dev);
- struct usb_interface *intf = usb_ifnum_to_if(udev, 0);
+ struct usb_interface *intf = to_usb_interface(dev);
struct ati_remote2 *ar2 = usb_get_intfdata(intf);
return sprintf(buf, "0x%02x\n", ar2->mode_mask);
@@ -731,8 +728,7 @@ static ssize_t ati_remote2_store_mode_mask(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- struct usb_device *udev = to_usb_device(dev);
- struct usb_interface *intf = usb_ifnum_to_if(udev, 0);
+ struct usb_interface *intf = to_usb_interface(dev);
struct ati_remote2 *ar2 = usb_get_intfdata(intf);
unsigned int mask;
int err;
diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
index 9bcab3dc4c11..7eb0f9fe6b18 100644
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -598,6 +598,7 @@ bool nvme_change_ctrl_state(struct nvme_ctrl *ctrl,
break;
case NVME_CTRL_RESETTING:
switch (old_state) {
+ case NVME_CTRL_NEW:
case NVME_CTRL_LIVE:
changed = true;
atomic_long_inc(&ctrl->nr_reset);
@@ -2616,10 +2617,19 @@ static int nvme_update_ns_info(struct nvme_ns *ns, struct nvme_ns_info *info)
set_capacity_and_notify(ns->head->disk, get_capacity(ns->disk));
set_disk_ro(ns->head->disk, nvme_ns_is_readonly(ns, info));
nvme_mpath_revalidate_paths(ns->head);
- ret = nvme_mpath_revalidate_zones(ns->head);
unfreeze_head_queue:
blk_mq_unfreeze_queue(ns->head->disk->queue, memflags);
+
+ /*
+ * Wait until the head queue is unfrozen before revalidating
+ * its zones. blk_revalidate_disk_zones() takes the queue limits
+ * lock and then freezes the queue on its own, so it must not be
+ * called with the queue already frozen. This is also what
+ * nvme_update_ns_info_block() does for ns->disk.
+ */
+ if (!ret)
+ ret = nvme_mpath_revalidate_zones(ns->head);
}
return ret;
diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c
index 8eea0a504308..07a74c2e1f58 100644
--- a/drivers/nvme/target/core.c
+++ b/drivers/nvme/target/core.c
@@ -1325,7 +1325,7 @@ static int nvmet_req_alloc_p2pmem_sgls(struct pci_dev *p2p_dev,
return 0;
out_free_sg:
- pci_p2pmem_free_sgl(req->p2p_dev, req->sg);
+ pci_p2pmem_free_sgl(p2p_dev, req->sg);
out_err:
return -ENOMEM;
}
diff --git a/drivers/nvme/target/pr.c b/drivers/nvme/target/pr.c
index 09d8c63f5680..81f48cb00aee 100644
--- a/drivers/nvme/target/pr.c
+++ b/drivers/nvme/target/pr.c
@@ -51,6 +51,8 @@ u16 nvmet_set_feat_resv_notif_mask(struct nvmet_req *req, u32 mask)
if (nsid != U32_MAX) {
status = nvmet_req_find_ns(req);
+ if (status == (NVME_SC_INVALID_NS | NVME_STATUS_DNR))
+ return NVME_SC_INVALID_FIELD | NVME_STATUS_DNR;
if (status)
return status;
if (!req->ns->pr.enable)
@@ -75,6 +77,8 @@ u16 nvmet_get_feat_resv_notif_mask(struct nvmet_req *req)
u16 status;
status = nvmet_req_find_ns(req);
+ if (status == (NVME_SC_INVALID_NS | NVME_STATUS_DNR))
+ return NVME_SC_INVALID_FIELD | NVME_STATUS_DNR;
if (status)
return status;
diff --git a/drivers/regulator/ltc3676.c b/drivers/regulator/ltc3676.c
index 307c6f7a155d..d67cee4e0be0 100644
--- a/drivers/regulator/ltc3676.c
+++ b/drivers/regulator/ltc3676.c
@@ -232,7 +232,7 @@ static const struct regulator_desc ltc3676_regulators[LTC3676_NUM_REGULATORS] =
LTC3676_FIXED_REG(LDO4, ldo4, LDOB, 2),
};
-static bool ltc3676_readable_writeable_reg(struct device *dev, unsigned int reg)
+static bool ltc3676_writeable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case LTC3676_BUCK1 ... LTC3676_IRQSTAT:
@@ -243,6 +243,15 @@ static bool ltc3676_readable_writeable_reg(struct device *dev, unsigned int reg)
return false;
}
+static bool ltc3676_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case LTC3676_BUCK1 ... LTC3676_IRQSTAT:
+ return true;
+ }
+ return false;
+}
+
static bool ltc3676_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
@@ -255,8 +264,8 @@ static bool ltc3676_volatile_reg(struct device *dev, unsigned int reg)
static const struct regmap_config ltc3676_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
- .writeable_reg = ltc3676_readable_writeable_reg,
- .readable_reg = ltc3676_readable_writeable_reg,
+ .writeable_reg = ltc3676_writeable_reg,
+ .readable_reg = ltc3676_readable_reg,
.volatile_reg = ltc3676_volatile_reg,
.max_register = LTC3676_CLIRQ,
.use_single_read = true,
diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c
index c0cc6cc0afd8..09e26febfc83 100644
--- a/drivers/regulator/of_regulator.c
+++ b/drivers/regulator/of_regulator.c
@@ -929,21 +929,26 @@ static int is_supply_name(const char *name)
* before returning to the caller, and @consumers will not be
* changed.
*
+ * On success the array is allocated here and handed to the caller, which
+ * owns it from then on: release the regulators with regulator_bulk_free()
+ * and free the array itself with kfree(). The supply names live in the
+ * same allocation, so they are gone once the array is freed.
+ *
* Return: Number of regulators on success, or a negative error number
* on failure.
*/
int of_regulator_bulk_get_all(struct device *dev, struct device_node *np,
struct regulator_bulk_data **consumers)
{
- int num_consumers = 0;
+ int num_consumers = 0, names_len = 0;
struct regulator *tmp;
struct regulator_bulk_data *_consumers = NULL;
struct property *prop;
+ char *names;
int i, n = 0, ret;
- char name[64];
/*
- * first pass: get numbers of xxx-supply
+ * first pass: get numbers of xxx-supply and the room their names take
* second pass: fill consumers
*/
restart:
@@ -953,16 +958,19 @@ restart:
continue;
if (!_consumers) {
num_consumers++;
+ names_len += i + 1;
continue;
} else {
- memcpy(name, prop->name, i);
- name[i] = '\0';
- tmp = regulator_get(dev, name);
+ memcpy(names, prop->name, i);
+ names[i] = '\0';
+ tmp = regulator_get(dev, names);
if (IS_ERR(tmp)) {
ret = PTR_ERR(tmp);
goto error;
}
+ _consumers[n].supply = names;
_consumers[n].consumer = tmp;
+ names += i + 1;
n++;
continue;
}
@@ -973,9 +981,16 @@ restart:
}
if (num_consumers == 0)
return 0;
- _consumers = kmalloc_objs(struct regulator_bulk_data, num_consumers);
+ /*
+ * The supply names are kept in the same allocation as the array, so
+ * that they share its lifetime and the caller has nothing extra to
+ * free.
+ */
+ _consumers = kzalloc(size_add(size_mul(num_consumers, sizeof(*_consumers)),
+ names_len), GFP_KERNEL);
if (!_consumers)
return -ENOMEM;
+ names = (char *)(_consumers + num_consumers);
goto restart;
error:
diff --git a/drivers/regulator/tps6594-regulator.c b/drivers/regulator/tps6594-regulator.c
index 31a5218d5510..503b89a2f5f8 100644
--- a/drivers/regulator/tps6594-regulator.c
+++ b/drivers/regulator/tps6594-regulator.c
@@ -241,21 +241,21 @@ static const struct regulator_desc tps65224_buck_regs[] = {
TPS65224_MASK_BUCKS_VSET,
TPS6594_REG_BUCKX_CTRL(1),
TPS6594_BIT_BUCK_EN, 0, 0, tps65224_bucks_2_3_4_ranges,
- 4, 0, 0, NULL, 0, 0),
+ ARRAY_SIZE(tps65224_bucks_2_3_4_ranges), 0, 0, NULL, 0, 0),
TPS6594_REGULATOR("BUCK3", "buck3", TPS6594_BUCK_3,
REGULATOR_VOLTAGE, tps6594_bucks_ops, TPS65224_MASK_BUCKS_VSET,
TPS6594_REG_BUCKX_VOUT_1(2),
TPS65224_MASK_BUCKS_VSET,
TPS6594_REG_BUCKX_CTRL(2),
TPS6594_BIT_BUCK_EN, 0, 0, tps65224_bucks_2_3_4_ranges,
- 4, 0, 0, NULL, 0, 0),
+ ARRAY_SIZE(tps65224_bucks_2_3_4_ranges), 0, 0, NULL, 0, 0),
TPS6594_REGULATOR("BUCK4", "buck4", TPS6594_BUCK_4,
REGULATOR_VOLTAGE, tps6594_bucks_ops, TPS65224_MASK_BUCKS_VSET,
TPS6594_REG_BUCKX_VOUT_1(3),
TPS65224_MASK_BUCKS_VSET,
TPS6594_REG_BUCKX_CTRL(3),
TPS6594_BIT_BUCK_EN, 0, 0, tps65224_bucks_2_3_4_ranges,
- 4, 0, 0, NULL, 0, 0),
+ ARRAY_SIZE(tps65224_bucks_2_3_4_ranges), 0, 0, NULL, 0, 0),
};
static const struct tps6594_regulator_irq_type tps6594_buck1_irq_types[] = {
diff --git a/drivers/scsi/ch.c b/drivers/scsi/ch.c
index 4010fdbf813c..c36539c83ed1 100644
--- a/drivers/scsi/ch.c
+++ b/drivers/scsi/ch.c
@@ -112,7 +112,6 @@ typedef struct {
int minor;
char name[8];
struct scsi_device *device;
- struct scsi_device **dt; /* ptrs to data transfer elements */
u_int firsts[CH_TYPES];
u_int counts[CH_TYPES];
u_int voltags;
@@ -363,15 +362,10 @@ ch_readconfig(scsi_changer *ch)
vendor_labels[i]);
}
- /* look up the devices of the data transfer elements */
- ch->dt = kzalloc_objs(*ch->dt, ch->counts[CHET_DT]);
-
- if (!ch->dt) {
- kfree(buffer);
- return -ENOMEM;
- }
-
+ /* report the devices of the data transfer elements */
for (elem = 0; elem < ch->counts[CHET_DT]; elem++) {
+ struct scsi_device *sdev;
+
id = -1;
lun = 0;
if (elem < CH_DT_MAX && -1 != dt_id[elem]) {
@@ -387,10 +381,8 @@ ch_readconfig(scsi_changer *ch)
VPRINTK(KERN_INFO, "dt 0x%x: ",elem+ch->firsts[CHET_DT]);
if (data[6] & 0x80) {
VPRINTK(KERN_CONT, "not this SCSI bus\n");
- ch->dt[elem] = NULL;
} else if (0 == (data[6] & 0x30)) {
VPRINTK(KERN_CONT, "ID/LUN unknown\n");
- ch->dt[elem] = NULL;
} else {
id = ch->device->id;
lun = 0;
@@ -400,18 +392,16 @@ ch_readconfig(scsi_changer *ch)
}
if (-1 != id) {
VPRINTK(KERN_CONT, "ID %i, LUN %i, ",id,lun);
- ch->dt[elem] =
- scsi_device_lookup(ch->device->host,
- ch->device->channel,
- id,lun);
- if (!ch->dt[elem]) {
+ sdev = scsi_device_lookup(ch->device->host,
+ ch->device->channel,
+ id, lun);
+ if (!sdev) {
/* should not happen */
VPRINTK(KERN_CONT, "Huh? device not found!\n");
} else {
VPRINTK(KERN_CONT, "name: %8.8s %16.16s %4.4s\n",
- ch->dt[elem]->vendor,
- ch->dt[elem]->model,
- ch->dt[elem]->rev);
+ sdev->vendor, sdev->model, sdev->rev);
+ scsi_device_put(sdev);
}
}
}
@@ -575,7 +565,6 @@ static void ch_destroy(struct kref *ref)
scsi_changer *ch = container_of(ref, scsi_changer, ref);
ch->device = NULL;
- kfree(ch->dt);
kfree(ch);
}
diff --git a/drivers/scsi/qedi/qedi_main.c b/drivers/scsi/qedi/qedi_main.c
index 227ff7bd1bdc..f394fcbb97e7 100644
--- a/drivers/scsi/qedi/qedi_main.c
+++ b/drivers/scsi/qedi/qedi_main.c
@@ -2652,8 +2652,6 @@ retry_probe:
snprintf(host_buf, sizeof(host_buf), "host_%d", qedi->shost->host_no);
qedi_ops->common->set_name(qedi->cdev, host_buf);
- qedi_ops->register_ops(qedi->cdev, &qedi_cb_ops, qedi);
-
memset(&params, 0, sizeof(params));
params.mtu = DEF_PATH_MTU + IPV6_HDR_LEN + TCP_HDR_LEN;
qedi->ll2_mtu = DEF_PATH_MTU;
@@ -2670,6 +2668,7 @@ retry_probe:
spin_lock_init(&qedi->task_idx_lock);
mutex_init(&qedi->stats_lock);
}
+ qedi_ops->register_ops(qedi->cdev, &qedi_cb_ops, qedi);
qedi_ops->ll2->register_cb_ops(qedi->cdev, &qedi_ll2_cb_ops, qedi);
qedi_ops->ll2->start(qedi->cdev, &params);
diff --git a/drivers/spi/spi-cadence-quadspi.c b/drivers/spi/spi-cadence-quadspi.c
index ecb0be394696..5a5f1a852cbe 100644
--- a/drivers/spi/spi-cadence-quadspi.c
+++ b/drivers/spi/spi-cadence-quadspi.c
@@ -1056,8 +1056,7 @@ static int cqspi_write_setup(struct cqspi_flash_pdata *f_pdata,
* care of polling the status register.
*/
if (cqspi->wr_completion) {
- reg = readl(reg_base + CQSPI_REG_WR_COMPLETION_CTRL);
- reg |= CQSPI_REG_WR_DISABLE_AUTO_POLL;
+ reg = CQSPI_REG_WR_DISABLE_AUTO_POLL;
writel(reg, reg_base + CQSPI_REG_WR_COMPLETION_CTRL);
/*
* DAC mode require auto polling as flash needs to be polled
diff --git a/drivers/spi/spi-nxp-fspi.c b/drivers/spi/spi-nxp-fspi.c
index 67d991dd249d..66cb58f95fdb 100644
--- a/drivers/spi/spi-nxp-fspi.c
+++ b/drivers/spi/spi-nxp-fspi.c
@@ -898,10 +898,10 @@ static int nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi,
dev_dbg(f->dev, "Target device [CS:%x] selected\n", spi_get_chipselect(spi, 0));
/*
- * Per the FlexSPI reference manual (initialization sequence), MCR0 and
- * the DLL control registers should be configured while the module is in
- * stop mode (MCR0[MDIS] = 1). Enter stop mode before reconfiguring the
- * RX sample clock source and the DLL, then exit stop mode afterwards.
+ * Per the FlexSPI reference manual (initialization sequence), MCR0
+ * should be configured while the module is in stop mode (MCR0[MDIS] = 1).
+ * Enter stop mode before reconfiguring the RX sample clock source, then
+ * exit stop mode afterwards.
*/
reg = fspi_readl(f, f->iobase + FSPI_MCR0);
fspi_writel(f, reg | FSPI_MCR0_MDIS, f->iobase + FSPI_MCR0);
@@ -937,6 +937,16 @@ static int nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi,
return ret;
/*
+ * Exit stop mode before running the DLL calibration. The DLL
+ * reference clock is derived from the module clock domain, which is
+ * gated while MCR0[MDIS] = 1. Calibrating the DLL in stop mode leaves
+ * the STS2 lock bits stuck at 0 (the DLL never runs), so clear MDIS
+ * first and let the module and reference clock run.
+ */
+ reg = fspi_readl(f, f->iobase + FSPI_MCR0);
+ fspi_writel(f, reg & ~FSPI_MCR0_MDIS, f->iobase + FSPI_MCR0);
+
+ /*
* If clock rate > 100MHz, then switch from DLL override mode to
* DLL calibration mode.
*/
@@ -945,10 +955,6 @@ static int nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi,
else
nxp_fspi_dll_override(f);
- /* Exit stop mode now that MCR0 and the DLL have been reconfigured. */
- reg = fspi_readl(f, f->iobase + FSPI_MCR0);
- fspi_writel(f, reg & ~FSPI_MCR0_MDIS, f->iobase + FSPI_MCR0);
-
f->pre_op_rate = op->max_freq;
f->selected = spi_get_chipselect(spi, 0);
diff --git a/drivers/spi/spi-sg2044-nor.c b/drivers/spi/spi-sg2044-nor.c
index f3bcb8a1b92b..bc9a8ee9bce4 100644
--- a/drivers/spi/spi-sg2044-nor.c
+++ b/drivers/spi/spi-sg2044-nor.c
@@ -307,15 +307,15 @@ static ssize_t sg2044_spifmc_tran_cmd(struct sg2044_spifmc *spifmc,
return 0;
}
-static void sg2044_spifmc_trans(struct sg2044_spifmc *spifmc,
- const struct spi_mem_op *op)
+static int sg2044_spifmc_trans(struct sg2044_spifmc *spifmc,
+ const struct spi_mem_op *op)
{
if (op->data.dir == SPI_MEM_DATA_IN)
- sg2044_spifmc_read(spifmc, op);
+ return sg2044_spifmc_read(spifmc, op);
else if (op->data.dir == SPI_MEM_DATA_OUT)
- sg2044_spifmc_write(spifmc, op);
+ return sg2044_spifmc_write(spifmc, op);
else
- sg2044_spifmc_tran_cmd(spifmc, op);
+ return sg2044_spifmc_tran_cmd(spifmc, op);
}
static ssize_t sg2044_spifmc_trans_reg(struct sg2044_spifmc *spifmc,
@@ -385,29 +385,64 @@ static ssize_t sg2044_spifmc_trans_reg(struct sg2044_spifmc *spifmc,
return 0;
}
+static int sg2044_spifmc_set_speed(struct sg2044_spifmc *spifmc,
+ unsigned int speed_hz)
+{
+ unsigned long rate = clk_get_rate(spifmc->clk);
+ u64 div;
+ u32 reg;
+
+ if (!rate)
+ return -EINVAL;
+
+ /* SCK = HCLK / (2 * (SckDiv + 1)); round down the resulting SCK. */
+ div = speed_hz ? DIV_ROUND_UP_ULL(rate, 2ULL * speed_hz) : 1;
+ if (div > SPIFMC_CTRL_SCK_DIV_MASK + 1)
+ return -EINVAL;
+
+ reg = readl(spifmc->io_base + SPIFMC_CTRL);
+ reg &= ~SPIFMC_CTRL_SCK_DIV_MASK;
+ reg |= div - 1;
+ writel(reg, spifmc->io_base + SPIFMC_CTRL);
+
+ return 0;
+}
+
static int sg2044_spifmc_exec_op(struct spi_mem *mem,
const struct spi_mem_op *op)
{
struct sg2044_spifmc *spifmc;
+ int ret;
spifmc = spi_controller_get_devdata(mem->spi->controller);
mutex_lock(&spifmc->lock);
+ ret = sg2044_spifmc_set_speed(spifmc, op->max_freq ?
+ min(op->max_freq, mem->spi->max_speed_hz) :
+ mem->spi->max_speed_hz);
+ if (ret)
+ goto out_unlock;
+
if (op->addr.nbytes == 0)
- sg2044_spifmc_trans_reg(spifmc, op);
+ ret = sg2044_spifmc_trans_reg(spifmc, op);
else
- sg2044_spifmc_trans(spifmc, op);
+ ret = sg2044_spifmc_trans(spifmc, op);
+out_unlock:
mutex_unlock(&spifmc->lock);
- return 0;
+ return ret;
}
static const struct spi_controller_mem_ops sg2044_spifmc_mem_ops = {
.exec_op = sg2044_spifmc_exec_op,
};
+static const struct spi_controller_mem_caps sg2044_spifmc_mem_caps = {
+ .per_op_freq = true,
+};
+
static void sg2044_spifmc_init(struct sg2044_spifmc *spifmc)
{
u32 tran_csr;
@@ -447,6 +482,10 @@ static int sg2044_spifmc_probe(struct platform_device *pdev)
if (IS_ERR(spifmc->clk))
return dev_err_probe(dev, PTR_ERR(spifmc->clk), "Cannot get and enable AHB clock\n");
+ ctrl->max_speed_hz = clk_get_rate(spifmc->clk) / 2;
+ if (!ctrl->max_speed_hz)
+ return dev_err_probe(dev, -EINVAL, "Invalid AHB clock rate\n");
+
spifmc->dev = &pdev->dev;
spifmc->ctrl = ctrl;
@@ -458,6 +497,7 @@ static int sg2044_spifmc_probe(struct platform_device *pdev)
ctrl->bits_per_word_mask = SPI_BPW_MASK(8);
ctrl->auto_runtime_pm = false;
ctrl->mem_ops = &sg2044_spifmc_mem_ops;
+ ctrl->mem_caps = &sg2044_spifmc_mem_caps;
ctrl->mode_bits = SPI_RX_DUAL | SPI_TX_DUAL | SPI_RX_QUAD | SPI_TX_QUAD;
ret = devm_mutex_init(dev, &spifmc->lock);
diff --git a/drivers/ufs/core/ufs-mcq.c b/drivers/ufs/core/ufs-mcq.c
index 8106d55f4041..df293b66350d 100644
--- a/drivers/ufs/core/ufs-mcq.c
+++ b/drivers/ufs/core/ufs-mcq.c
@@ -312,20 +312,24 @@ static int ufshcd_mcq_get_tag(struct ufs_hba *hba, struct cq_entry *cqe)
UFSHCD_NUM_RESERVED;
}
+static void ufshcd_mcq_compl_cqe(struct ufs_hba *hba, struct cq_entry *cqe)
+{
+ int tag = ufshcd_mcq_get_tag(hba, cqe);
+
+ ufshcd_compl_one_cqe(hba, tag, cqe);
+ /* After processing the CQE, mark it as an empty (invalid) entry. */
+ cqe->command_desc_base_addr = 0;
+}
+
static void ufshcd_mcq_process_cqe(struct ufs_hba *hba,
struct ufs_hw_queue *hwq)
{
struct cq_entry *cqe = ufshcd_mcq_cur_cqe(hwq);
- if (cqe->command_desc_base_addr) {
- int tag = ufshcd_mcq_get_tag(hba, cqe);
-
- ufshcd_compl_one_cqe(hba, tag, cqe);
- /* After processed the cqe, mark it empty (invalid) entry */
- cqe->command_desc_base_addr = 0;
- } else {
+ if (cqe->command_desc_base_addr)
+ ufshcd_mcq_compl_cqe(hba, cqe);
+ else
dev_err(hba->dev, "Abnormal CQ entry!\n");
- }
}
/*
@@ -333,7 +337,7 @@ static void ufshcd_mcq_process_cqe(struct ufs_hba *hba,
* controller disabled (HCE = 0). Reading host controller registers, e.g. the
* CQ tail pointer (CQTPy), may not be safe with the host controller disabled.
* Hence, iterate over all completion queue entries. This won't result in
- * double completions because ufshcd_mcq_process_cqe() clears a CQE after it
+ * double completions because ufshcd_mcq_compl_cqe() clears a CQE after it
* has been processed.
*/
void ufshcd_mcq_compl_all_cqes_lock(struct ufs_hba *hba,
@@ -344,13 +348,13 @@ void ufshcd_mcq_compl_all_cqes_lock(struct ufs_hba *hba,
spin_lock_irqsave(&hwq->cq_lock, flags);
while (entries > 0) {
- ufshcd_mcq_process_cqe(hba, hwq);
+ struct cq_entry *cqe = ufshcd_mcq_cur_cqe(hwq);
+
+ if (cqe->command_desc_base_addr)
+ ufshcd_mcq_compl_cqe(hba, cqe);
ufshcd_mcq_inc_cq_head_slot(hwq);
entries--;
}
-
- ufshcd_mcq_update_cq_tail_slot(hwq);
- hwq->cq_head_slot = hwq->cq_tail_slot;
spin_unlock_irqrestore(&hwq->cq_lock, flags);
}
diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
index 54f4e7d7de02..ca8329052cbc 100644
--- a/drivers/ufs/core/ufshcd.c
+++ b/drivers/ufs/core/ufshcd.c
@@ -6044,8 +6044,6 @@ static bool ufshcd_mcq_force_compl_one(struct request *rq, void *priv)
if (blk_mq_is_reserved_rq(rq) || !hwq)
return true;
- ufshcd_mcq_compl_all_cqes_lock(hba, hwq);
-
/*
* For those cmds of which the cqes are not present in the cq, complete
* them explicitly.
@@ -6061,19 +6059,6 @@ static bool ufshcd_mcq_force_compl_one(struct request *rq, void *priv)
return true;
}
-static bool ufshcd_mcq_compl_one(struct request *rq, void *priv)
-{
- struct scsi_device *sdev = rq->q->queuedata;
- struct Scsi_Host *shost = sdev->host;
- struct ufs_hba *hba = shost_priv(shost);
- struct ufs_hw_queue *hwq = ufshcd_mcq_req_to_hwq(hba, rq);
-
- if (!blk_mq_is_reserved_rq(rq) && hwq)
- ufshcd_mcq_poll_cqe_lock(hba, hwq);
-
- return true;
-}
-
/**
* ufshcd_mcq_compl_pending_transfer - MCQ mode function. It is
* invoked from the error handler context or ufshcd_host_reset_and_restore()
@@ -6088,10 +6073,18 @@ static bool ufshcd_mcq_compl_one(struct request *rq, void *priv)
static void ufshcd_mcq_compl_pending_transfer(struct ufs_hba *hba,
bool force_compl)
{
- blk_mq_tagset_busy_iter(&hba->host->tag_set,
- force_compl ? ufshcd_mcq_force_compl_one :
- ufshcd_mcq_compl_one,
- NULL);
+ int i;
+
+ for (i = 0; i < hba->nr_hw_queues; i++) {
+ if (force_compl)
+ ufshcd_mcq_compl_all_cqes_lock(hba, &hba->uhq[i]);
+ else
+ ufshcd_mcq_poll_cqe_lock(hba, &hba->uhq[i]);
+ }
+
+ if (force_compl)
+ blk_mq_tagset_busy_iter(&hba->host->tag_set,
+ ufshcd_mcq_force_compl_one, NULL);
}
/**
@@ -9556,6 +9549,7 @@ static void ufshcd_async_scan(void *data, async_cookie_t cookie)
ret = ufshcd_add_lus(hba);
out:
+ ufshcd_release(hba);
pm_runtime_put_sync(hba->dev);
if (ret)
@@ -11278,6 +11272,9 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
ufshcd_init_clk_gating(hba);
+ /* Released by ufshcd_async_scan(), or by out_release on failure. */
+ ufshcd_hold(hba);
+
ufshcd_init_clk_scaling(hba);
/*
@@ -11298,7 +11295,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
err = devm_request_irq(dev, irq, ufshcd_intr, IRQF_SHARED, UFSHCD, hba);
if (err) {
dev_err(hba->dev, "request irq failed\n");
- goto out_disable;
+ goto out_release;
} else {
hba->is_irq_enabled = true;
}
@@ -11314,7 +11311,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
dev_err(hba->dev, "Host controller enable failed\n");
ufshcd_print_evt_hist(hba);
ufshcd_print_host_state(hba);
- goto out_disable;
+ goto out_release;
}
INIT_DELAYED_WORK(&hba->rpm_dev_flush_recheck_work, ufshcd_rpm_dev_flush_recheck_work);
@@ -11328,7 +11325,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
err = ufshcd_add_scsi_host(hba);
if (err)
- goto out_disable;
+ goto out_release;
/* Hold auto suspend until async scan completes */
pm_runtime_get_sync(dev);
@@ -11348,7 +11345,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
err = ufshcd_link_startup(hba);
if (err)
- goto out_disable;
+ goto out_release;
if (hba->mcq_enabled)
ufshcd_config_mcq(hba);
@@ -11365,23 +11362,23 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
/* Verify device initialization by sending NOP OUT UPIU */
err = ufshcd_verify_dev_init(hba);
if (err)
- goto out_disable;
+ goto out_release;
/* Initiate UFS initialization, and waiting until completion */
err = ufshcd_complete_dev_init(hba);
if (err)
- goto out_disable;
+ goto out_release;
err = ufshcd_device_params_init(hba);
if (err)
- goto out_disable;
+ goto out_release;
err = ufshcd_post_device_init(hba);
initialized:
ufshcd_process_probe_result(hba, probe_start, err);
if (err)
- goto out_disable;
+ goto out_release;
ufs_sysfs_add_nodes(hba->dev);
hba->dme_qos_sysfs_handle = sysfs_get_dirent(hba->dev->kobj.sd,
@@ -11392,6 +11389,8 @@ initialized:
ufshcd_pm_qos_init(hba);
return 0;
+out_release:
+ ufshcd_release(hba);
out_disable:
hba->is_irq_enabled = false;
ufshcd_hba_exit(hba);
diff --git a/drivers/ufs/host/ufs-mediatek.c b/drivers/ufs/host/ufs-mediatek.c
index 814c1b7343b9..12a6d4138e44 100644
--- a/drivers/ufs/host/ufs-mediatek.c
+++ b/drivers/ufs/host/ufs-mediatek.c
@@ -509,6 +509,7 @@ static int ufs_mtk_mphy_power_on(struct ufs_hba *hba, bool on)
struct ufs_mtk_host *host = ufshcd_get_variant(hba);
struct phy *mphy = host->mphy;
struct arm_smccc_res res;
+ int err;
int ret = 0;
if (!mphy || !(on ^ host->mphy_powered_on))
@@ -523,7 +524,18 @@ static int ufs_mtk_mphy_power_on(struct ufs_hba *hba, bool on)
usleep_range(200, 210);
ufs_mtk_va09_pwr_ctrl(res, 1);
}
- phy_power_on(mphy);
+ ret = phy_power_on(mphy);
+ if (ret) {
+ if (ufs_mtk_is_va09_supported(hba)) {
+ ufs_mtk_va09_pwr_ctrl(res, 0);
+ err = regulator_disable(host->reg_va09);
+ if (err)
+ dev_err(hba->dev,
+ "failed to unwind va09: %d\n", err);
+ }
+ dev_err(hba->dev, "failed to enable mphy: %d\n", ret);
+ return ret;
+ }
} else {
phy_power_off(mphy);
if (ufs_mtk_is_va09_supported(hba)) {
@@ -1295,7 +1307,9 @@ static int ufs_mtk_init(struct ufs_hba *hba)
*
* Enable phy clocks specifically here.
*/
- ufs_mtk_mphy_power_on(hba, true);
+ err = ufs_mtk_mphy_power_on(hba, true);
+ if (err)
+ goto out_variant_clear;
if (ufs_mtk_is_rtff_mtcmos(hba)) {
/* First Restore here, to avoid backup unexpected value */
diff --git a/fs/namespace.c b/fs/namespace.c
index 54b88e1b3f4d..4a5e963fd873 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -3163,7 +3163,8 @@ static struct mnt_namespace *get_detached_copy(const struct path *path, unsigned
ns->seq_origin = src_mnt_ns->ns.ns_id;
}
- mnt = __do_loopback(path, (flags & AT_RECURSIVE), CL_COPY_MNT_NS_FILE);
+ mnt = __do_loopback(path, (flags & AT_RECURSIVE),
+ (flags & OPEN_TREE_DROP_MNTNS_MOUNTS) ? 0 : CL_COPY_MNT_NS_FILE);
if (IS_ERR(mnt)) {
emptied_ns = ns;
return ERR_CAST(mnt);
@@ -3315,7 +3316,18 @@ static struct file *vfs_open_tree(int dfd, const char __user *filename, unsigned
if (flags & ~(AT_EMPTY_PATH | AT_NO_AUTOMOUNT | AT_RECURSIVE |
AT_SYMLINK_NOFOLLOW | OPEN_TREE_CLONE |
- OPEN_TREE_CLOEXEC | OPEN_TREE_NAMESPACE))
+ OPEN_TREE_CLOEXEC | OPEN_TREE_NAMESPACE |
+ OPEN_TREE_DROP_MNTNS_MOUNTS))
+ return ERR_PTR(-EINVAL);
+
+ /*
+ * Only meaningful when a tree is copied recursively: a non-recursive
+ * clone has no submounts to drop. OPEN_TREE_NAMESPACE never copies
+ * pinned mount namespaces, so there the flag is a no-op.
+ */
+ if ((flags & OPEN_TREE_DROP_MNTNS_MOUNTS) &&
+ (!(flags & AT_RECURSIVE) ||
+ !(flags & (OPEN_TREE_CLONE | OPEN_TREE_NAMESPACE))))
return ERR_PTR(-EINVAL);
if ((flags & (AT_RECURSIVE | OPEN_TREE_CLONE | OPEN_TREE_NAMESPACE)) ==
diff --git a/fs/xfs/libxfs/xfs_exchmaps.c b/fs/xfs/libxfs/xfs_exchmaps.c
index 6a66b6075e0a..f527fce46def 100644
--- a/fs/xfs/libxfs/xfs_exchmaps.c
+++ b/fs/xfs/libxfs/xfs_exchmaps.c
@@ -49,7 +49,7 @@ struct xfs_exchmaps_adjacent {
.right2 = { .br_startblock = HOLESTARTBLOCK }, \
}
-/* Information to reset reflink flag / CoW fork state after an exchange. */
+/* Information to reset CoW fork state after an exchange. */
/*
* If the reflink flag is set on either inode, make sure it has an incore CoW
@@ -127,9 +127,7 @@ xmi_has_more_exchange_work(const struct xfs_exchmaps_intent *xmi)
static inline bool
xmi_has_postop_work(const struct xfs_exchmaps_intent *xmi)
{
- return xmi->xmi_flags & (XFS_EXCHMAPS_CLEAR_INO1_REFLINK |
- XFS_EXCHMAPS_CLEAR_INO2_REFLINK |
- __XFS_EXCHMAPS_INO2_SHORTFORM);
+ return xmi->xmi_flags & __XFS_EXCHMAPS_INO2_SHORTFORM;
}
/* Check all mappings to make sure we can actually exchange them. */
@@ -525,18 +523,6 @@ free:
return error;
}
-/* Clear the reflink flag after an exchange. */
-static inline void
-xfs_exchmaps_clear_reflink(
- struct xfs_trans *tp,
- struct xfs_inode *ip)
-{
- trace_xfs_reflink_unset_inode_flag(ip);
-
- ip->i_diflags2 &= ~XFS_DIFLAG2_REFLINK;
- xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
-}
-
/* Finish whatever work might come after an exchange operation. */
static int
xfs_exchmaps_do_postop_work(
@@ -557,16 +543,6 @@ xfs_exchmaps_do_postop_work(
return error;
}
- if (xmi->xmi_flags & XFS_EXCHMAPS_CLEAR_INO1_REFLINK) {
- xfs_exchmaps_clear_reflink(tp, xmi->xmi_ip1);
- xmi->xmi_flags &= ~XFS_EXCHMAPS_CLEAR_INO1_REFLINK;
- }
-
- if (xmi->xmi_flags & XFS_EXCHMAPS_CLEAR_INO2_REFLINK) {
- xfs_exchmaps_clear_reflink(tp, xmi->xmi_ip2);
- xmi->xmi_flags &= ~XFS_EXCHMAPS_CLEAR_INO2_REFLINK;
- }
-
return 0;
}
@@ -948,46 +924,14 @@ xfs_exchmaps_intent_destroy_cache(void)
}
/*
- * Decide if we will exchange the reflink flags between the two files after the
- * exchange. The only time we want to do this is if we're exchanging all
- * mappings under EOF and the inode reflink flags have different states.
+ * Allocate and initialize a new incore intent item from a request.
*/
-static inline bool
-xmi_can_exchange_reflink_flags(
- const struct xfs_exchmaps_req *req,
- unsigned int reflink_state)
-{
- struct xfs_mount *mp = req->ip1->i_mount;
-
- /*
- * The INO1_WRITTEN optimization can skip exchanging hole and
- * unwritten mappings, which means we cannot guarantee that all
- * shared extents actually moved to the other file. Clearing the
- * reflink flag of an inode that still holds shared extents breaks
- * the CoW write path, so refuse to exchange the flags in that case.
- */
- if (req->flags & XFS_EXCHMAPS_INO1_WRITTEN)
- return false;
-
- if (hweight32(reflink_state) != 1)
- return false;
- if (req->startoff1 != 0 || req->startoff2 != 0)
- return false;
- if (req->blockcount != XFS_B_TO_FSB(mp, req->ip1->i_disk_size))
- return false;
- if (req->blockcount != XFS_B_TO_FSB(mp, req->ip2->i_disk_size))
- return false;
- return true;
-}
-
-
-/* Allocate and initialize a new incore intent item from a request. */
struct xfs_exchmaps_intent *
xfs_exchmaps_init_intent(
const struct xfs_exchmaps_req *req)
{
struct xfs_exchmaps_intent *xmi;
- unsigned int rs = 0;
+ struct xfs_mount *mp = req->ip1->i_mount;
xmi = kmem_cache_zalloc(xfs_exchmaps_intent_cache,
GFP_NOFS | __GFP_NOFAIL);
@@ -1005,27 +949,18 @@ xfs_exchmaps_init_intent(
return xmi;
}
- if (req->flags & XFS_EXCHMAPS_SET_SIZES) {
- xmi->xmi_flags |= XFS_EXCHMAPS_SET_SIZES;
- xmi->xmi_isize1 = req->ip2->i_disk_size;
- xmi->xmi_isize2 = req->ip1->i_disk_size;
- }
-
- /* Record the state of each inode's reflink flag before the op. */
- if (xfs_is_reflink_inode(req->ip1))
- rs |= 1;
- if (xfs_is_reflink_inode(req->ip2))
- rs |= 2;
-
/*
- * Figure out if we're clearing the reflink flags (which effectively
- * exchanges them) after the operation.
+ * If the caller wanted, set each file's size to that file's exchange
+ * offset + length exchanged from the other file because the ranges in
+ * each file might be different lengths.
*/
- if (xmi_can_exchange_reflink_flags(req, rs)) {
- if (rs & 1)
- xmi->xmi_flags |= XFS_EXCHMAPS_CLEAR_INO1_REFLINK;
- if (rs & 2)
- xmi->xmi_flags |= XFS_EXCHMAPS_CLEAR_INO2_REFLINK;
+ if (req->flags & XFS_EXCHMAPS_SET_SIZES) {
+ loff_t off1 = XFS_FSB_TO_B(mp, xmi->xmi_startoff1);
+ loff_t off2 = XFS_FSB_TO_B(mp, xmi->xmi_startoff2);
+
+ xmi->xmi_flags |= XFS_EXCHMAPS_SET_SIZES;
+ xmi->xmi_isize1 = off1 + (req->ip2->i_disk_size - off2);
+ xmi->xmi_isize2 = off2 + (req->ip1->i_disk_size - off1);
}
if (S_ISDIR(VFS_I(xmi->xmi_ip2)->i_mode) ||
diff --git a/fs/xfs/libxfs/xfs_log_format.h b/fs/xfs/libxfs/xfs_log_format.h
index a4e1b3eb425c..6e620380d5dc 100644
--- a/fs/xfs/libxfs/xfs_log_format.h
+++ b/fs/xfs/libxfs/xfs_log_format.h
@@ -927,10 +927,8 @@ struct xfs_xmi_log_format {
*/
#define XFS_EXCHMAPS_INO1_WRITTEN (1ULL << 2)
-/* Clear the reflink flag from inode1 after the operation. */
+/* No longer set, but older intents may carry them; keep them valid. */
#define XFS_EXCHMAPS_CLEAR_INO1_REFLINK (1ULL << 3)
-
-/* Clear the reflink flag from inode2 after the operation. */
#define XFS_EXCHMAPS_CLEAR_INO2_REFLINK (1ULL << 4)
#define XFS_EXCHMAPS_LOGGED_FLAGS (XFS_EXCHMAPS_ATTR_FORK | \
diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c
index 268d159339d0..865aec3ae5ab 100644
--- a/fs/xfs/xfs_bmap_util.c
+++ b/fs/xfs/xfs_bmap_util.c
@@ -1544,7 +1544,6 @@ xfs_swap_extents(
struct xfs_bstat *sbp = &sxp->sx_stat;
int src_log_flags, target_log_flags;
int error = 0;
- uint64_t f;
int resblks = 0;
unsigned int flags = 0;
struct timespec64 ctime, mtime;
@@ -1700,14 +1699,12 @@ xfs_swap_extents(
if (error)
goto out_trans_cancel;
- /* Do we have to swap reflink flags? */
- if ((ip->i_diflags2 & XFS_DIFLAG2_REFLINK) ^
- (tip->i_diflags2 & XFS_DIFLAG2_REFLINK)) {
- f = ip->i_diflags2 & XFS_DIFLAG2_REFLINK;
- ip->i_diflags2 &= ~XFS_DIFLAG2_REFLINK;
- ip->i_diflags2 |= tip->i_diflags2 & XFS_DIFLAG2_REFLINK;
- tip->i_diflags2 &= ~XFS_DIFLAG2_REFLINK;
- tip->i_diflags2 |= f & XFS_DIFLAG2_REFLINK;
+ /* Either inode may now own shared blocks; flag both, with CoW forks. */
+ if ((ip->i_diflags2 | tip->i_diflags2) & XFS_DIFLAG2_REFLINK) {
+ ip->i_diflags2 |= XFS_DIFLAG2_REFLINK;
+ tip->i_diflags2 |= XFS_DIFLAG2_REFLINK;
+ xfs_ifork_init_cow(ip);
+ xfs_ifork_init_cow(tip);
}
/* Swap the cow forks. */
diff --git a/fs/xfs/xfs_exchrange.c b/fs/xfs/xfs_exchrange.c
index fafb4e3f065c..935b561dfd3c 100644
--- a/fs/xfs/xfs_exchrange.c
+++ b/fs/xfs/xfs_exchrange.c
@@ -290,17 +290,20 @@ retry:
goto out_unlock;
/*
- * If the caller wanted us to exchange the contents of two complete
- * files of unequal length, exchange the incore sizes now. This should
- * be safe because we flushed both files' page caches, exchanged all
- * the mappings, and updated the ondisk sizes.
+ * If the caller wanted, set each file's size to that file's exchange
+ * offset + length exchanged from the other file because the ranges in
+ * each file might be different lengths. This should be safe because
+ * we flushed both files' page caches, exchanged all the mappings, and
+ * updated the ondisk sizes.
*/
if (fxr->flags & XFS_EXCHANGE_RANGE_TO_EOF) {
- loff_t temp;
+ loff_t old_ip1_size = i_size_read(VFS_I(ip1));
+ loff_t old_ip2_size = i_size_read(VFS_I(ip2));
- temp = i_size_read(VFS_I(ip2));
- i_size_write(VFS_I(ip2), i_size_read(VFS_I(ip1)));
- i_size_write(VFS_I(ip1), temp);
+ i_size_write(VFS_I(ip2), fxr->file2_offset +
+ (old_ip1_size - fxr->file1_offset));
+ i_size_write(VFS_I(ip1), fxr->file1_offset +
+ (old_ip2_size - fxr->file2_offset));
}
out_unlock:
@@ -445,6 +448,30 @@ xfs_exchange_range_checks(
return blen == fxr->length ? 0 : -EINVAL;
}
+/* Flush all relevant dirty pagecache near the range to be exchanged. */
+static inline int
+xfs_exchange_range_flush(
+ struct xfs_inode *ip,
+ const struct xfs_exchrange *fxr,
+ loff_t offset)
+{
+ /*
+ * If TO_EOF is set, the ondisk file size update logic depends on the
+ * ondisk file size matching the incore file size. Flush everything.
+ */
+ if (fxr->flags & XFS_EXCHANGE_RANGE_TO_EOF)
+ return filemap_write_and_wait(VFS_I(ip)->i_mapping);
+
+ /*
+ * Because we're updating the ondisk mappings, flush all dirty data
+ * between the ondisk size and the exchange offset to reduce the chance
+ * of zeroed file contents in that gap after a crash.
+ */
+ return filemap_write_and_wait_range(VFS_I(ip)->i_mapping,
+ min(offset, ip->i_disk_size),
+ offset + fxr->length - 1);
+}
+
/*
* Check that the two inodes are eligible for range exchanges, the ranges make
* sense, and then flush all dirty data. Caller must ensure that the inodes
@@ -470,15 +497,11 @@ xfs_exchange_range_prep(
if (!same_inode)
inode_dio_wait(inode2);
- error = filemap_write_and_wait_range(inode1->i_mapping,
- fxr->file1_offset,
- fxr->file1_offset + fxr->length - 1);
+ error = xfs_exchange_range_flush(XFS_I(inode1), fxr, fxr->file1_offset);
if (error)
return error;
- error = filemap_write_and_wait_range(inode2->i_mapping,
- fxr->file2_offset,
- fxr->file2_offset + fxr->length - 1);
+ error = xfs_exchange_range_flush(XFS_I(inode2), fxr, fxr->file2_offset);
if (error)
return error;
diff --git a/include/linux/io_uring_types.h b/include/linux/io_uring_types.h
index 39629ee77b91..8126af652845 100644
--- a/include/linux/io_uring_types.h
+++ b/include/linux/io_uring_types.h
@@ -322,6 +322,8 @@ enum {
IO_RING_F_DRAIN_DISABLED = BIT(10),
IO_RING_F_COMPAT = BIT(11),
IO_RING_F_IOWQ_LIMITS_SET = BIT(12),
+ /* restrictions were inherited from the task that created the ring */
+ IO_RING_F_RESTRICT_INHERITED = BIT(13),
};
struct iou_ctx {};
diff --git a/include/linux/nvme.h b/include/linux/nvme.h
index 91ce434a7e8d..6248711784b3 100644
--- a/include/linux/nvme.h
+++ b/include/linux/nvme.h
@@ -1266,8 +1266,8 @@ enum {
NVME_ZRASF_ZONE_STATE_IMP_OPEN = 0x02,
NVME_ZRASF_ZONE_STATE_EXP_OPEN = 0x03,
NVME_ZRASF_ZONE_STATE_CLOSED = 0x04,
- NVME_ZRASF_ZONE_STATE_READONLY = 0x05,
- NVME_ZRASF_ZONE_STATE_FULL = 0x06,
+ NVME_ZRASF_ZONE_STATE_FULL = 0x05,
+ NVME_ZRASF_ZONE_STATE_READONLY = 0x06,
NVME_ZRASF_ZONE_STATE_OFFLINE = 0x07,
NVME_REPORT_ZONE_PARTIAL = 1,
};
diff --git a/include/uapi/linux/mount.h b/include/uapi/linux/mount.h
index 2204708dbf7a..ac865edac517 100644
--- a/include/uapi/linux/mount.h
+++ b/include/uapi/linux/mount.h
@@ -63,6 +63,7 @@
*/
#define OPEN_TREE_CLONE (1 << 0) /* Clone the target tree and attach the clone */
#define OPEN_TREE_NAMESPACE (1 << 1) /* Clone the target tree into a new mount namespace */
+#define OPEN_TREE_DROP_MNTNS_MOUNTS (1 << 2) /* Drop mntns mounts from the clone */
#define OPEN_TREE_CLOEXEC O_CLOEXEC /* Close the file on execve() */
/*
diff --git a/io_uring/cancel.c b/io_uring/cancel.c
index 7d7820eab878..4f706aff14fc 100644
--- a/io_uring/cancel.c
+++ b/io_uring/cancel.c
@@ -634,12 +634,11 @@ __cold void io_uring_cancel_generic(bool cancel_all, struct io_sq_data *sqd)
prepare_to_wait(&tctx->wait, &wait, TASK_INTERRUPTIBLE);
io_run_task_work();
io_uring_drop_tctx_refs(current);
+ /* only skip the sleep for local work this task may run */
xa_for_each(&tctx->xa, index, node) {
- if (io_local_work_pending(node->ctx)) {
- WARN_ON_ONCE(node->ctx->submitter_task &&
- node->ctx->submitter_task != current);
+ if (io_local_work_pending(node->ctx) &&
+ io_allowed_defer_tw_run(node->ctx))
goto end_wait;
- }
}
/*
* If we've seen completions, retry without waiting. This
diff --git a/io_uring/io_uring.c b/io_uring/io_uring.c
index cbea989c5a7c..cf5218d100d9 100644
--- a/io_uring/io_uring.c
+++ b/io_uring/io_uring.c
@@ -2070,8 +2070,9 @@ int io_submit_sqes(struct io_ring_ctx *ctx, unsigned int nr)
static void io_rings_free(struct io_ring_ctx *ctx)
{
- io_free_region(ctx->user, &ctx->sq_region);
- io_free_region(ctx->user, &ctx->ring_region);
+ /* ring memory is not charged to RLIMIT_MEMLOCK, hence no user */
+ io_free_region(NULL, &ctx->sq_region);
+ io_free_region(NULL, &ctx->ring_region);
ctx->rings = NULL;
RCU_INIT_POINTER(ctx->rings_rcu, NULL);
ctx->sq_sqes = NULL;
@@ -2735,7 +2736,8 @@ static __cold int io_allocate_scq_urings(struct io_ring_ctx *ctx,
rd.user_addr = p->cq_off.user_addr;
rd.flags |= IORING_MEM_REGION_TYPE_USER;
}
- ret = io_create_region(ctx, &ctx->ring_region, &rd, IORING_OFF_CQ_RING);
+ /* ring memory is not charged to RLIMIT_MEMLOCK, hence no user */
+ ret = io_create_region(NULL, &ctx->ring_region, &rd, IORING_OFF_CQ_RING);
if (ret)
return ret;
ctx->rings = rings = io_region_get_ptr(&ctx->ring_region);
@@ -2749,7 +2751,7 @@ static __cold int io_allocate_scq_urings(struct io_ring_ctx *ctx,
rd.user_addr = p->sq_off.user_addr;
rd.flags |= IORING_MEM_REGION_TYPE_USER;
}
- ret = io_create_region(ctx, &ctx->sq_region, &rd, IORING_OFF_SQES);
+ ret = io_create_region(NULL, &ctx->sq_region, &rd, IORING_OFF_SQES);
if (ret) {
io_rings_free(ctx);
return ret;
@@ -2960,7 +2962,8 @@ static void io_ctx_restriction_clone(struct io_ring_ctx *ctx,
if (dst->op_registered)
ctx->int_flags |= IO_RING_F_OP_RESTRICTED;
if (dst->reg_registered)
- ctx->int_flags |= IO_RING_F_REG_RESTRICTED;
+ ctx->int_flags |= IO_RING_F_REG_RESTRICTED |
+ IO_RING_F_RESTRICT_INHERITED;
}
static __cold int io_uring_create(struct io_ctx_config *config)
diff --git a/io_uring/kbuf.c b/io_uring/kbuf.c
index 7c309173dd19..6e2facf34bfa 100644
--- a/io_uring/kbuf.c
+++ b/io_uring/kbuf.c
@@ -459,7 +459,7 @@ static int io_remove_buffers_legacy(struct io_ring_ctx *ctx,
static void io_put_bl(struct io_ring_ctx *ctx, struct io_buffer_list *bl)
{
if (bl->flags & IOBL_BUF_RING)
- io_free_region(ctx->user, &bl->region);
+ io_free_region(NULL, &bl->region);
else
io_remove_buffers_legacy(ctx, bl, -1U);
@@ -676,7 +676,7 @@ int io_register_pbuf_ring(struct io_ring_ctx *ctx, void __user *arg)
rd.user_addr = reg.ring_addr;
rd.flags |= IORING_MEM_REGION_TYPE_USER;
}
- ret = io_create_region(ctx, &bl->region, &rd, mmap_offset);
+ ret = io_create_region(NULL, &bl->region, &rd, mmap_offset);
if (ret)
goto fail;
br = io_region_get_ptr(&bl->region);
@@ -709,7 +709,7 @@ int io_register_pbuf_ring(struct io_ring_ctx *ctx, void __user *arg)
if (!ret)
return 0;
fail:
- io_free_region(ctx->user, &bl->region);
+ io_free_region(NULL, &bl->region);
kfree(bl);
return ret;
}
diff --git a/io_uring/memmap.c b/io_uring/memmap.c
index 48c0eb012412..d4d716946971 100644
--- a/io_uring/memmap.c
+++ b/io_uring/memmap.c
@@ -204,7 +204,7 @@ done:
return 0;
}
-int io_create_region(struct io_ring_ctx *ctx, struct io_mapped_region *mr,
+int io_create_region(struct user_struct *user, struct io_mapped_region *mr,
struct io_uring_region_desc *reg,
unsigned long mmap_offset)
{
@@ -230,8 +230,8 @@ int io_create_region(struct io_ring_ctx *ctx, struct io_mapped_region *mr,
return -EOVERFLOW;
nr_pages = reg->size >> PAGE_SHIFT;
- if (ctx->user) {
- ret = __io_account_mem(ctx->user, nr_pages);
+ if (user) {
+ ret = __io_account_mem(user, nr_pages);
if (ret)
return ret;
}
@@ -240,7 +240,7 @@ int io_create_region(struct io_ring_ctx *ctx, struct io_mapped_region *mr,
if (reg->flags & IORING_MEM_REGION_TYPE_USER)
ret = io_region_pin_pages(mr, reg);
else
- ret = io_region_allocate_pages(mr, reg, mmap_offset, ctx->user);
+ ret = io_region_allocate_pages(mr, reg, mmap_offset, user);
if (ret)
goto out_free;
@@ -249,7 +249,7 @@ int io_create_region(struct io_ring_ctx *ctx, struct io_mapped_region *mr,
goto out_free;
return 0;
out_free:
- io_free_region(ctx->user, mr);
+ io_free_region(user, mr);
return ret;
}
diff --git a/io_uring/memmap.h b/io_uring/memmap.h
index f4cfbb6b9a1f..0714bb5a9616 100644
--- a/io_uring/memmap.h
+++ b/io_uring/memmap.h
@@ -18,7 +18,7 @@ unsigned long io_uring_get_unmapped_area(struct file *file, unsigned long addr,
int io_uring_mmap(struct file *file, struct vm_area_struct *vma);
void io_free_region(struct user_struct *user, struct io_mapped_region *mr);
-int io_create_region(struct io_ring_ctx *ctx, struct io_mapped_region *mr,
+int io_create_region(struct user_struct *user, struct io_mapped_region *mr,
struct io_uring_region_desc *reg,
unsigned long mmap_offset);
diff --git a/io_uring/net.c b/io_uring/net.c
index 050ed274170a..f5f4f3ce6ef3 100644
--- a/io_uring/net.c
+++ b/io_uring/net.c
@@ -7,6 +7,7 @@
#include <linux/un.h>
#include <linux/compat.h>
#include <net/compat.h>
+#include <net/ipv6.h>
#include <linux/io_uring.h>
#include <uapi/linux/io_uring.h>
@@ -1741,7 +1742,8 @@ void io_connect_bpf_populate(struct io_uring_bpf_ctx *bctx, struct io_kiocb *req
case AF_INET6: {
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ss;
- if (conn->addr_len < (int)sizeof(*sin6))
+ /* The IPv6 stack accepts SIN6_LEN_RFC2133; port and addr are in it. */
+ if (conn->addr_len < SIN6_LEN_RFC2133)
break;
bctx->connect.port = sin6->sin6_port;
memcpy(bctx->connect.v6_addr, &sin6->sin6_addr,
diff --git a/io_uring/register.c b/io_uring/register.c
index 02bc103bcc9d..dab3eb4258df 100644
--- a/io_uring/register.c
+++ b/io_uring/register.c
@@ -480,8 +480,9 @@ struct io_ring_ctx_rings {
static void io_register_free_rings(struct io_ring_ctx *ctx,
struct io_ring_ctx_rings *r)
{
- io_free_region(ctx->user, &r->sq_region);
- io_free_region(ctx->user, &r->ring_region);
+ /* ring memory is not charged to RLIMIT_MEMLOCK, hence no user */
+ io_free_region(NULL, &r->sq_region);
+ io_free_region(NULL, &r->ring_region);
}
#define swap_old(ctx, o, n, field) \
@@ -529,7 +530,7 @@ static int io_register_resize_rings(struct io_ring_ctx *ctx, void __user *arg)
rd.user_addr = p->cq_off.user_addr;
rd.flags |= IORING_MEM_REGION_TYPE_USER;
}
- ret = io_create_region(ctx, &n.ring_region, &rd, IORING_OFF_CQ_RING);
+ ret = io_create_region(NULL, &n.ring_region, &rd, IORING_OFF_CQ_RING);
if (ret)
return ret;
@@ -559,7 +560,7 @@ static int io_register_resize_rings(struct io_ring_ctx *ctx, void __user *arg)
rd.user_addr = p->sq_off.user_addr;
rd.flags |= IORING_MEM_REGION_TYPE_USER;
}
- ret = io_create_region(ctx, &n.sq_region, &rd, IORING_OFF_SQES);
+ ret = io_create_region(NULL, &n.sq_region, &rd, IORING_OFF_SQES);
if (ret) {
io_register_free_rings(ctx, &n);
return ret;
@@ -730,7 +731,8 @@ static int io_register_mem_region(struct io_ring_ctx *ctx, void __user *uarg)
!(ctx->flags & IORING_SETUP_R_DISABLED))
return -EINVAL;
- ret = io_create_region(ctx, &region, &rd, IORING_MAP_OFF_PARAM_REGION);
+ ret = io_create_region(ctx->user, &region, &rd,
+ IORING_MAP_OFF_PARAM_REGION);
if (ret)
return ret;
if (copy_to_user(rd_uptr, &rd, sizeof(rd))) {
@@ -764,7 +766,14 @@ static int __io_uring_register(struct io_ring_ctx *ctx, unsigned opcode,
if (ctx->submitter_task && ctx->submitter_task != current)
return -EEXIST;
- if ((ctx->int_flags & IO_RING_F_REG_RESTRICTED) && !(ctx->flags & IORING_SETUP_R_DISABLED)) {
+ /*
+ * A disabled ring is exempt from its own register restrictions, so
+ * that the creator can set them up and then enable the ring. That
+ * does not apply to restrictions inherited from the task.
+ */
+ if ((ctx->int_flags & IO_RING_F_REG_RESTRICTED) &&
+ (!(ctx->flags & IORING_SETUP_R_DISABLED) ||
+ (ctx->int_flags & IO_RING_F_RESTRICT_INHERITED))) {
opcode = array_index_nospec(opcode, IORING_REGISTER_LAST);
if (!test_bit(opcode, ctx->restrictions.register_op))
return -EACCES;
diff --git a/io_uring/zcrx.c b/io_uring/zcrx.c
index dcff0b995d34..cc9df7e64a40 100644
--- a/io_uring/zcrx.c
+++ b/io_uring/zcrx.c
@@ -432,7 +432,7 @@ static int io_allocate_rbuf_ring(struct io_ring_ctx *ctx,
mmap_offset = IORING_MAP_OFF_ZCRX_REGION;
mmap_offset += (u64)id << IORING_OFF_ZCRX_SHIFT;
- ret = io_create_region(ctx, &ifq->rq_region, rd, mmap_offset);
+ ret = io_create_region(ctx->user, &ifq->rq_region, rd, mmap_offset);
if (ret < 0)
return ret;
diff --git a/security/landlock/domain.h b/security/landlock/domain.h
index caa3d19d2c43..428c1d9890db 100644
--- a/security/landlock/domain.h
+++ b/security/landlock/domain.h
@@ -242,9 +242,14 @@ struct landlock_domain {
* to properly handle merged rulesets without
* overlapping access rights. These layers are set once
* and never changed for the lifetime of the domain.
+ *
+ * Not annotated with __counted_by(num_layers): clang
+ * miscomputes the counter address for a FAM nested in
+ * an anonymous struct within a union, which trips
+ * FORTIFY:
+ * https://github.com/llvm/llvm-project/pull/228309
*/
- struct access_masks
- handled_masks[] __counted_by(num_layers);
+ struct access_masks handled_masks[];
};
};
};
diff --git a/tools/testing/selftests/filesystems/open_tree_ns/open_tree_ns_test.c b/tools/testing/selftests/filesystems/open_tree_ns/open_tree_ns_test.c
index 82f3c8c02c9a..d96b995cb1cd 100644
--- a/tools/testing/selftests/filesystems/open_tree_ns/open_tree_ns_test.c
+++ b/tools/testing/selftests/filesystems/open_tree_ns/open_tree_ns_test.c
@@ -14,6 +14,7 @@
#include <limits.h>
#include <linux/nsfs.h>
#include <sched.h>
+#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -32,6 +33,10 @@
#define OPEN_TREE_NAMESPACE (1 << 1)
#endif
+#ifndef OPEN_TREE_DROP_MNTNS_MOUNTS
+#define OPEN_TREE_DROP_MNTNS_MOUNTS (1 << 2)
+#endif
+
static int get_mnt_ns_id(int fd, uint64_t *mnt_ns_id)
{
if (ioctl(fd, NS_GET_MNTNS_ID, mnt_ns_id) < 0)
@@ -1004,4 +1009,197 @@ TEST_F(open_tree_ns_unbindable, recursive_skips_on_unbindable)
close(fd);
}
+/*
+ * Pin a mount namespace at @where, the way snapd does under /run/snapd/ns.
+ * The namespace has to be younger than ours, so a child unshares and the
+ * parent binds the child's namespace file: binding one's own namespace is
+ * refused by the very check this test is about.
+ */
+static int pin_mount_namespace(const char *where)
+{
+ int pipefd[2], status, ret = -1;
+ char path[PATH_MAX];
+ pid_t pid;
+ char c;
+
+ if (pipe(pipefd))
+ return -1;
+
+ pid = fork();
+ if (pid < 0)
+ goto out;
+ if (pid == 0) {
+ close(pipefd[0]);
+ if (unshare(CLONE_NEWNS))
+ _exit(1);
+ /* Tell the parent the namespace exists, then hold it open. */
+ if (write(pipefd[1], "x", 1) != 1)
+ _exit(1);
+ pause();
+ _exit(0);
+ }
+
+ close(pipefd[1]);
+ pipefd[1] = -1;
+ if (read(pipefd[0], &c, 1) != 1)
+ goto out_kill;
+
+ snprintf(path, sizeof(path), "/proc/%d/ns/mnt", pid);
+ if (mount(path, where, NULL, MS_BIND, NULL))
+ goto out_kill;
+
+ ret = 0;
+
+out_kill:
+ kill(pid, SIGKILL);
+ waitpid(pid, &status, 0);
+out:
+ close(pipefd[0]);
+ if (pipefd[1] >= 0)
+ close(pipefd[1]);
+ return ret;
+}
+
+FIXTURE(open_tree_ns_drop_mntns)
+{
+ char dir[64];
+ char pin[PATH_MAX];
+ bool mounted;
+};
+
+FIXTURE_SETUP(open_tree_ns_drop_mntns)
+{
+ int fd, ret;
+
+ self->mounted = false;
+ snprintf(self->dir, sizeof(self->dir), "/tmp/open_tree_ns_drop_mntns.XXXXXX");
+
+ ret = sys_open_tree(-1, NULL, 0);
+ if (ret == -1 && errno == ENOSYS)
+ SKIP(return, "open_tree() syscall not supported");
+
+ /*
+ * Work in a private mount namespace, so whatever is mounted here,
+ * the pinned namespace included, goes away with the test process.
+ */
+ if (unshare(CLONE_NEWNS))
+ SKIP(return, "unshare(CLONE_NEWNS) failed: %s", strerror(errno));
+ ASSERT_EQ(mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL), 0);
+
+ ASSERT_NE(mkdtemp(self->dir), NULL);
+ if (mount("tmpfs", self->dir, "tmpfs", 0, NULL)) {
+ rmdir(self->dir);
+ SKIP(return, "Failed to mount tmpfs");
+ }
+ self->mounted = true;
+
+ fd = sys_open_tree(AT_FDCWD, self->dir,
+ OPEN_TREE_CLONE | OPEN_TREE_DROP_MNTNS_MOUNTS |
+ AT_RECURSIVE | OPEN_TREE_CLOEXEC);
+ if (fd < 0 && errno == EINVAL) {
+ umount2(self->dir, MNT_DETACH);
+ rmdir(self->dir);
+ SKIP(return, "OPEN_TREE_DROP_MNTNS_MOUNTS not supported");
+ }
+ ASSERT_GE(fd, 0);
+ close(fd);
+
+ snprintf(self->pin, sizeof(self->pin), "%s/ns", self->dir);
+ fd = open(self->pin, O_CREAT | O_RDONLY | O_CLOEXEC, 0600);
+ ASSERT_GE(fd, 0);
+ close(fd);
+ ASSERT_EQ(pin_mount_namespace(self->pin), 0);
+}
+
+FIXTURE_TEARDOWN(open_tree_ns_drop_mntns)
+{
+ if (self->mounted)
+ umount2(self->dir, MNT_DETACH);
+ rmdir(self->dir);
+}
+
+/* Attach @clone_fd at "/" inside the namespace @ns_fd; returns errno. */
+static int move_into_namespace(int clone_fd, int ns_fd)
+{
+ pid_t pid;
+ int status;
+
+ pid = fork();
+ if (pid < 0)
+ return -1;
+ if (pid == 0) {
+ int target;
+
+ if (setns(ns_fd, CLONE_NEWNS))
+ _exit(255);
+ target = open("/", O_PATH | O_DIRECTORY | O_CLOEXEC);
+ if (target < 0)
+ _exit(255);
+ if (sys_move_mount(clone_fd, "", target, "",
+ MOVE_MOUNT_F_EMPTY_PATH | MOVE_MOUNT_T_EMPTY_PATH))
+ _exit(errno);
+ _exit(0);
+ }
+ if (waitpid(pid, &status, 0) != pid || !WIFEXITED(status))
+ return -1;
+ return WEXITSTATUS(status);
+}
+
+TEST_F(open_tree_ns_drop_mntns, move_into_younger_namespace)
+{
+ int clone_fd, ns_fd;
+
+ ns_fd = sys_open_tree(AT_FDCWD, self->dir,
+ OPEN_TREE_NAMESPACE | OPEN_TREE_CLOEXEC);
+ ASSERT_GE(ns_fd, 0);
+
+ /*
+ * Without the flag the clone carries the pinned namespace along,
+ * and move_mount() refuses it from inside a younger namespace.
+ */
+ clone_fd = sys_open_tree(AT_FDCWD, self->dir,
+ OPEN_TREE_CLONE | AT_RECURSIVE | OPEN_TREE_CLOEXEC);
+ ASSERT_GE(clone_fd, 0);
+ EXPECT_EQ(move_into_namespace(clone_fd, ns_fd), ELOOP);
+ close(clone_fd);
+
+ /* With the flag the pinned namespace is left out. */
+ clone_fd = sys_open_tree(AT_FDCWD, self->dir,
+ OPEN_TREE_CLONE | OPEN_TREE_DROP_MNTNS_MOUNTS |
+ AT_RECURSIVE | OPEN_TREE_CLOEXEC);
+ ASSERT_GE(clone_fd, 0);
+ EXPECT_EQ(move_into_namespace(clone_fd, ns_fd), 0);
+ close(clone_fd);
+
+ close(ns_fd);
+}
+
+TEST_F(open_tree_ns_drop_mntns, flag_combinations)
+{
+ int fd;
+
+ /* Nothing is copied, so there is nothing to drop. */
+ EXPECT_LT(sys_open_tree(AT_FDCWD, self->dir,
+ OPEN_TREE_DROP_MNTNS_MOUNTS | OPEN_TREE_CLOEXEC), 0);
+ EXPECT_EQ(errno, EINVAL);
+
+ /* A non-recursive clone has no submounts, so there is nothing to drop. */
+ EXPECT_LT(sys_open_tree(AT_FDCWD, self->dir,
+ OPEN_TREE_CLONE | OPEN_TREE_DROP_MNTNS_MOUNTS |
+ OPEN_TREE_CLOEXEC), 0);
+ EXPECT_EQ(errno, EINVAL);
+ EXPECT_LT(sys_open_tree(AT_FDCWD, self->dir,
+ OPEN_TREE_NAMESPACE | OPEN_TREE_DROP_MNTNS_MOUNTS |
+ OPEN_TREE_CLOEXEC), 0);
+ EXPECT_EQ(errno, EINVAL);
+
+ /* OPEN_TREE_NAMESPACE never copies them; the flag is a no-op there. */
+ fd = sys_open_tree(AT_FDCWD, self->dir,
+ OPEN_TREE_NAMESPACE | OPEN_TREE_DROP_MNTNS_MOUNTS |
+ AT_RECURSIVE | OPEN_TREE_CLOEXEC);
+ EXPECT_GE(fd, 0);
+ if (fd >= 0)
+ close(fd);
+}
+
TEST_HARNESS_MAIN