mirror of
https://github.com/Fishwaldo/build.git
synced 2025-03-28 17:51:40 +00:00
4957 lines
146 KiB
Diff
4957 lines
146 KiB
Diff
diff --git a/Makefile b/Makefile
|
|
index ace4a655548a..ac52ee65685b 100644
|
|
--- a/Makefile
|
|
+++ b/Makefile
|
|
@@ -1,6 +1,6 @@
|
|
VERSION = 4
|
|
PATCHLEVEL = 4
|
|
-SUBLEVEL = 132
|
|
+SUBLEVEL = 133
|
|
EXTRAVERSION =
|
|
NAME = Blurry Fish Butt
|
|
|
|
diff --git a/arch/alpha/include/asm/futex.h b/arch/alpha/include/asm/futex.h
|
|
index f939794363ac..56474690e685 100644
|
|
--- a/arch/alpha/include/asm/futex.h
|
|
+++ b/arch/alpha/include/asm/futex.h
|
|
@@ -29,18 +29,10 @@
|
|
: "r" (uaddr), "r"(oparg) \
|
|
: "memory")
|
|
|
|
-static inline int futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
|
|
+static inline int arch_futex_atomic_op_inuser(int op, int oparg, int *oval,
|
|
+ u32 __user *uaddr)
|
|
{
|
|
- int op = (encoded_op >> 28) & 7;
|
|
- int cmp = (encoded_op >> 24) & 15;
|
|
- int oparg = (encoded_op << 8) >> 20;
|
|
- int cmparg = (encoded_op << 20) >> 20;
|
|
int oldval = 0, ret;
|
|
- if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
|
|
- oparg = 1 << oparg;
|
|
-
|
|
- if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
|
|
- return -EFAULT;
|
|
|
|
pagefault_disable();
|
|
|
|
@@ -66,17 +58,9 @@ static inline int futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
|
|
|
|
pagefault_enable();
|
|
|
|
- if (!ret) {
|
|
- switch (cmp) {
|
|
- case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break;
|
|
- case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break;
|
|
- case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break;
|
|
- case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break;
|
|
- case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break;
|
|
- case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break;
|
|
- default: ret = -ENOSYS;
|
|
- }
|
|
- }
|
|
+ if (!ret)
|
|
+ *oval = oldval;
|
|
+
|
|
return ret;
|
|
}
|
|
|
|
diff --git a/arch/arc/include/asm/futex.h b/arch/arc/include/asm/futex.h
|
|
index 11e1b1f3acda..eb887dd13e74 100644
|
|
--- a/arch/arc/include/asm/futex.h
|
|
+++ b/arch/arc/include/asm/futex.h
|
|
@@ -73,20 +73,11 @@
|
|
|
|
#endif
|
|
|
|
-static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
|
|
+static inline int arch_futex_atomic_op_inuser(int op, int oparg, int *oval,
|
|
+ u32 __user *uaddr)
|
|
{
|
|
- int op = (encoded_op >> 28) & 7;
|
|
- int cmp = (encoded_op >> 24) & 15;
|
|
- int oparg = (encoded_op << 8) >> 20;
|
|
- int cmparg = (encoded_op << 20) >> 20;
|
|
int oldval = 0, ret;
|
|
|
|
- if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
|
|
- oparg = 1 << oparg;
|
|
-
|
|
- if (!access_ok(VERIFY_WRITE, uaddr, sizeof(int)))
|
|
- return -EFAULT;
|
|
-
|
|
#ifndef CONFIG_ARC_HAS_LLSC
|
|
preempt_disable(); /* to guarantee atomic r-m-w of futex op */
|
|
#endif
|
|
@@ -118,30 +109,9 @@ static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
|
|
preempt_enable();
|
|
#endif
|
|
|
|
- if (!ret) {
|
|
- switch (cmp) {
|
|
- case FUTEX_OP_CMP_EQ:
|
|
- ret = (oldval == cmparg);
|
|
- break;
|
|
- case FUTEX_OP_CMP_NE:
|
|
- ret = (oldval != cmparg);
|
|
- break;
|
|
- case FUTEX_OP_CMP_LT:
|
|
- ret = (oldval < cmparg);
|
|
- break;
|
|
- case FUTEX_OP_CMP_GE:
|
|
- ret = (oldval >= cmparg);
|
|
- break;
|
|
- case FUTEX_OP_CMP_LE:
|
|
- ret = (oldval <= cmparg);
|
|
- break;
|
|
- case FUTEX_OP_CMP_GT:
|
|
- ret = (oldval > cmparg);
|
|
- break;
|
|
- default:
|
|
- ret = -ENOSYS;
|
|
- }
|
|
- }
|
|
+ if (!ret)
|
|
+ *oval = oldval;
|
|
+
|
|
return ret;
|
|
}
|
|
|
|
diff --git a/arch/arm/boot/dts/imx6qdl-wandboard.dtsi b/arch/arm/boot/dts/imx6qdl-wandboard.dtsi
|
|
index 7a032dd84bb2..9e096d811bed 100644
|
|
--- a/arch/arm/boot/dts/imx6qdl-wandboard.dtsi
|
|
+++ b/arch/arm/boot/dts/imx6qdl-wandboard.dtsi
|
|
@@ -88,7 +88,6 @@
|
|
clocks = <&clks 201>;
|
|
VDDA-supply = <®_2p5v>;
|
|
VDDIO-supply = <®_3p3v>;
|
|
- lrclk-strength = <3>;
|
|
};
|
|
};
|
|
|
|
diff --git a/arch/arm/include/asm/assembler.h b/arch/arm/include/asm/assembler.h
|
|
index 2c16d9e7c03c..4a275fba6059 100644
|
|
--- a/arch/arm/include/asm/assembler.h
|
|
+++ b/arch/arm/include/asm/assembler.h
|
|
@@ -530,4 +530,14 @@ THUMB( orr \reg , \reg , #PSR_T_BIT )
|
|
#endif
|
|
.endm
|
|
|
|
+#ifdef CONFIG_KPROBES
|
|
+#define _ASM_NOKPROBE(entry) \
|
|
+ .pushsection "_kprobe_blacklist", "aw" ; \
|
|
+ .balign 4 ; \
|
|
+ .long entry; \
|
|
+ .popsection
|
|
+#else
|
|
+#define _ASM_NOKPROBE(entry)
|
|
+#endif
|
|
+
|
|
#endif /* __ASM_ASSEMBLER_H__ */
|
|
diff --git a/arch/arm/include/asm/futex.h b/arch/arm/include/asm/futex.h
|
|
index 6795368ad023..cc414382dab4 100644
|
|
--- a/arch/arm/include/asm/futex.h
|
|
+++ b/arch/arm/include/asm/futex.h
|
|
@@ -128,20 +128,10 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
|
|
#endif /* !SMP */
|
|
|
|
static inline int
|
|
-futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
|
|
+arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr)
|
|
{
|
|
- int op = (encoded_op >> 28) & 7;
|
|
- int cmp = (encoded_op >> 24) & 15;
|
|
- int oparg = (encoded_op << 8) >> 20;
|
|
- int cmparg = (encoded_op << 20) >> 20;
|
|
int oldval = 0, ret, tmp;
|
|
|
|
- if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
|
|
- oparg = 1 << oparg;
|
|
-
|
|
- if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
|
|
- return -EFAULT;
|
|
-
|
|
#ifndef CONFIG_SMP
|
|
preempt_disable();
|
|
#endif
|
|
@@ -172,17 +162,9 @@ futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
|
|
preempt_enable();
|
|
#endif
|
|
|
|
- if (!ret) {
|
|
- switch (cmp) {
|
|
- case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break;
|
|
- case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break;
|
|
- case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break;
|
|
- case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break;
|
|
- case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break;
|
|
- case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break;
|
|
- default: ret = -ENOSYS;
|
|
- }
|
|
- }
|
|
+ if (!ret)
|
|
+ *oval = oldval;
|
|
+
|
|
return ret;
|
|
}
|
|
|
|
diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c
|
|
index c92b535150a0..306a2a581785 100644
|
|
--- a/arch/arm/kernel/traps.c
|
|
+++ b/arch/arm/kernel/traps.c
|
|
@@ -19,6 +19,7 @@
|
|
#include <linux/uaccess.h>
|
|
#include <linux/hardirq.h>
|
|
#include <linux/kdebug.h>
|
|
+#include <linux/kprobes.h>
|
|
#include <linux/module.h>
|
|
#include <linux/kexec.h>
|
|
#include <linux/bug.h>
|
|
@@ -395,7 +396,8 @@ void unregister_undef_hook(struct undef_hook *hook)
|
|
raw_spin_unlock_irqrestore(&undef_lock, flags);
|
|
}
|
|
|
|
-static int call_undef_hook(struct pt_regs *regs, unsigned int instr)
|
|
+static nokprobe_inline
|
|
+int call_undef_hook(struct pt_regs *regs, unsigned int instr)
|
|
{
|
|
struct undef_hook *hook;
|
|
unsigned long flags;
|
|
@@ -468,6 +470,7 @@ die_sig:
|
|
|
|
arm_notify_die("Oops - undefined instruction", regs, &info, 0, 6);
|
|
}
|
|
+NOKPROBE_SYMBOL(do_undefinstr)
|
|
|
|
/*
|
|
* Handle FIQ similarly to NMI on x86 systems.
|
|
diff --git a/arch/arm/lib/getuser.S b/arch/arm/lib/getuser.S
|
|
index df73914e81c8..746e7801dcdf 100644
|
|
--- a/arch/arm/lib/getuser.S
|
|
+++ b/arch/arm/lib/getuser.S
|
|
@@ -38,6 +38,7 @@ ENTRY(__get_user_1)
|
|
mov r0, #0
|
|
ret lr
|
|
ENDPROC(__get_user_1)
|
|
+_ASM_NOKPROBE(__get_user_1)
|
|
|
|
ENTRY(__get_user_2)
|
|
check_uaccess r0, 2, r1, r2, __get_user_bad
|
|
@@ -58,6 +59,7 @@ rb .req r0
|
|
mov r0, #0
|
|
ret lr
|
|
ENDPROC(__get_user_2)
|
|
+_ASM_NOKPROBE(__get_user_2)
|
|
|
|
ENTRY(__get_user_4)
|
|
check_uaccess r0, 4, r1, r2, __get_user_bad
|
|
@@ -65,6 +67,7 @@ ENTRY(__get_user_4)
|
|
mov r0, #0
|
|
ret lr
|
|
ENDPROC(__get_user_4)
|
|
+_ASM_NOKPROBE(__get_user_4)
|
|
|
|
ENTRY(__get_user_8)
|
|
check_uaccess r0, 8, r1, r2, __get_user_bad8
|
|
@@ -78,6 +81,7 @@ ENTRY(__get_user_8)
|
|
mov r0, #0
|
|
ret lr
|
|
ENDPROC(__get_user_8)
|
|
+_ASM_NOKPROBE(__get_user_8)
|
|
|
|
#ifdef __ARMEB__
|
|
ENTRY(__get_user_32t_8)
|
|
@@ -91,6 +95,7 @@ ENTRY(__get_user_32t_8)
|
|
mov r0, #0
|
|
ret lr
|
|
ENDPROC(__get_user_32t_8)
|
|
+_ASM_NOKPROBE(__get_user_32t_8)
|
|
|
|
ENTRY(__get_user_64t_1)
|
|
check_uaccess r0, 1, r1, r2, __get_user_bad8
|
|
@@ -98,6 +103,7 @@ ENTRY(__get_user_64t_1)
|
|
mov r0, #0
|
|
ret lr
|
|
ENDPROC(__get_user_64t_1)
|
|
+_ASM_NOKPROBE(__get_user_64t_1)
|
|
|
|
ENTRY(__get_user_64t_2)
|
|
check_uaccess r0, 2, r1, r2, __get_user_bad8
|
|
@@ -114,6 +120,7 @@ rb .req r0
|
|
mov r0, #0
|
|
ret lr
|
|
ENDPROC(__get_user_64t_2)
|
|
+_ASM_NOKPROBE(__get_user_64t_2)
|
|
|
|
ENTRY(__get_user_64t_4)
|
|
check_uaccess r0, 4, r1, r2, __get_user_bad8
|
|
@@ -121,6 +128,7 @@ ENTRY(__get_user_64t_4)
|
|
mov r0, #0
|
|
ret lr
|
|
ENDPROC(__get_user_64t_4)
|
|
+_ASM_NOKPROBE(__get_user_64t_4)
|
|
#endif
|
|
|
|
__get_user_bad8:
|
|
@@ -131,6 +139,8 @@ __get_user_bad:
|
|
ret lr
|
|
ENDPROC(__get_user_bad)
|
|
ENDPROC(__get_user_bad8)
|
|
+_ASM_NOKPROBE(__get_user_bad)
|
|
+_ASM_NOKPROBE(__get_user_bad8)
|
|
|
|
.pushsection __ex_table, "a"
|
|
.long 1b, __get_user_bad
|
|
diff --git a/arch/arm/probes/kprobes/opt-arm.c b/arch/arm/probes/kprobes/opt-arm.c
|
|
index bcdecc25461b..b2aa9b32bff2 100644
|
|
--- a/arch/arm/probes/kprobes/opt-arm.c
|
|
+++ b/arch/arm/probes/kprobes/opt-arm.c
|
|
@@ -165,13 +165,14 @@ optimized_callback(struct optimized_kprobe *op, struct pt_regs *regs)
|
|
{
|
|
unsigned long flags;
|
|
struct kprobe *p = &op->kp;
|
|
- struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
|
|
+ struct kprobe_ctlblk *kcb;
|
|
|
|
/* Save skipped registers */
|
|
regs->ARM_pc = (unsigned long)op->kp.addr;
|
|
regs->ARM_ORIG_r0 = ~0UL;
|
|
|
|
local_irq_save(flags);
|
|
+ kcb = get_kprobe_ctlblk();
|
|
|
|
if (kprobe_running()) {
|
|
kprobes_inc_nmissed_count(&op->kp);
|
|
@@ -191,6 +192,7 @@ optimized_callback(struct optimized_kprobe *op, struct pt_regs *regs)
|
|
|
|
local_irq_restore(flags);
|
|
}
|
|
+NOKPROBE_SYMBOL(optimized_callback)
|
|
|
|
int arch_prepare_optimized_kprobe(struct optimized_kprobe *op, struct kprobe *orig)
|
|
{
|
|
diff --git a/arch/frv/include/asm/futex.h b/arch/frv/include/asm/futex.h
|
|
index 4bea27f50a7a..2702bd802d44 100644
|
|
--- a/arch/frv/include/asm/futex.h
|
|
+++ b/arch/frv/include/asm/futex.h
|
|
@@ -7,7 +7,8 @@
|
|
#include <asm/errno.h>
|
|
#include <asm/uaccess.h>
|
|
|
|
-extern int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr);
|
|
+extern int arch_futex_atomic_op_inuser(int op, int oparg, int *oval,
|
|
+ u32 __user *uaddr);
|
|
|
|
static inline int
|
|
futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
|
|
diff --git a/arch/frv/kernel/futex.c b/arch/frv/kernel/futex.c
|
|
index d155ca9e5098..37f7b2bf7f73 100644
|
|
--- a/arch/frv/kernel/futex.c
|
|
+++ b/arch/frv/kernel/futex.c
|
|
@@ -186,20 +186,10 @@ static inline int atomic_futex_op_xchg_xor(int oparg, u32 __user *uaddr, int *_o
|
|
/*
|
|
* do the futex operations
|
|
*/
|
|
-int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
|
|
+int arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr)
|
|
{
|
|
- int op = (encoded_op >> 28) & 7;
|
|
- int cmp = (encoded_op >> 24) & 15;
|
|
- int oparg = (encoded_op << 8) >> 20;
|
|
- int cmparg = (encoded_op << 20) >> 20;
|
|
int oldval = 0, ret;
|
|
|
|
- if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
|
|
- oparg = 1 << oparg;
|
|
-
|
|
- if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
|
|
- return -EFAULT;
|
|
-
|
|
pagefault_disable();
|
|
|
|
switch (op) {
|
|
@@ -225,18 +215,9 @@ int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
|
|
|
|
pagefault_enable();
|
|
|
|
- if (!ret) {
|
|
- switch (cmp) {
|
|
- case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break;
|
|
- case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break;
|
|
- case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break;
|
|
- case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break;
|
|
- case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break;
|
|
- case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break;
|
|
- default: ret = -ENOSYS; break;
|
|
- }
|
|
- }
|
|
+ if (!ret)
|
|
+ *oval = oldval;
|
|
|
|
return ret;
|
|
|
|
-} /* end futex_atomic_op_inuser() */
|
|
+} /* end arch_futex_atomic_op_inuser() */
|
|
diff --git a/arch/hexagon/include/asm/futex.h b/arch/hexagon/include/asm/futex.h
|
|
index 7e597f8434da..c607b77c8215 100644
|
|
--- a/arch/hexagon/include/asm/futex.h
|
|
+++ b/arch/hexagon/include/asm/futex.h
|
|
@@ -31,18 +31,9 @@
|
|
|
|
|
|
static inline int
|
|
-futex_atomic_op_inuser(int encoded_op, int __user *uaddr)
|
|
+arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr)
|
|
{
|
|
- int op = (encoded_op >> 28) & 7;
|
|
- int cmp = (encoded_op >> 24) & 15;
|
|
- int oparg = (encoded_op << 8) >> 20;
|
|
- int cmparg = (encoded_op << 20) >> 20;
|
|
int oldval = 0, ret;
|
|
- if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
|
|
- oparg = 1 << oparg;
|
|
-
|
|
- if (!access_ok(VERIFY_WRITE, uaddr, sizeof(int)))
|
|
- return -EFAULT;
|
|
|
|
pagefault_disable();
|
|
|
|
@@ -72,30 +63,9 @@ futex_atomic_op_inuser(int encoded_op, int __user *uaddr)
|
|
|
|
pagefault_enable();
|
|
|
|
- if (!ret) {
|
|
- switch (cmp) {
|
|
- case FUTEX_OP_CMP_EQ:
|
|
- ret = (oldval == cmparg);
|
|
- break;
|
|
- case FUTEX_OP_CMP_NE:
|
|
- ret = (oldval != cmparg);
|
|
- break;
|
|
- case FUTEX_OP_CMP_LT:
|
|
- ret = (oldval < cmparg);
|
|
- break;
|
|
- case FUTEX_OP_CMP_GE:
|
|
- ret = (oldval >= cmparg);
|
|
- break;
|
|
- case FUTEX_OP_CMP_LE:
|
|
- ret = (oldval <= cmparg);
|
|
- break;
|
|
- case FUTEX_OP_CMP_GT:
|
|
- ret = (oldval > cmparg);
|
|
- break;
|
|
- default:
|
|
- ret = -ENOSYS;
|
|
- }
|
|
- }
|
|
+ if (!ret)
|
|
+ *oval = oldval;
|
|
+
|
|
return ret;
|
|
}
|
|
|
|
diff --git a/arch/ia64/include/asm/futex.h b/arch/ia64/include/asm/futex.h
|
|
index 76acbcd5c060..6d67dc1eaf2b 100644
|
|
--- a/arch/ia64/include/asm/futex.h
|
|
+++ b/arch/ia64/include/asm/futex.h
|
|
@@ -45,18 +45,9 @@ do { \
|
|
} while (0)
|
|
|
|
static inline int
|
|
-futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
|
|
+arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr)
|
|
{
|
|
- int op = (encoded_op >> 28) & 7;
|
|
- int cmp = (encoded_op >> 24) & 15;
|
|
- int oparg = (encoded_op << 8) >> 20;
|
|
- int cmparg = (encoded_op << 20) >> 20;
|
|
int oldval = 0, ret;
|
|
- if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
|
|
- oparg = 1 << oparg;
|
|
-
|
|
- if (! access_ok (VERIFY_WRITE, uaddr, sizeof(u32)))
|
|
- return -EFAULT;
|
|
|
|
pagefault_disable();
|
|
|
|
@@ -84,17 +75,9 @@ futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
|
|
|
|
pagefault_enable();
|
|
|
|
- if (!ret) {
|
|
- switch (cmp) {
|
|
- case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break;
|
|
- case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break;
|
|
- case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break;
|
|
- case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break;
|
|
- case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break;
|
|
- case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break;
|
|
- default: ret = -ENOSYS;
|
|
- }
|
|
- }
|
|
+ if (!ret)
|
|
+ *oval = oldval;
|
|
+
|
|
return ret;
|
|
}
|
|
|
|
diff --git a/arch/microblaze/include/asm/futex.h b/arch/microblaze/include/asm/futex.h
|
|
index 01848f056f43..a9dad9e5e132 100644
|
|
--- a/arch/microblaze/include/asm/futex.h
|
|
+++ b/arch/microblaze/include/asm/futex.h
|
|
@@ -29,18 +29,9 @@
|
|
})
|
|
|
|
static inline int
|
|
-futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
|
|
+arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr)
|
|
{
|
|
- int op = (encoded_op >> 28) & 7;
|
|
- int cmp = (encoded_op >> 24) & 15;
|
|
- int oparg = (encoded_op << 8) >> 20;
|
|
- int cmparg = (encoded_op << 20) >> 20;
|
|
int oldval = 0, ret;
|
|
- if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
|
|
- oparg = 1 << oparg;
|
|
-
|
|
- if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
|
|
- return -EFAULT;
|
|
|
|
pagefault_disable();
|
|
|
|
@@ -66,30 +57,9 @@ futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
|
|
|
|
pagefault_enable();
|
|
|
|
- if (!ret) {
|
|
- switch (cmp) {
|
|
- case FUTEX_OP_CMP_EQ:
|
|
- ret = (oldval == cmparg);
|
|
- break;
|
|
- case FUTEX_OP_CMP_NE:
|
|
- ret = (oldval != cmparg);
|
|
- break;
|
|
- case FUTEX_OP_CMP_LT:
|
|
- ret = (oldval < cmparg);
|
|
- break;
|
|
- case FUTEX_OP_CMP_GE:
|
|
- ret = (oldval >= cmparg);
|
|
- break;
|
|
- case FUTEX_OP_CMP_LE:
|
|
- ret = (oldval <= cmparg);
|
|
- break;
|
|
- case FUTEX_OP_CMP_GT:
|
|
- ret = (oldval > cmparg);
|
|
- break;
|
|
- default:
|
|
- ret = -ENOSYS;
|
|
- }
|
|
- }
|
|
+ if (!ret)
|
|
+ *oval = oldval;
|
|
+
|
|
return ret;
|
|
}
|
|
|
|
diff --git a/arch/mips/include/asm/futex.h b/arch/mips/include/asm/futex.h
|
|
index 1de190bdfb9c..a9e61ea54ca9 100644
|
|
--- a/arch/mips/include/asm/futex.h
|
|
+++ b/arch/mips/include/asm/futex.h
|
|
@@ -83,18 +83,9 @@
|
|
}
|
|
|
|
static inline int
|
|
-futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
|
|
+arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr)
|
|
{
|
|
- int op = (encoded_op >> 28) & 7;
|
|
- int cmp = (encoded_op >> 24) & 15;
|
|
- int oparg = (encoded_op << 8) >> 20;
|
|
- int cmparg = (encoded_op << 20) >> 20;
|
|
int oldval = 0, ret;
|
|
- if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
|
|
- oparg = 1 << oparg;
|
|
-
|
|
- if (! access_ok (VERIFY_WRITE, uaddr, sizeof(u32)))
|
|
- return -EFAULT;
|
|
|
|
pagefault_disable();
|
|
|
|
@@ -125,17 +116,9 @@ futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
|
|
|
|
pagefault_enable();
|
|
|
|
- if (!ret) {
|
|
- switch (cmp) {
|
|
- case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break;
|
|
- case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break;
|
|
- case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break;
|
|
- case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break;
|
|
- case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break;
|
|
- case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break;
|
|
- default: ret = -ENOSYS;
|
|
- }
|
|
- }
|
|
+ if (!ret)
|
|
+ *oval = oldval;
|
|
+
|
|
return ret;
|
|
}
|
|
|
|
diff --git a/arch/parisc/include/asm/futex.h b/arch/parisc/include/asm/futex.h
|
|
index 49df14805a9b..ae5b64981d72 100644
|
|
--- a/arch/parisc/include/asm/futex.h
|
|
+++ b/arch/parisc/include/asm/futex.h
|
|
@@ -32,20 +32,11 @@ _futex_spin_unlock_irqrestore(u32 __user *uaddr, unsigned long int *flags)
|
|
}
|
|
|
|
static inline int
|
|
-futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
|
|
+arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr)
|
|
{
|
|
unsigned long int flags;
|
|
u32 val;
|
|
- int op = (encoded_op >> 28) & 7;
|
|
- int cmp = (encoded_op >> 24) & 15;
|
|
- int oparg = (encoded_op << 8) >> 20;
|
|
- int cmparg = (encoded_op << 20) >> 20;
|
|
int oldval = 0, ret;
|
|
- if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
|
|
- oparg = 1 << oparg;
|
|
-
|
|
- if (!access_ok(VERIFY_WRITE, uaddr, sizeof(*uaddr)))
|
|
- return -EFAULT;
|
|
|
|
pagefault_disable();
|
|
|
|
@@ -98,17 +89,9 @@ futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
|
|
|
|
pagefault_enable();
|
|
|
|
- if (!ret) {
|
|
- switch (cmp) {
|
|
- case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break;
|
|
- case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break;
|
|
- case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break;
|
|
- case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break;
|
|
- case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break;
|
|
- case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break;
|
|
- default: ret = -ENOSYS;
|
|
- }
|
|
- }
|
|
+ if (!ret)
|
|
+ *oval = oldval;
|
|
+
|
|
return ret;
|
|
}
|
|
|
|
diff --git a/arch/powerpc/include/asm/firmware.h b/arch/powerpc/include/asm/firmware.h
|
|
index e05808a328db..b0629249778b 100644
|
|
--- a/arch/powerpc/include/asm/firmware.h
|
|
+++ b/arch/powerpc/include/asm/firmware.h
|
|
@@ -47,12 +47,10 @@
|
|
#define FW_FEATURE_VPHN ASM_CONST(0x0000000004000000)
|
|
#define FW_FEATURE_XCMO ASM_CONST(0x0000000008000000)
|
|
#define FW_FEATURE_OPAL ASM_CONST(0x0000000010000000)
|
|
-#define FW_FEATURE_OPALv2 ASM_CONST(0x0000000020000000)
|
|
#define FW_FEATURE_SET_MODE ASM_CONST(0x0000000040000000)
|
|
#define FW_FEATURE_BEST_ENERGY ASM_CONST(0x0000000080000000)
|
|
#define FW_FEATURE_TYPE1_AFFINITY ASM_CONST(0x0000000100000000)
|
|
#define FW_FEATURE_PRRN ASM_CONST(0x0000000200000000)
|
|
-#define FW_FEATURE_OPALv3 ASM_CONST(0x0000000400000000)
|
|
|
|
#ifndef __ASSEMBLY__
|
|
|
|
@@ -70,8 +68,7 @@ enum {
|
|
FW_FEATURE_SET_MODE | FW_FEATURE_BEST_ENERGY |
|
|
FW_FEATURE_TYPE1_AFFINITY | FW_FEATURE_PRRN,
|
|
FW_FEATURE_PSERIES_ALWAYS = 0,
|
|
- FW_FEATURE_POWERNV_POSSIBLE = FW_FEATURE_OPAL | FW_FEATURE_OPALv2 |
|
|
- FW_FEATURE_OPALv3,
|
|
+ FW_FEATURE_POWERNV_POSSIBLE = FW_FEATURE_OPAL,
|
|
FW_FEATURE_POWERNV_ALWAYS = 0,
|
|
FW_FEATURE_PS3_POSSIBLE = FW_FEATURE_LPAR | FW_FEATURE_PS3_LV1,
|
|
FW_FEATURE_PS3_ALWAYS = FW_FEATURE_LPAR | FW_FEATURE_PS3_LV1,
|
|
diff --git a/arch/powerpc/include/asm/futex.h b/arch/powerpc/include/asm/futex.h
|
|
index 2a9cf845473b..f4c7467f7465 100644
|
|
--- a/arch/powerpc/include/asm/futex.h
|
|
+++ b/arch/powerpc/include/asm/futex.h
|
|
@@ -31,18 +31,10 @@
|
|
: "b" (uaddr), "i" (-EFAULT), "r" (oparg) \
|
|
: "cr0", "memory")
|
|
|
|
-static inline int futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
|
|
+static inline int arch_futex_atomic_op_inuser(int op, int oparg, int *oval,
|
|
+ u32 __user *uaddr)
|
|
{
|
|
- int op = (encoded_op >> 28) & 7;
|
|
- int cmp = (encoded_op >> 24) & 15;
|
|
- int oparg = (encoded_op << 8) >> 20;
|
|
- int cmparg = (encoded_op << 20) >> 20;
|
|
int oldval = 0, ret;
|
|
- if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
|
|
- oparg = 1 << oparg;
|
|
-
|
|
- if (! access_ok (VERIFY_WRITE, uaddr, sizeof(u32)))
|
|
- return -EFAULT;
|
|
|
|
pagefault_disable();
|
|
|
|
@@ -68,17 +60,9 @@ static inline int futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
|
|
|
|
pagefault_enable();
|
|
|
|
- if (!ret) {
|
|
- switch (cmp) {
|
|
- case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break;
|
|
- case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break;
|
|
- case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break;
|
|
- case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break;
|
|
- case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break;
|
|
- case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break;
|
|
- default: ret = -ENOSYS;
|
|
- }
|
|
- }
|
|
+ if (!ret)
|
|
+ *oval = oldval;
|
|
+
|
|
return ret;
|
|
}
|
|
|
|
diff --git a/arch/powerpc/kernel/setup-common.c b/arch/powerpc/kernel/setup-common.c
|
|
index 44c8d03558ac..318224784114 100644
|
|
--- a/arch/powerpc/kernel/setup-common.c
|
|
+++ b/arch/powerpc/kernel/setup-common.c
|
|
@@ -217,14 +217,6 @@ static int show_cpuinfo(struct seq_file *m, void *v)
|
|
unsigned short maj;
|
|
unsigned short min;
|
|
|
|
- /* We only show online cpus: disable preempt (overzealous, I
|
|
- * knew) to prevent cpu going down. */
|
|
- preempt_disable();
|
|
- if (!cpu_online(cpu_id)) {
|
|
- preempt_enable();
|
|
- return 0;
|
|
- }
|
|
-
|
|
#ifdef CONFIG_SMP
|
|
pvr = per_cpu(cpu_pvr, cpu_id);
|
|
#else
|
|
@@ -329,9 +321,6 @@ static int show_cpuinfo(struct seq_file *m, void *v)
|
|
#ifdef CONFIG_SMP
|
|
seq_printf(m, "\n");
|
|
#endif
|
|
-
|
|
- preempt_enable();
|
|
-
|
|
/* If this is the last cpu, print the summary */
|
|
if (cpumask_next(cpu_id, cpu_online_mask) >= nr_cpu_ids)
|
|
show_cpuinfo_summary(m);
|
|
diff --git a/arch/powerpc/platforms/powernv/eeh-powernv.c b/arch/powerpc/platforms/powernv/eeh-powernv.c
|
|
index 92736851c795..3f653f5201e7 100644
|
|
--- a/arch/powerpc/platforms/powernv/eeh-powernv.c
|
|
+++ b/arch/powerpc/platforms/powernv/eeh-powernv.c
|
|
@@ -48,8 +48,8 @@ static int pnv_eeh_init(void)
|
|
struct pci_controller *hose;
|
|
struct pnv_phb *phb;
|
|
|
|
- if (!firmware_has_feature(FW_FEATURE_OPALv3)) {
|
|
- pr_warn("%s: OPALv3 is required !\n",
|
|
+ if (!firmware_has_feature(FW_FEATURE_OPAL)) {
|
|
+ pr_warn("%s: OPAL is required !\n",
|
|
__func__);
|
|
return -EINVAL;
|
|
}
|
|
diff --git a/arch/powerpc/platforms/powernv/idle.c b/arch/powerpc/platforms/powernv/idle.c
|
|
index 59d735d2e5c0..15bfbcd5debc 100644
|
|
--- a/arch/powerpc/platforms/powernv/idle.c
|
|
+++ b/arch/powerpc/platforms/powernv/idle.c
|
|
@@ -242,7 +242,7 @@ static int __init pnv_init_idle_states(void)
|
|
if (cpuidle_disable != IDLE_NO_OVERRIDE)
|
|
goto out;
|
|
|
|
- if (!firmware_has_feature(FW_FEATURE_OPALv3))
|
|
+ if (!firmware_has_feature(FW_FEATURE_OPAL))
|
|
goto out;
|
|
|
|
power_mgt = of_find_node_by_path("/ibm,opal/power-mgt");
|
|
diff --git a/arch/powerpc/platforms/powernv/opal-nvram.c b/arch/powerpc/platforms/powernv/opal-nvram.c
|
|
index 1bceb95f422d..5584247f5029 100644
|
|
--- a/arch/powerpc/platforms/powernv/opal-nvram.c
|
|
+++ b/arch/powerpc/platforms/powernv/opal-nvram.c
|
|
@@ -44,6 +44,10 @@ static ssize_t opal_nvram_read(char *buf, size_t count, loff_t *index)
|
|
return count;
|
|
}
|
|
|
|
+/*
|
|
+ * This can be called in the panic path with interrupts off, so use
|
|
+ * mdelay in that case.
|
|
+ */
|
|
static ssize_t opal_nvram_write(char *buf, size_t count, loff_t *index)
|
|
{
|
|
s64 rc = OPAL_BUSY;
|
|
@@ -58,10 +62,16 @@ static ssize_t opal_nvram_write(char *buf, size_t count, loff_t *index)
|
|
while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
|
|
rc = opal_write_nvram(__pa(buf), count, off);
|
|
if (rc == OPAL_BUSY_EVENT) {
|
|
- msleep(OPAL_BUSY_DELAY_MS);
|
|
+ if (in_interrupt() || irqs_disabled())
|
|
+ mdelay(OPAL_BUSY_DELAY_MS);
|
|
+ else
|
|
+ msleep(OPAL_BUSY_DELAY_MS);
|
|
opal_poll_events(NULL);
|
|
} else if (rc == OPAL_BUSY) {
|
|
- msleep(OPAL_BUSY_DELAY_MS);
|
|
+ if (in_interrupt() || irqs_disabled())
|
|
+ mdelay(OPAL_BUSY_DELAY_MS);
|
|
+ else
|
|
+ msleep(OPAL_BUSY_DELAY_MS);
|
|
}
|
|
}
|
|
|
|
diff --git a/arch/powerpc/platforms/powernv/opal-xscom.c b/arch/powerpc/platforms/powernv/opal-xscom.c
|
|
index 7634d1c62299..d0ac535cf5d7 100644
|
|
--- a/arch/powerpc/platforms/powernv/opal-xscom.c
|
|
+++ b/arch/powerpc/platforms/powernv/opal-xscom.c
|
|
@@ -126,7 +126,7 @@ static const struct scom_controller opal_scom_controller = {
|
|
|
|
static int opal_xscom_init(void)
|
|
{
|
|
- if (firmware_has_feature(FW_FEATURE_OPALv3))
|
|
+ if (firmware_has_feature(FW_FEATURE_OPAL))
|
|
scom_init(&opal_scom_controller);
|
|
return 0;
|
|
}
|
|
diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c
|
|
index ae29eaf85e9e..e48826aa314c 100644
|
|
--- a/arch/powerpc/platforms/powernv/opal.c
|
|
+++ b/arch/powerpc/platforms/powernv/opal.c
|
|
@@ -98,16 +98,11 @@ int __init early_init_dt_scan_opal(unsigned long node,
|
|
pr_debug("OPAL Entry = 0x%llx (sizep=%p runtimesz=%d)\n",
|
|
opal.size, sizep, runtimesz);
|
|
|
|
- powerpc_firmware_features |= FW_FEATURE_OPAL;
|
|
if (of_flat_dt_is_compatible(node, "ibm,opal-v3")) {
|
|
- powerpc_firmware_features |= FW_FEATURE_OPALv2;
|
|
- powerpc_firmware_features |= FW_FEATURE_OPALv3;
|
|
- pr_info("OPAL V3 detected !\n");
|
|
- } else if (of_flat_dt_is_compatible(node, "ibm,opal-v2")) {
|
|
- powerpc_firmware_features |= FW_FEATURE_OPALv2;
|
|
- pr_info("OPAL V2 detected !\n");
|
|
+ powerpc_firmware_features |= FW_FEATURE_OPAL;
|
|
+ pr_info("OPAL detected !\n");
|
|
} else {
|
|
- pr_info("OPAL V1 detected !\n");
|
|
+ panic("OPAL != V3 detected, no longer supported.\n");
|
|
}
|
|
|
|
/* Reinit all cores with the right endian */
|
|
@@ -352,17 +347,15 @@ int opal_put_chars(uint32_t vtermno, const char *data, int total_len)
|
|
* enough room and be done with it
|
|
*/
|
|
spin_lock_irqsave(&opal_write_lock, flags);
|
|
- if (firmware_has_feature(FW_FEATURE_OPALv2)) {
|
|
- rc = opal_console_write_buffer_space(vtermno, &olen);
|
|
- len = be64_to_cpu(olen);
|
|
- if (rc || len < total_len) {
|
|
- spin_unlock_irqrestore(&opal_write_lock, flags);
|
|
- /* Closed -> drop characters */
|
|
- if (rc)
|
|
- return total_len;
|
|
- opal_poll_events(NULL);
|
|
- return -EAGAIN;
|
|
- }
|
|
+ rc = opal_console_write_buffer_space(vtermno, &olen);
|
|
+ len = be64_to_cpu(olen);
|
|
+ if (rc || len < total_len) {
|
|
+ spin_unlock_irqrestore(&opal_write_lock, flags);
|
|
+ /* Closed -> drop characters */
|
|
+ if (rc)
|
|
+ return total_len;
|
|
+ opal_poll_events(NULL);
|
|
+ return -EAGAIN;
|
|
}
|
|
|
|
/* We still try to handle partial completions, though they
|
|
@@ -696,10 +689,7 @@ static int __init opal_init(void)
|
|
}
|
|
|
|
/* Register OPAL consoles if any ports */
|
|
- if (firmware_has_feature(FW_FEATURE_OPALv2))
|
|
- consoles = of_find_node_by_path("/ibm,opal/consoles");
|
|
- else
|
|
- consoles = of_node_get(opal_node);
|
|
+ consoles = of_find_node_by_path("/ibm,opal/consoles");
|
|
if (consoles) {
|
|
for_each_child_of_node(consoles, np) {
|
|
if (strcmp(np->name, "serial"))
|
|
diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c
|
|
index ecb7f3220355..eac3b7cc78c6 100644
|
|
--- a/arch/powerpc/platforms/powernv/pci-ioda.c
|
|
+++ b/arch/powerpc/platforms/powernv/pci-ioda.c
|
|
@@ -344,7 +344,7 @@ static void __init pnv_ioda_parse_m64_window(struct pnv_phb *phb)
|
|
return;
|
|
}
|
|
|
|
- if (!firmware_has_feature(FW_FEATURE_OPALv3)) {
|
|
+ if (!firmware_has_feature(FW_FEATURE_OPAL)) {
|
|
pr_info(" Firmware too old to support M64 window\n");
|
|
return;
|
|
}
|
|
diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c
|
|
index 30c6b3b7be90..c57afc619b20 100644
|
|
--- a/arch/powerpc/platforms/powernv/setup.c
|
|
+++ b/arch/powerpc/platforms/powernv/setup.c
|
|
@@ -140,12 +140,8 @@ static void pnv_show_cpuinfo(struct seq_file *m)
|
|
if (root)
|
|
model = of_get_property(root, "model", NULL);
|
|
seq_printf(m, "machine\t\t: PowerNV %s\n", model);
|
|
- if (firmware_has_feature(FW_FEATURE_OPALv3))
|
|
- seq_printf(m, "firmware\t: OPAL v3\n");
|
|
- else if (firmware_has_feature(FW_FEATURE_OPALv2))
|
|
- seq_printf(m, "firmware\t: OPAL v2\n");
|
|
- else if (firmware_has_feature(FW_FEATURE_OPAL))
|
|
- seq_printf(m, "firmware\t: OPAL v1\n");
|
|
+ if (firmware_has_feature(FW_FEATURE_OPAL))
|
|
+ seq_printf(m, "firmware\t: OPAL\n");
|
|
else
|
|
seq_printf(m, "firmware\t: BML\n");
|
|
of_node_put(root);
|
|
@@ -274,9 +270,9 @@ static void pnv_kexec_cpu_down(int crash_shutdown, int secondary)
|
|
{
|
|
xics_kexec_teardown_cpu(secondary);
|
|
|
|
- /* On OPAL v3, we return all CPUs to firmware */
|
|
+ /* On OPAL, we return all CPUs to firmware */
|
|
|
|
- if (!firmware_has_feature(FW_FEATURE_OPALv3))
|
|
+ if (!firmware_has_feature(FW_FEATURE_OPAL))
|
|
return;
|
|
|
|
if (secondary) {
|
|
diff --git a/arch/powerpc/platforms/powernv/smp.c b/arch/powerpc/platforms/powernv/smp.c
|
|
index ca264833ee64..ad7b1a3dbed0 100644
|
|
--- a/arch/powerpc/platforms/powernv/smp.c
|
|
+++ b/arch/powerpc/platforms/powernv/smp.c
|
|
@@ -61,14 +61,15 @@ static int pnv_smp_kick_cpu(int nr)
|
|
unsigned long start_here =
|
|
__pa(ppc_function_entry(generic_secondary_smp_init));
|
|
long rc;
|
|
+ uint8_t status;
|
|
|
|
BUG_ON(nr < 0 || nr >= NR_CPUS);
|
|
|
|
/*
|
|
- * If we already started or OPALv2 is not supported, we just
|
|
+ * If we already started or OPAL is not supported, we just
|
|
* kick the CPU via the PACA
|
|
*/
|
|
- if (paca[nr].cpu_start || !firmware_has_feature(FW_FEATURE_OPALv2))
|
|
+ if (paca[nr].cpu_start || !firmware_has_feature(FW_FEATURE_OPAL))
|
|
goto kick;
|
|
|
|
/*
|
|
@@ -77,55 +78,42 @@ static int pnv_smp_kick_cpu(int nr)
|
|
* first time. OPAL v3 allows us to query OPAL to know if it
|
|
* has the CPUs, so we do that
|
|
*/
|
|
- if (firmware_has_feature(FW_FEATURE_OPALv3)) {
|
|
- uint8_t status;
|
|
-
|
|
- rc = opal_query_cpu_status(pcpu, &status);
|
|
- if (rc != OPAL_SUCCESS) {
|
|
- pr_warn("OPAL Error %ld querying CPU %d state\n",
|
|
- rc, nr);
|
|
- return -ENODEV;
|
|
- }
|
|
+ rc = opal_query_cpu_status(pcpu, &status);
|
|
+ if (rc != OPAL_SUCCESS) {
|
|
+ pr_warn("OPAL Error %ld querying CPU %d state\n", rc, nr);
|
|
+ return -ENODEV;
|
|
+ }
|
|
|
|
- /*
|
|
- * Already started, just kick it, probably coming from
|
|
- * kexec and spinning
|
|
- */
|
|
- if (status == OPAL_THREAD_STARTED)
|
|
- goto kick;
|
|
+ /*
|
|
+ * Already started, just kick it, probably coming from
|
|
+ * kexec and spinning
|
|
+ */
|
|
+ if (status == OPAL_THREAD_STARTED)
|
|
+ goto kick;
|
|
|
|
- /*
|
|
- * Available/inactive, let's kick it
|
|
- */
|
|
- if (status == OPAL_THREAD_INACTIVE) {
|
|
- pr_devel("OPAL: Starting CPU %d (HW 0x%x)...\n",
|
|
- nr, pcpu);
|
|
- rc = opal_start_cpu(pcpu, start_here);
|
|
- if (rc != OPAL_SUCCESS) {
|
|
- pr_warn("OPAL Error %ld starting CPU %d\n",
|
|
- rc, nr);
|
|
- return -ENODEV;
|
|
- }
|
|
- } else {
|
|
- /*
|
|
- * An unavailable CPU (or any other unknown status)
|
|
- * shouldn't be started. It should also
|
|
- * not be in the possible map but currently it can
|
|
- * happen
|
|
- */
|
|
- pr_devel("OPAL: CPU %d (HW 0x%x) is unavailable"
|
|
- " (status %d)...\n", nr, pcpu, status);
|
|
+ /*
|
|
+ * Available/inactive, let's kick it
|
|
+ */
|
|
+ if (status == OPAL_THREAD_INACTIVE) {
|
|
+ pr_devel("OPAL: Starting CPU %d (HW 0x%x)...\n", nr, pcpu);
|
|
+ rc = opal_start_cpu(pcpu, start_here);
|
|
+ if (rc != OPAL_SUCCESS) {
|
|
+ pr_warn("OPAL Error %ld starting CPU %d\n", rc, nr);
|
|
return -ENODEV;
|
|
}
|
|
} else {
|
|
/*
|
|
- * On OPAL v2, we just kick it and hope for the best,
|
|
- * we must not test the error from opal_start_cpu() or
|
|
- * we would fail to get CPUs from kexec.
|
|
+ * An unavailable CPU (or any other unknown status)
|
|
+ * shouldn't be started. It should also
|
|
+ * not be in the possible map but currently it can
|
|
+ * happen
|
|
*/
|
|
- opal_start_cpu(pcpu, start_here);
|
|
+ pr_devel("OPAL: CPU %d (HW 0x%x) is unavailable"
|
|
+ " (status %d)...\n", nr, pcpu, status);
|
|
+ return -ENODEV;
|
|
}
|
|
- kick:
|
|
+
|
|
+kick:
|
|
return smp_generic_kick_cpu(nr);
|
|
}
|
|
|
|
diff --git a/arch/s390/include/asm/alternative-asm.h b/arch/s390/include/asm/alternative-asm.h
|
|
new file mode 100644
|
|
index 000000000000..955d620db23e
|
|
--- /dev/null
|
|
+++ b/arch/s390/include/asm/alternative-asm.h
|
|
@@ -0,0 +1,108 @@
|
|
+/* SPDX-License-Identifier: GPL-2.0 */
|
|
+#ifndef _ASM_S390_ALTERNATIVE_ASM_H
|
|
+#define _ASM_S390_ALTERNATIVE_ASM_H
|
|
+
|
|
+#ifdef __ASSEMBLY__
|
|
+
|
|
+/*
|
|
+ * Check the length of an instruction sequence. The length may not be larger
|
|
+ * than 254 bytes and it has to be divisible by 2.
|
|
+ */
|
|
+.macro alt_len_check start,end
|
|
+ .if ( \end - \start ) > 254
|
|
+ .error "cpu alternatives does not support instructions blocks > 254 bytes\n"
|
|
+ .endif
|
|
+ .if ( \end - \start ) % 2
|
|
+ .error "cpu alternatives instructions length is odd\n"
|
|
+ .endif
|
|
+.endm
|
|
+
|
|
+/*
|
|
+ * Issue one struct alt_instr descriptor entry (need to put it into
|
|
+ * the section .altinstructions, see below). This entry contains
|
|
+ * enough information for the alternatives patching code to patch an
|
|
+ * instruction. See apply_alternatives().
|
|
+ */
|
|
+.macro alt_entry orig_start, orig_end, alt_start, alt_end, feature
|
|
+ .long \orig_start - .
|
|
+ .long \alt_start - .
|
|
+ .word \feature
|
|
+ .byte \orig_end - \orig_start
|
|
+ .byte \alt_end - \alt_start
|
|
+.endm
|
|
+
|
|
+/*
|
|
+ * Fill up @bytes with nops. The macro emits 6-byte nop instructions
|
|
+ * for the bulk of the area, possibly followed by a 4-byte and/or
|
|
+ * a 2-byte nop if the size of the area is not divisible by 6.
|
|
+ */
|
|
+.macro alt_pad_fill bytes
|
|
+ .fill ( \bytes ) / 6, 6, 0xc0040000
|
|
+ .fill ( \bytes ) % 6 / 4, 4, 0x47000000
|
|
+ .fill ( \bytes ) % 6 % 4 / 2, 2, 0x0700
|
|
+.endm
|
|
+
|
|
+/*
|
|
+ * Fill up @bytes with nops. If the number of bytes is larger
|
|
+ * than 6, emit a jg instruction to branch over all nops, then
|
|
+ * fill an area of size (@bytes - 6) with nop instructions.
|
|
+ */
|
|
+.macro alt_pad bytes
|
|
+ .if ( \bytes > 0 )
|
|
+ .if ( \bytes > 6 )
|
|
+ jg . + \bytes
|
|
+ alt_pad_fill \bytes - 6
|
|
+ .else
|
|
+ alt_pad_fill \bytes
|
|
+ .endif
|
|
+ .endif
|
|
+.endm
|
|
+
|
|
+/*
|
|
+ * Define an alternative between two instructions. If @feature is
|
|
+ * present, early code in apply_alternatives() replaces @oldinstr with
|
|
+ * @newinstr. ".skip" directive takes care of proper instruction padding
|
|
+ * in case @newinstr is longer than @oldinstr.
|
|
+ */
|
|
+.macro ALTERNATIVE oldinstr, newinstr, feature
|
|
+ .pushsection .altinstr_replacement,"ax"
|
|
+770: \newinstr
|
|
+771: .popsection
|
|
+772: \oldinstr
|
|
+773: alt_len_check 770b, 771b
|
|
+ alt_len_check 772b, 773b
|
|
+ alt_pad ( ( 771b - 770b ) - ( 773b - 772b ) )
|
|
+774: .pushsection .altinstructions,"a"
|
|
+ alt_entry 772b, 774b, 770b, 771b, \feature
|
|
+ .popsection
|
|
+.endm
|
|
+
|
|
+/*
|
|
+ * Define an alternative between two instructions. If @feature is
|
|
+ * present, early code in apply_alternatives() replaces @oldinstr with
|
|
+ * @newinstr. ".skip" directive takes care of proper instruction padding
|
|
+ * in case @newinstr is longer than @oldinstr.
|
|
+ */
|
|
+.macro ALTERNATIVE_2 oldinstr, newinstr1, feature1, newinstr2, feature2
|
|
+ .pushsection .altinstr_replacement,"ax"
|
|
+770: \newinstr1
|
|
+771: \newinstr2
|
|
+772: .popsection
|
|
+773: \oldinstr
|
|
+774: alt_len_check 770b, 771b
|
|
+ alt_len_check 771b, 772b
|
|
+ alt_len_check 773b, 774b
|
|
+ .if ( 771b - 770b > 772b - 771b )
|
|
+ alt_pad ( ( 771b - 770b ) - ( 774b - 773b ) )
|
|
+ .else
|
|
+ alt_pad ( ( 772b - 771b ) - ( 774b - 773b ) )
|
|
+ .endif
|
|
+775: .pushsection .altinstructions,"a"
|
|
+ alt_entry 773b, 775b, 770b, 771b,\feature1
|
|
+ alt_entry 773b, 775b, 771b, 772b,\feature2
|
|
+ .popsection
|
|
+.endm
|
|
+
|
|
+#endif /* __ASSEMBLY__ */
|
|
+
|
|
+#endif /* _ASM_S390_ALTERNATIVE_ASM_H */
|
|
diff --git a/arch/s390/include/asm/futex.h b/arch/s390/include/asm/futex.h
|
|
index a4811aa0304d..8f8eec9e1198 100644
|
|
--- a/arch/s390/include/asm/futex.h
|
|
+++ b/arch/s390/include/asm/futex.h
|
|
@@ -21,17 +21,12 @@
|
|
: "0" (-EFAULT), "d" (oparg), "a" (uaddr), \
|
|
"m" (*uaddr) : "cc");
|
|
|
|
-static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
|
|
+static inline int arch_futex_atomic_op_inuser(int op, int oparg, int *oval,
|
|
+ u32 __user *uaddr)
|
|
{
|
|
- int op = (encoded_op >> 28) & 7;
|
|
- int cmp = (encoded_op >> 24) & 15;
|
|
- int oparg = (encoded_op << 8) >> 20;
|
|
- int cmparg = (encoded_op << 20) >> 20;
|
|
int oldval = 0, newval, ret;
|
|
|
|
load_kernel_asce();
|
|
- if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
|
|
- oparg = 1 << oparg;
|
|
|
|
pagefault_disable();
|
|
switch (op) {
|
|
@@ -60,17 +55,9 @@ static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
|
|
}
|
|
pagefault_enable();
|
|
|
|
- if (!ret) {
|
|
- switch (cmp) {
|
|
- case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break;
|
|
- case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break;
|
|
- case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break;
|
|
- case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break;
|
|
- case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break;
|
|
- case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break;
|
|
- default: ret = -ENOSYS;
|
|
- }
|
|
- }
|
|
+ if (!ret)
|
|
+ *oval = oldval;
|
|
+
|
|
return ret;
|
|
}
|
|
|
|
diff --git a/arch/s390/include/asm/nospec-insn.h b/arch/s390/include/asm/nospec-insn.h
|
|
new file mode 100644
|
|
index 000000000000..087fc9b972c5
|
|
--- /dev/null
|
|
+++ b/arch/s390/include/asm/nospec-insn.h
|
|
@@ -0,0 +1,182 @@
|
|
+/* SPDX-License-Identifier: GPL-2.0 */
|
|
+#ifndef _ASM_S390_NOSPEC_ASM_H
|
|
+#define _ASM_S390_NOSPEC_ASM_H
|
|
+
|
|
+#ifdef __ASSEMBLY__
|
|
+
|
|
+#ifdef CONFIG_EXPOLINE
|
|
+
|
|
+/*
|
|
+ * The expoline macros are used to create thunks in the same format
|
|
+ * as gcc generates them. The 'comdat' section flag makes sure that
|
|
+ * the various thunks are merged into a single copy.
|
|
+ */
|
|
+ .macro __THUNK_PROLOG_NAME name
|
|
+ .pushsection .text.\name,"axG",@progbits,\name,comdat
|
|
+ .globl \name
|
|
+ .hidden \name
|
|
+ .type \name,@function
|
|
+\name:
|
|
+ .cfi_startproc
|
|
+ .endm
|
|
+
|
|
+ .macro __THUNK_EPILOG
|
|
+ .cfi_endproc
|
|
+ .popsection
|
|
+ .endm
|
|
+
|
|
+ .macro __THUNK_PROLOG_BR r1,r2
|
|
+ __THUNK_PROLOG_NAME __s390x_indirect_jump_r\r2\()use_r\r1
|
|
+ .endm
|
|
+
|
|
+ .macro __THUNK_PROLOG_BC d0,r1,r2
|
|
+ __THUNK_PROLOG_NAME __s390x_indirect_branch_\d0\()_\r2\()use_\r1
|
|
+ .endm
|
|
+
|
|
+ .macro __THUNK_BR r1,r2
|
|
+ jg __s390x_indirect_jump_r\r2\()use_r\r1
|
|
+ .endm
|
|
+
|
|
+ .macro __THUNK_BC d0,r1,r2
|
|
+ jg __s390x_indirect_branch_\d0\()_\r2\()use_\r1
|
|
+ .endm
|
|
+
|
|
+ .macro __THUNK_BRASL r1,r2,r3
|
|
+ brasl \r1,__s390x_indirect_jump_r\r3\()use_r\r2
|
|
+ .endm
|
|
+
|
|
+ .macro __DECODE_RR expand,reg,ruse
|
|
+ .set __decode_fail,1
|
|
+ .irp r1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
|
|
+ .ifc \reg,%r\r1
|
|
+ .irp r2,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
|
|
+ .ifc \ruse,%r\r2
|
|
+ \expand \r1,\r2
|
|
+ .set __decode_fail,0
|
|
+ .endif
|
|
+ .endr
|
|
+ .endif
|
|
+ .endr
|
|
+ .if __decode_fail == 1
|
|
+ .error "__DECODE_RR failed"
|
|
+ .endif
|
|
+ .endm
|
|
+
|
|
+ .macro __DECODE_RRR expand,rsave,rtarget,ruse
|
|
+ .set __decode_fail,1
|
|
+ .irp r1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
|
|
+ .ifc \rsave,%r\r1
|
|
+ .irp r2,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
|
|
+ .ifc \rtarget,%r\r2
|
|
+ .irp r3,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
|
|
+ .ifc \ruse,%r\r3
|
|
+ \expand \r1,\r2,\r3
|
|
+ .set __decode_fail,0
|
|
+ .endif
|
|
+ .endr
|
|
+ .endif
|
|
+ .endr
|
|
+ .endif
|
|
+ .endr
|
|
+ .if __decode_fail == 1
|
|
+ .error "__DECODE_RRR failed"
|
|
+ .endif
|
|
+ .endm
|
|
+
|
|
+ .macro __DECODE_DRR expand,disp,reg,ruse
|
|
+ .set __decode_fail,1
|
|
+ .irp r1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
|
|
+ .ifc \reg,%r\r1
|
|
+ .irp r2,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
|
|
+ .ifc \ruse,%r\r2
|
|
+ \expand \disp,\r1,\r2
|
|
+ .set __decode_fail,0
|
|
+ .endif
|
|
+ .endr
|
|
+ .endif
|
|
+ .endr
|
|
+ .if __decode_fail == 1
|
|
+ .error "__DECODE_DRR failed"
|
|
+ .endif
|
|
+ .endm
|
|
+
|
|
+ .macro __THUNK_EX_BR reg,ruse
|
|
+#ifdef CONFIG_HAVE_MARCH_Z10_FEATURES
|
|
+ exrl 0,555f
|
|
+ j .
|
|
+#else
|
|
+ larl \ruse,555f
|
|
+ ex 0,0(\ruse)
|
|
+ j .
|
|
+#endif
|
|
+555: br \reg
|
|
+ .endm
|
|
+
|
|
+ .macro __THUNK_EX_BC disp,reg,ruse
|
|
+#ifdef CONFIG_HAVE_MARCH_Z10_FEATURES
|
|
+ exrl 0,556f
|
|
+ j .
|
|
+#else
|
|
+ larl \ruse,556f
|
|
+ ex 0,0(\ruse)
|
|
+ j .
|
|
+#endif
|
|
+556: b \disp(\reg)
|
|
+ .endm
|
|
+
|
|
+ .macro GEN_BR_THUNK reg,ruse=%r1
|
|
+ __DECODE_RR __THUNK_PROLOG_BR,\reg,\ruse
|
|
+ __THUNK_EX_BR \reg,\ruse
|
|
+ __THUNK_EPILOG
|
|
+ .endm
|
|
+
|
|
+ .macro GEN_B_THUNK disp,reg,ruse=%r1
|
|
+ __DECODE_DRR __THUNK_PROLOG_BC,\disp,\reg,\ruse
|
|
+ __THUNK_EX_BC \disp,\reg,\ruse
|
|
+ __THUNK_EPILOG
|
|
+ .endm
|
|
+
|
|
+ .macro BR_EX reg,ruse=%r1
|
|
+557: __DECODE_RR __THUNK_BR,\reg,\ruse
|
|
+ .pushsection .s390_indirect_branches,"a",@progbits
|
|
+ .long 557b-.
|
|
+ .popsection
|
|
+ .endm
|
|
+
|
|
+ .macro B_EX disp,reg,ruse=%r1
|
|
+558: __DECODE_DRR __THUNK_BC,\disp,\reg,\ruse
|
|
+ .pushsection .s390_indirect_branches,"a",@progbits
|
|
+ .long 558b-.
|
|
+ .popsection
|
|
+ .endm
|
|
+
|
|
+ .macro BASR_EX rsave,rtarget,ruse=%r1
|
|
+559: __DECODE_RRR __THUNK_BRASL,\rsave,\rtarget,\ruse
|
|
+ .pushsection .s390_indirect_branches,"a",@progbits
|
|
+ .long 559b-.
|
|
+ .popsection
|
|
+ .endm
|
|
+
|
|
+#else
|
|
+ .macro GEN_BR_THUNK reg,ruse=%r1
|
|
+ .endm
|
|
+
|
|
+ .macro GEN_B_THUNK disp,reg,ruse=%r1
|
|
+ .endm
|
|
+
|
|
+ .macro BR_EX reg,ruse=%r1
|
|
+ br \reg
|
|
+ .endm
|
|
+
|
|
+ .macro B_EX disp,reg,ruse=%r1
|
|
+ b \disp(\reg)
|
|
+ .endm
|
|
+
|
|
+ .macro BASR_EX rsave,rtarget,ruse=%r1
|
|
+ basr \rsave,\rtarget
|
|
+ .endm
|
|
+#endif
|
|
+
|
|
+#endif /* __ASSEMBLY__ */
|
|
+
|
|
+#endif /* _ASM_S390_NOSPEC_ASM_H */
|
|
diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile
|
|
index 8ccfbf22ecbb..c4d4d4ef5e58 100644
|
|
--- a/arch/s390/kernel/Makefile
|
|
+++ b/arch/s390/kernel/Makefile
|
|
@@ -49,6 +49,7 @@ obj-y += nospec-branch.o
|
|
|
|
extra-y += head.o head64.o vmlinux.lds
|
|
|
|
+obj-$(CONFIG_SYSFS) += nospec-sysfs.o
|
|
CFLAGS_REMOVE_nospec-branch.o += $(CC_FLAGS_EXPOLINE)
|
|
|
|
obj-$(CONFIG_MODULES) += s390_ksyms.o module.o
|
|
diff --git a/arch/s390/kernel/base.S b/arch/s390/kernel/base.S
|
|
index 326f717df587..61fca549a93b 100644
|
|
--- a/arch/s390/kernel/base.S
|
|
+++ b/arch/s390/kernel/base.S
|
|
@@ -8,18 +8,22 @@
|
|
|
|
#include <linux/linkage.h>
|
|
#include <asm/asm-offsets.h>
|
|
+#include <asm/nospec-insn.h>
|
|
#include <asm/ptrace.h>
|
|
#include <asm/sigp.h>
|
|
|
|
+ GEN_BR_THUNK %r9
|
|
+ GEN_BR_THUNK %r14
|
|
+
|
|
ENTRY(s390_base_mcck_handler)
|
|
basr %r13,0
|
|
0: lg %r15,__LC_PANIC_STACK # load panic stack
|
|
aghi %r15,-STACK_FRAME_OVERHEAD
|
|
larl %r1,s390_base_mcck_handler_fn
|
|
- lg %r1,0(%r1)
|
|
- ltgr %r1,%r1
|
|
+ lg %r9,0(%r1)
|
|
+ ltgr %r9,%r9
|
|
jz 1f
|
|
- basr %r14,%r1
|
|
+ BASR_EX %r14,%r9
|
|
1: la %r1,4095
|
|
lmg %r0,%r15,__LC_GPREGS_SAVE_AREA-4095(%r1)
|
|
lpswe __LC_MCK_OLD_PSW
|
|
@@ -36,10 +40,10 @@ ENTRY(s390_base_ext_handler)
|
|
basr %r13,0
|
|
0: aghi %r15,-STACK_FRAME_OVERHEAD
|
|
larl %r1,s390_base_ext_handler_fn
|
|
- lg %r1,0(%r1)
|
|
- ltgr %r1,%r1
|
|
+ lg %r9,0(%r1)
|
|
+ ltgr %r9,%r9
|
|
jz 1f
|
|
- basr %r14,%r1
|
|
+ BASR_EX %r14,%r9
|
|
1: lmg %r0,%r15,__LC_SAVE_AREA_ASYNC
|
|
ni __LC_EXT_OLD_PSW+1,0xfd # clear wait state bit
|
|
lpswe __LC_EXT_OLD_PSW
|
|
@@ -56,10 +60,10 @@ ENTRY(s390_base_pgm_handler)
|
|
basr %r13,0
|
|
0: aghi %r15,-STACK_FRAME_OVERHEAD
|
|
larl %r1,s390_base_pgm_handler_fn
|
|
- lg %r1,0(%r1)
|
|
- ltgr %r1,%r1
|
|
+ lg %r9,0(%r1)
|
|
+ ltgr %r9,%r9
|
|
jz 1f
|
|
- basr %r14,%r1
|
|
+ BASR_EX %r14,%r9
|
|
lmg %r0,%r15,__LC_SAVE_AREA_SYNC
|
|
lpswe __LC_PGM_OLD_PSW
|
|
1: lpswe disabled_wait_psw-0b(%r13)
|
|
@@ -116,7 +120,7 @@ ENTRY(diag308_reset)
|
|
larl %r4,.Lcontinue_psw # Restore PSW flags
|
|
lpswe 0(%r4)
|
|
.Lcontinue:
|
|
- br %r14
|
|
+ BR_EX %r14
|
|
.align 16
|
|
.Lrestart_psw:
|
|
.long 0x00080000,0x80000000 + .Lrestart_part2
|
|
diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S
|
|
index c63730326215..5416d5d68308 100644
|
|
--- a/arch/s390/kernel/entry.S
|
|
+++ b/arch/s390/kernel/entry.S
|
|
@@ -23,6 +23,7 @@
|
|
#include <asm/vx-insn.h>
|
|
#include <asm/setup.h>
|
|
#include <asm/nmi.h>
|
|
+#include <asm/nospec-insn.h>
|
|
|
|
__PT_R0 = __PT_GPRS
|
|
__PT_R1 = __PT_GPRS + 8
|
|
@@ -225,74 +226,16 @@ _PIF_WORK = (_PIF_PER_TRAP)
|
|
.popsection
|
|
.endm
|
|
|
|
-#ifdef CONFIG_EXPOLINE
|
|
-
|
|
- .macro GEN_BR_THUNK name,reg,tmp
|
|
- .section .text.\name,"axG",@progbits,\name,comdat
|
|
- .globl \name
|
|
- .hidden \name
|
|
- .type \name,@function
|
|
-\name:
|
|
- .cfi_startproc
|
|
-#ifdef CONFIG_HAVE_MARCH_Z10_FEATURES
|
|
- exrl 0,0f
|
|
-#else
|
|
- larl \tmp,0f
|
|
- ex 0,0(\tmp)
|
|
-#endif
|
|
- j .
|
|
-0: br \reg
|
|
- .cfi_endproc
|
|
- .endm
|
|
-
|
|
- GEN_BR_THUNK __s390x_indirect_jump_r1use_r9,%r9,%r1
|
|
- GEN_BR_THUNK __s390x_indirect_jump_r1use_r14,%r14,%r1
|
|
- GEN_BR_THUNK __s390x_indirect_jump_r11use_r14,%r14,%r11
|
|
-
|
|
- .macro BASR_R14_R9
|
|
-0: brasl %r14,__s390x_indirect_jump_r1use_r9
|
|
- .pushsection .s390_indirect_branches,"a",@progbits
|
|
- .long 0b-.
|
|
- .popsection
|
|
- .endm
|
|
-
|
|
- .macro BR_R1USE_R14
|
|
-0: jg __s390x_indirect_jump_r1use_r14
|
|
- .pushsection .s390_indirect_branches,"a",@progbits
|
|
- .long 0b-.
|
|
- .popsection
|
|
- .endm
|
|
-
|
|
- .macro BR_R11USE_R14
|
|
-0: jg __s390x_indirect_jump_r11use_r14
|
|
- .pushsection .s390_indirect_branches,"a",@progbits
|
|
- .long 0b-.
|
|
- .popsection
|
|
- .endm
|
|
-
|
|
-#else /* CONFIG_EXPOLINE */
|
|
-
|
|
- .macro BASR_R14_R9
|
|
- basr %r14,%r9
|
|
- .endm
|
|
-
|
|
- .macro BR_R1USE_R14
|
|
- br %r14
|
|
- .endm
|
|
-
|
|
- .macro BR_R11USE_R14
|
|
- br %r14
|
|
- .endm
|
|
-
|
|
-#endif /* CONFIG_EXPOLINE */
|
|
-
|
|
+ GEN_BR_THUNK %r9
|
|
+ GEN_BR_THUNK %r14
|
|
+ GEN_BR_THUNK %r14,%r11
|
|
|
|
.section .kprobes.text, "ax"
|
|
|
|
ENTRY(__bpon)
|
|
.globl __bpon
|
|
BPON
|
|
- BR_R1USE_R14
|
|
+ BR_EX %r14
|
|
|
|
/*
|
|
* Scheduler resume function, called by switch_to
|
|
@@ -322,7 +265,7 @@ ENTRY(__switch_to)
|
|
TSTMSK __LC_MACHINE_FLAGS,MACHINE_FLAG_LPP
|
|
jz 0f
|
|
.insn s,0xb2800000,__LC_LPP # set program parameter
|
|
-0: BR_R1USE_R14
|
|
+0: BR_EX %r14
|
|
|
|
.L__critical_start:
|
|
|
|
@@ -388,7 +331,7 @@ sie_exit:
|
|
xgr %r5,%r5
|
|
lmg %r6,%r14,__SF_GPRS(%r15) # restore kernel registers
|
|
lg %r2,__SF_EMPTY+16(%r15) # return exit reason code
|
|
- BR_R1USE_R14
|
|
+ BR_EX %r14
|
|
.Lsie_fault:
|
|
lghi %r14,-EFAULT
|
|
stg %r14,__SF_EMPTY+16(%r15) # set exit reason code
|
|
@@ -445,7 +388,7 @@ ENTRY(system_call)
|
|
lgf %r9,0(%r8,%r10) # get system call add.
|
|
TSTMSK __TI_flags(%r12),_TIF_TRACE
|
|
jnz .Lsysc_tracesys
|
|
- BASR_R14_R9 # call sys_xxxx
|
|
+ BASR_EX %r14,%r9 # call sys_xxxx
|
|
stg %r2,__PT_R2(%r11) # store return value
|
|
|
|
.Lsysc_return:
|
|
@@ -585,7 +528,7 @@ ENTRY(system_call)
|
|
lmg %r3,%r7,__PT_R3(%r11)
|
|
stg %r7,STACK_FRAME_OVERHEAD(%r15)
|
|
lg %r2,__PT_ORIG_GPR2(%r11)
|
|
- BASR_R14_R9 # call sys_xxx
|
|
+ BASR_EX %r14,%r9 # call sys_xxx
|
|
stg %r2,__PT_R2(%r11) # store return value
|
|
.Lsysc_tracenogo:
|
|
TSTMSK __TI_flags(%r12),_TIF_TRACE
|
|
@@ -609,7 +552,7 @@ ENTRY(ret_from_fork)
|
|
lmg %r9,%r10,__PT_R9(%r11) # load gprs
|
|
ENTRY(kernel_thread_starter)
|
|
la %r2,0(%r10)
|
|
- BASR_R14_R9
|
|
+ BASR_EX %r14,%r9
|
|
j .Lsysc_tracenogo
|
|
|
|
/*
|
|
@@ -685,7 +628,7 @@ ENTRY(pgm_check_handler)
|
|
je .Lpgm_return
|
|
lgf %r9,0(%r10,%r1) # load address of handler routine
|
|
lgr %r2,%r11 # pass pointer to pt_regs
|
|
- BASR_R14_R9 # branch to interrupt-handler
|
|
+ BASR_EX %r14,%r9 # branch to interrupt-handler
|
|
.Lpgm_return:
|
|
LOCKDEP_SYS_EXIT
|
|
tm __PT_PSW+1(%r11),0x01 # returning to user ?
|
|
@@ -962,7 +905,7 @@ ENTRY(psw_idle)
|
|
stpt __TIMER_IDLE_ENTER(%r2)
|
|
.Lpsw_idle_lpsw:
|
|
lpswe __SF_EMPTY(%r15)
|
|
- BR_R1USE_R14
|
|
+ BR_EX %r14
|
|
.Lpsw_idle_end:
|
|
|
|
/*
|
|
@@ -1007,7 +950,7 @@ ENTRY(save_fpu_regs)
|
|
.Lsave_fpu_regs_done:
|
|
oi __LC_CPU_FLAGS+7,_CIF_FPU
|
|
.Lsave_fpu_regs_exit:
|
|
- BR_R1USE_R14
|
|
+ BR_EX %r14
|
|
.Lsave_fpu_regs_end:
|
|
|
|
/*
|
|
@@ -1054,7 +997,7 @@ load_fpu_regs:
|
|
.Lload_fpu_regs_done:
|
|
ni __LC_CPU_FLAGS+7,255-_CIF_FPU
|
|
.Lload_fpu_regs_exit:
|
|
- BR_R1USE_R14
|
|
+ BR_EX %r14
|
|
.Lload_fpu_regs_end:
|
|
|
|
.L__critical_end:
|
|
@@ -1227,7 +1170,7 @@ cleanup_critical:
|
|
jl 0f
|
|
clg %r9,BASED(.Lcleanup_table+104) # .Lload_fpu_regs_end
|
|
jl .Lcleanup_load_fpu_regs
|
|
-0: BR_R11USE_R14
|
|
+0: BR_EX %r14
|
|
|
|
.align 8
|
|
.Lcleanup_table:
|
|
@@ -1257,7 +1200,7 @@ cleanup_critical:
|
|
ni __SIE_PROG0C+3(%r9),0xfe # no longer in SIE
|
|
lctlg %c1,%c1,__LC_USER_ASCE # load primary asce
|
|
larl %r9,sie_exit # skip forward to sie_exit
|
|
- BR_R11USE_R14
|
|
+ BR_EX %r14
|
|
#endif
|
|
|
|
.Lcleanup_system_call:
|
|
@@ -1315,7 +1258,7 @@ cleanup_critical:
|
|
stg %r15,56(%r11) # r15 stack pointer
|
|
# set new psw address and exit
|
|
larl %r9,.Lsysc_do_svc
|
|
- BR_R11USE_R14
|
|
+ BR_EX %r14,%r11
|
|
.Lcleanup_system_call_insn:
|
|
.quad system_call
|
|
.quad .Lsysc_stmg
|
|
@@ -1325,7 +1268,7 @@ cleanup_critical:
|
|
|
|
.Lcleanup_sysc_tif:
|
|
larl %r9,.Lsysc_tif
|
|
- BR_R11USE_R14
|
|
+ BR_EX %r14,%r11
|
|
|
|
.Lcleanup_sysc_restore:
|
|
# check if stpt has been executed
|
|
@@ -1342,14 +1285,14 @@ cleanup_critical:
|
|
mvc 0(64,%r11),__PT_R8(%r9)
|
|
lmg %r0,%r7,__PT_R0(%r9)
|
|
1: lmg %r8,%r9,__LC_RETURN_PSW
|
|
- BR_R11USE_R14
|
|
+ BR_EX %r14,%r11
|
|
.Lcleanup_sysc_restore_insn:
|
|
.quad .Lsysc_exit_timer
|
|
.quad .Lsysc_done - 4
|
|
|
|
.Lcleanup_io_tif:
|
|
larl %r9,.Lio_tif
|
|
- BR_R11USE_R14
|
|
+ BR_EX %r14,%r11
|
|
|
|
.Lcleanup_io_restore:
|
|
# check if stpt has been executed
|
|
@@ -1363,7 +1306,7 @@ cleanup_critical:
|
|
mvc 0(64,%r11),__PT_R8(%r9)
|
|
lmg %r0,%r7,__PT_R0(%r9)
|
|
1: lmg %r8,%r9,__LC_RETURN_PSW
|
|
- BR_R11USE_R14
|
|
+ BR_EX %r14,%r11
|
|
.Lcleanup_io_restore_insn:
|
|
.quad .Lio_exit_timer
|
|
.quad .Lio_done - 4
|
|
@@ -1415,17 +1358,17 @@ cleanup_critical:
|
|
# prepare return psw
|
|
nihh %r8,0xfcfd # clear irq & wait state bits
|
|
lg %r9,48(%r11) # return from psw_idle
|
|
- BR_R11USE_R14
|
|
+ BR_EX %r14,%r11
|
|
.Lcleanup_idle_insn:
|
|
.quad .Lpsw_idle_lpsw
|
|
|
|
.Lcleanup_save_fpu_regs:
|
|
larl %r9,save_fpu_regs
|
|
- BR_R11USE_R14
|
|
+ BR_EX %r14,%r11
|
|
|
|
.Lcleanup_load_fpu_regs:
|
|
larl %r9,load_fpu_regs
|
|
- BR_R11USE_R14
|
|
+ BR_EX %r14,%r11
|
|
|
|
/*
|
|
* Integer constants
|
|
diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c
|
|
index f41d5208aaf7..590e9394b4dd 100644
|
|
--- a/arch/s390/kernel/irq.c
|
|
+++ b/arch/s390/kernel/irq.c
|
|
@@ -173,10 +173,9 @@ void do_softirq_own_stack(void)
|
|
new -= STACK_FRAME_OVERHEAD;
|
|
((struct stack_frame *) new)->back_chain = old;
|
|
asm volatile(" la 15,0(%0)\n"
|
|
- " basr 14,%2\n"
|
|
+ " brasl 14,__do_softirq\n"
|
|
" la 15,0(%1)\n"
|
|
- : : "a" (new), "a" (old),
|
|
- "a" (__do_softirq)
|
|
+ : : "a" (new), "a" (old)
|
|
: "0", "1", "2", "3", "4", "5", "14",
|
|
"cc", "memory" );
|
|
} else {
|
|
diff --git a/arch/s390/kernel/nospec-branch.c b/arch/s390/kernel/nospec-branch.c
|
|
index 9f3b5b382743..d5eed651b5ab 100644
|
|
--- a/arch/s390/kernel/nospec-branch.c
|
|
+++ b/arch/s390/kernel/nospec-branch.c
|
|
@@ -44,24 +44,6 @@ static int __init nospec_report(void)
|
|
}
|
|
arch_initcall(nospec_report);
|
|
|
|
-#ifdef CONFIG_SYSFS
|
|
-ssize_t cpu_show_spectre_v1(struct device *dev,
|
|
- struct device_attribute *attr, char *buf)
|
|
-{
|
|
- return sprintf(buf, "Mitigation: __user pointer sanitization\n");
|
|
-}
|
|
-
|
|
-ssize_t cpu_show_spectre_v2(struct device *dev,
|
|
- struct device_attribute *attr, char *buf)
|
|
-{
|
|
- if (IS_ENABLED(CC_USING_EXPOLINE) && !nospec_disable)
|
|
- return sprintf(buf, "Mitigation: execute trampolines\n");
|
|
- if (__test_facility(82, S390_lowcore.alt_stfle_fac_list))
|
|
- return sprintf(buf, "Mitigation: limited branch prediction.\n");
|
|
- return sprintf(buf, "Vulnerable\n");
|
|
-}
|
|
-#endif
|
|
-
|
|
#ifdef CONFIG_EXPOLINE
|
|
|
|
int nospec_disable = IS_ENABLED(CONFIG_EXPOLINE_OFF);
|
|
@@ -112,7 +94,6 @@ static void __init_or_module __nospec_revert(s32 *start, s32 *end)
|
|
s32 *epo;
|
|
|
|
/* Second part of the instruction replace is always a nop */
|
|
- memcpy(insnbuf + 2, (char[]) { 0x47, 0x00, 0x00, 0x00 }, 4);
|
|
for (epo = start; epo < end; epo++) {
|
|
instr = (u8 *) epo + *epo;
|
|
if (instr[0] == 0xc0 && (instr[1] & 0x0f) == 0x04)
|
|
@@ -133,18 +114,34 @@ static void __init_or_module __nospec_revert(s32 *start, s32 *end)
|
|
br = thunk + (*(int *)(thunk + 2)) * 2;
|
|
else
|
|
continue;
|
|
- if (br[0] != 0x07 || (br[1] & 0xf0) != 0xf0)
|
|
+ /* Check for unconditional branch 0x07f? or 0x47f???? */
|
|
+ if ((br[0] & 0xbf) != 0x07 || (br[1] & 0xf0) != 0xf0)
|
|
continue;
|
|
+
|
|
+ memcpy(insnbuf + 2, (char[]) { 0x47, 0x00, 0x07, 0x00 }, 4);
|
|
switch (type) {
|
|
case BRCL_EXPOLINE:
|
|
- /* brcl to thunk, replace with br + nop */
|
|
insnbuf[0] = br[0];
|
|
insnbuf[1] = (instr[1] & 0xf0) | (br[1] & 0x0f);
|
|
+ if (br[0] == 0x47) {
|
|
+ /* brcl to b, replace with bc + nopr */
|
|
+ insnbuf[2] = br[2];
|
|
+ insnbuf[3] = br[3];
|
|
+ } else {
|
|
+ /* brcl to br, replace with bcr + nop */
|
|
+ }
|
|
break;
|
|
case BRASL_EXPOLINE:
|
|
- /* brasl to thunk, replace with basr + nop */
|
|
- insnbuf[0] = 0x0d;
|
|
insnbuf[1] = (instr[1] & 0xf0) | (br[1] & 0x0f);
|
|
+ if (br[0] == 0x47) {
|
|
+ /* brasl to b, replace with bas + nopr */
|
|
+ insnbuf[0] = 0x4d;
|
|
+ insnbuf[2] = br[2];
|
|
+ insnbuf[3] = br[3];
|
|
+ } else {
|
|
+ /* brasl to br, replace with basr + nop */
|
|
+ insnbuf[0] = 0x0d;
|
|
+ }
|
|
break;
|
|
}
|
|
|
|
diff --git a/arch/s390/kernel/nospec-sysfs.c b/arch/s390/kernel/nospec-sysfs.c
|
|
new file mode 100644
|
|
index 000000000000..8affad5f18cb
|
|
--- /dev/null
|
|
+++ b/arch/s390/kernel/nospec-sysfs.c
|
|
@@ -0,0 +1,21 @@
|
|
+// SPDX-License-Identifier: GPL-2.0
|
|
+#include <linux/device.h>
|
|
+#include <linux/cpu.h>
|
|
+#include <asm/facility.h>
|
|
+#include <asm/nospec-branch.h>
|
|
+
|
|
+ssize_t cpu_show_spectre_v1(struct device *dev,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ return sprintf(buf, "Mitigation: __user pointer sanitization\n");
|
|
+}
|
|
+
|
|
+ssize_t cpu_show_spectre_v2(struct device *dev,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ if (IS_ENABLED(CC_USING_EXPOLINE) && !nospec_disable)
|
|
+ return sprintf(buf, "Mitigation: execute trampolines\n");
|
|
+ if (__test_facility(82, S390_lowcore.alt_stfle_fac_list))
|
|
+ return sprintf(buf, "Mitigation: limited branch prediction\n");
|
|
+ return sprintf(buf, "Vulnerable\n");
|
|
+}
|
|
diff --git a/arch/s390/kernel/perf_cpum_sf.c b/arch/s390/kernel/perf_cpum_sf.c
|
|
index 3d8da1e742c2..b79d51459cf2 100644
|
|
--- a/arch/s390/kernel/perf_cpum_sf.c
|
|
+++ b/arch/s390/kernel/perf_cpum_sf.c
|
|
@@ -744,6 +744,10 @@ static int __hw_perf_event_init(struct perf_event *event)
|
|
*/
|
|
rate = 0;
|
|
if (attr->freq) {
|
|
+ if (!attr->sample_freq) {
|
|
+ err = -EINVAL;
|
|
+ goto out;
|
|
+ }
|
|
rate = freq_to_sample_rate(&si, attr->sample_freq);
|
|
rate = hw_limit_rate(&si, rate);
|
|
attr->freq = 0;
|
|
diff --git a/arch/s390/kernel/reipl.S b/arch/s390/kernel/reipl.S
|
|
index 52aab0bd84f8..6b1b91c17b40 100644
|
|
--- a/arch/s390/kernel/reipl.S
|
|
+++ b/arch/s390/kernel/reipl.S
|
|
@@ -6,8 +6,11 @@
|
|
|
|
#include <linux/linkage.h>
|
|
#include <asm/asm-offsets.h>
|
|
+#include <asm/nospec-insn.h>
|
|
#include <asm/sigp.h>
|
|
|
|
+ GEN_BR_THUNK %r14
|
|
+
|
|
#
|
|
# store_status
|
|
#
|
|
@@ -62,7 +65,7 @@ ENTRY(store_status)
|
|
st %r3,__LC_PSW_SAVE_AREA-SAVE_AREA_BASE + 4(%r1)
|
|
larl %r2,store_status
|
|
stg %r2,__LC_PSW_SAVE_AREA-SAVE_AREA_BASE + 8(%r1)
|
|
- br %r14
|
|
+ BR_EX %r14
|
|
|
|
.section .bss
|
|
.align 8
|
|
diff --git a/arch/s390/kernel/swsusp.S b/arch/s390/kernel/swsusp.S
|
|
index 2d6b6e81f812..60a829c77378 100644
|
|
--- a/arch/s390/kernel/swsusp.S
|
|
+++ b/arch/s390/kernel/swsusp.S
|
|
@@ -12,6 +12,7 @@
|
|
#include <asm/ptrace.h>
|
|
#include <asm/thread_info.h>
|
|
#include <asm/asm-offsets.h>
|
|
+#include <asm/nospec-insn.h>
|
|
#include <asm/sigp.h>
|
|
|
|
/*
|
|
@@ -23,6 +24,8 @@
|
|
* (see below) in the resume process.
|
|
* This function runs with disabled interrupts.
|
|
*/
|
|
+ GEN_BR_THUNK %r14
|
|
+
|
|
.section .text
|
|
ENTRY(swsusp_arch_suspend)
|
|
stmg %r6,%r15,__SF_GPRS(%r15)
|
|
@@ -102,7 +105,7 @@ ENTRY(swsusp_arch_suspend)
|
|
spx 0x318(%r1)
|
|
lmg %r6,%r15,STACK_FRAME_OVERHEAD + __SF_GPRS(%r15)
|
|
lghi %r2,0
|
|
- br %r14
|
|
+ BR_EX %r14
|
|
|
|
/*
|
|
* Restore saved memory image to correct place and restore register context.
|
|
@@ -196,11 +199,10 @@ pgm_check_entry:
|
|
larl %r15,init_thread_union
|
|
ahi %r15,1<<(PAGE_SHIFT+THREAD_ORDER)
|
|
larl %r2,.Lpanic_string
|
|
- larl %r3,_sclp_print_early
|
|
lghi %r1,0
|
|
sam31
|
|
sigp %r1,%r0,SIGP_SET_ARCHITECTURE
|
|
- basr %r14,%r3
|
|
+ brasl %r14,_sclp_print_early
|
|
larl %r3,.Ldisabled_wait_31
|
|
lpsw 0(%r3)
|
|
4:
|
|
@@ -266,7 +268,7 @@ restore_registers:
|
|
/* Return 0 */
|
|
lmg %r6,%r15,STACK_FRAME_OVERHEAD + __SF_GPRS(%r15)
|
|
lghi %r2,0
|
|
- br %r14
|
|
+ BR_EX %r14
|
|
|
|
.section .data..nosave,"aw",@progbits
|
|
.align 8
|
|
diff --git a/arch/s390/lib/mem.S b/arch/s390/lib/mem.S
|
|
index c6d553e85ab1..16c5998b9792 100644
|
|
--- a/arch/s390/lib/mem.S
|
|
+++ b/arch/s390/lib/mem.S
|
|
@@ -5,6 +5,9 @@
|
|
*/
|
|
|
|
#include <linux/linkage.h>
|
|
+#include <asm/nospec-insn.h>
|
|
+
|
|
+ GEN_BR_THUNK %r14
|
|
|
|
/*
|
|
* memset implementation
|
|
@@ -38,7 +41,7 @@ ENTRY(memset)
|
|
.Lmemset_clear_rest:
|
|
larl %r3,.Lmemset_xc
|
|
ex %r4,0(%r3)
|
|
- br %r14
|
|
+ BR_EX %r14
|
|
.Lmemset_fill:
|
|
stc %r3,0(%r2)
|
|
cghi %r4,1
|
|
@@ -55,7 +58,7 @@ ENTRY(memset)
|
|
.Lmemset_fill_rest:
|
|
larl %r3,.Lmemset_mvc
|
|
ex %r4,0(%r3)
|
|
- br %r14
|
|
+ BR_EX %r14
|
|
.Lmemset_xc:
|
|
xc 0(1,%r1),0(%r1)
|
|
.Lmemset_mvc:
|
|
@@ -77,7 +80,7 @@ ENTRY(memcpy)
|
|
.Lmemcpy_rest:
|
|
larl %r5,.Lmemcpy_mvc
|
|
ex %r4,0(%r5)
|
|
- br %r14
|
|
+ BR_EX %r14
|
|
.Lmemcpy_loop:
|
|
mvc 0(256,%r1),0(%r3)
|
|
la %r1,256(%r1)
|
|
diff --git a/arch/s390/net/bpf_jit.S b/arch/s390/net/bpf_jit.S
|
|
index a1c917d881ec..fa716f2a95a7 100644
|
|
--- a/arch/s390/net/bpf_jit.S
|
|
+++ b/arch/s390/net/bpf_jit.S
|
|
@@ -8,6 +8,7 @@
|
|
*/
|
|
|
|
#include <linux/linkage.h>
|
|
+#include <asm/nospec-insn.h>
|
|
#include "bpf_jit.h"
|
|
|
|
/*
|
|
@@ -53,7 +54,7 @@ ENTRY(sk_load_##NAME##_pos); \
|
|
clg %r3,STK_OFF_HLEN(%r15); /* Offset + SIZE > hlen? */ \
|
|
jh sk_load_##NAME##_slow; \
|
|
LOAD %r14,-SIZE(%r3,%r12); /* Get data from skb */ \
|
|
- b OFF_OK(%r6); /* Return */ \
|
|
+ B_EX OFF_OK,%r6; /* Return */ \
|
|
\
|
|
sk_load_##NAME##_slow:; \
|
|
lgr %r2,%r7; /* Arg1 = skb pointer */ \
|
|
@@ -63,11 +64,14 @@ sk_load_##NAME##_slow:; \
|
|
brasl %r14,skb_copy_bits; /* Get data from skb */ \
|
|
LOAD %r14,STK_OFF_TMP(%r15); /* Load from temp bufffer */ \
|
|
ltgr %r2,%r2; /* Set cc to (%r2 != 0) */ \
|
|
- br %r6; /* Return */
|
|
+ BR_EX %r6; /* Return */
|
|
|
|
sk_load_common(word, 4, llgf) /* r14 = *(u32 *) (skb->data+offset) */
|
|
sk_load_common(half, 2, llgh) /* r14 = *(u16 *) (skb->data+offset) */
|
|
|
|
+ GEN_BR_THUNK %r6
|
|
+ GEN_B_THUNK OFF_OK,%r6
|
|
+
|
|
/*
|
|
* Load 1 byte from SKB (optimized version)
|
|
*/
|
|
@@ -79,7 +83,7 @@ ENTRY(sk_load_byte_pos)
|
|
clg %r3,STK_OFF_HLEN(%r15) # Offset >= hlen?
|
|
jnl sk_load_byte_slow
|
|
llgc %r14,0(%r3,%r12) # Get byte from skb
|
|
- b OFF_OK(%r6) # Return OK
|
|
+ B_EX OFF_OK,%r6 # Return OK
|
|
|
|
sk_load_byte_slow:
|
|
lgr %r2,%r7 # Arg1 = skb pointer
|
|
@@ -89,7 +93,7 @@ sk_load_byte_slow:
|
|
brasl %r14,skb_copy_bits # Get data from skb
|
|
llgc %r14,STK_OFF_TMP(%r15) # Load result from temp buffer
|
|
ltgr %r2,%r2 # Set cc to (%r2 != 0)
|
|
- br %r6 # Return cc
|
|
+ BR_EX %r6 # Return cc
|
|
|
|
#define sk_negative_common(NAME, SIZE, LOAD) \
|
|
sk_load_##NAME##_slow_neg:; \
|
|
@@ -103,7 +107,7 @@ sk_load_##NAME##_slow_neg:; \
|
|
jz bpf_error; \
|
|
LOAD %r14,0(%r2); /* Get data from pointer */ \
|
|
xr %r3,%r3; /* Set cc to zero */ \
|
|
- br %r6; /* Return cc */
|
|
+ BR_EX %r6; /* Return cc */
|
|
|
|
sk_negative_common(word, 4, llgf)
|
|
sk_negative_common(half, 2, llgh)
|
|
@@ -112,4 +116,4 @@ sk_negative_common(byte, 1, llgc)
|
|
bpf_error:
|
|
# force a return 0 from jit handler
|
|
ltgr %r15,%r15 # Set condition code
|
|
- br %r6
|
|
+ BR_EX %r6
|
|
diff --git a/arch/s390/net/bpf_jit_comp.c b/arch/s390/net/bpf_jit_comp.c
|
|
index 1395eeb6005f..a26528afceb2 100644
|
|
--- a/arch/s390/net/bpf_jit_comp.c
|
|
+++ b/arch/s390/net/bpf_jit_comp.c
|
|
@@ -24,6 +24,8 @@
|
|
#include <linux/bpf.h>
|
|
#include <asm/cacheflush.h>
|
|
#include <asm/dis.h>
|
|
+#include <asm/facility.h>
|
|
+#include <asm/nospec-branch.h>
|
|
#include "bpf_jit.h"
|
|
|
|
int bpf_jit_enable __read_mostly;
|
|
@@ -41,6 +43,8 @@ struct bpf_jit {
|
|
int base_ip; /* Base address for literal pool */
|
|
int ret0_ip; /* Address of return 0 */
|
|
int exit_ip; /* Address of exit */
|
|
+ int r1_thunk_ip; /* Address of expoline thunk for 'br %r1' */
|
|
+ int r14_thunk_ip; /* Address of expoline thunk for 'br %r14' */
|
|
int tail_call_start; /* Tail call start offset */
|
|
int labels[1]; /* Labels for local jumps */
|
|
};
|
|
@@ -248,6 +252,19 @@ static inline void reg_set_seen(struct bpf_jit *jit, u32 b1)
|
|
REG_SET_SEEN(b2); \
|
|
})
|
|
|
|
+#define EMIT6_PCREL_RILB(op, b, target) \
|
|
+({ \
|
|
+ int rel = (target - jit->prg) / 2; \
|
|
+ _EMIT6(op | reg_high(b) << 16 | rel >> 16, rel & 0xffff); \
|
|
+ REG_SET_SEEN(b); \
|
|
+})
|
|
+
|
|
+#define EMIT6_PCREL_RIL(op, target) \
|
|
+({ \
|
|
+ int rel = (target - jit->prg) / 2; \
|
|
+ _EMIT6(op | rel >> 16, rel & 0xffff); \
|
|
+})
|
|
+
|
|
#define _EMIT6_IMM(op, imm) \
|
|
({ \
|
|
unsigned int __imm = (imm); \
|
|
@@ -475,8 +492,45 @@ static void bpf_jit_epilogue(struct bpf_jit *jit)
|
|
EMIT4(0xb9040000, REG_2, BPF_REG_0);
|
|
/* Restore registers */
|
|
save_restore_regs(jit, REGS_RESTORE);
|
|
+ if (IS_ENABLED(CC_USING_EXPOLINE) && !nospec_disable) {
|
|
+ jit->r14_thunk_ip = jit->prg;
|
|
+ /* Generate __s390_indirect_jump_r14 thunk */
|
|
+ if (test_facility(35)) {
|
|
+ /* exrl %r0,.+10 */
|
|
+ EMIT6_PCREL_RIL(0xc6000000, jit->prg + 10);
|
|
+ } else {
|
|
+ /* larl %r1,.+14 */
|
|
+ EMIT6_PCREL_RILB(0xc0000000, REG_1, jit->prg + 14);
|
|
+ /* ex 0,0(%r1) */
|
|
+ EMIT4_DISP(0x44000000, REG_0, REG_1, 0);
|
|
+ }
|
|
+ /* j . */
|
|
+ EMIT4_PCREL(0xa7f40000, 0);
|
|
+ }
|
|
/* br %r14 */
|
|
_EMIT2(0x07fe);
|
|
+
|
|
+ if (IS_ENABLED(CC_USING_EXPOLINE) && !nospec_disable &&
|
|
+ (jit->seen & SEEN_FUNC)) {
|
|
+ jit->r1_thunk_ip = jit->prg;
|
|
+ /* Generate __s390_indirect_jump_r1 thunk */
|
|
+ if (test_facility(35)) {
|
|
+ /* exrl %r0,.+10 */
|
|
+ EMIT6_PCREL_RIL(0xc6000000, jit->prg + 10);
|
|
+ /* j . */
|
|
+ EMIT4_PCREL(0xa7f40000, 0);
|
|
+ /* br %r1 */
|
|
+ _EMIT2(0x07f1);
|
|
+ } else {
|
|
+ /* larl %r1,.+14 */
|
|
+ EMIT6_PCREL_RILB(0xc0000000, REG_1, jit->prg + 14);
|
|
+ /* ex 0,S390_lowcore.br_r1_tampoline */
|
|
+ EMIT4_DISP(0x44000000, REG_0, REG_0,
|
|
+ offsetof(struct _lowcore, br_r1_trampoline));
|
|
+ /* j . */
|
|
+ EMIT4_PCREL(0xa7f40000, 0);
|
|
+ }
|
|
+ }
|
|
}
|
|
|
|
/*
|
|
@@ -980,8 +1034,13 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp, int i
|
|
/* lg %w1,<d(imm)>(%l) */
|
|
EMIT6_DISP_LH(0xe3000000, 0x0004, REG_W1, REG_0, REG_L,
|
|
EMIT_CONST_U64(func));
|
|
- /* basr %r14,%w1 */
|
|
- EMIT2(0x0d00, REG_14, REG_W1);
|
|
+ if (IS_ENABLED(CC_USING_EXPOLINE) && !nospec_disable) {
|
|
+ /* brasl %r14,__s390_indirect_jump_r1 */
|
|
+ EMIT6_PCREL_RILB(0xc0050000, REG_14, jit->r1_thunk_ip);
|
|
+ } else {
|
|
+ /* basr %r14,%w1 */
|
|
+ EMIT2(0x0d00, REG_14, REG_W1);
|
|
+ }
|
|
/* lgr %b0,%r2: load return value into %b0 */
|
|
EMIT4(0xb9040000, BPF_REG_0, REG_2);
|
|
if (bpf_helper_changes_skb_data((void *)func)) {
|
|
diff --git a/arch/sh/include/asm/futex.h b/arch/sh/include/asm/futex.h
|
|
index 7be39a646fbd..e05187d26d76 100644
|
|
--- a/arch/sh/include/asm/futex.h
|
|
+++ b/arch/sh/include/asm/futex.h
|
|
@@ -10,20 +10,11 @@
|
|
/* XXX: UP variants, fix for SH-4A and SMP.. */
|
|
#include <asm/futex-irq.h>
|
|
|
|
-static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
|
|
+static inline int arch_futex_atomic_op_inuser(int op, u32 oparg, int *oval,
|
|
+ u32 __user *uaddr)
|
|
{
|
|
- int op = (encoded_op >> 28) & 7;
|
|
- int cmp = (encoded_op >> 24) & 15;
|
|
- int oparg = (encoded_op << 8) >> 20;
|
|
- int cmparg = (encoded_op << 20) >> 20;
|
|
int oldval = 0, ret;
|
|
|
|
- if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
|
|
- oparg = 1 << oparg;
|
|
-
|
|
- if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
|
|
- return -EFAULT;
|
|
-
|
|
pagefault_disable();
|
|
|
|
switch (op) {
|
|
@@ -49,17 +40,8 @@ static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
|
|
|
|
pagefault_enable();
|
|
|
|
- if (!ret) {
|
|
- switch (cmp) {
|
|
- case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break;
|
|
- case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break;
|
|
- case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break;
|
|
- case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break;
|
|
- case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break;
|
|
- case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break;
|
|
- default: ret = -ENOSYS;
|
|
- }
|
|
- }
|
|
+ if (!ret)
|
|
+ *oval = oldval;
|
|
|
|
return ret;
|
|
}
|
|
diff --git a/arch/sparc/include/asm/futex_64.h b/arch/sparc/include/asm/futex_64.h
|
|
index 4e899b0dabf7..1cfd89d92208 100644
|
|
--- a/arch/sparc/include/asm/futex_64.h
|
|
+++ b/arch/sparc/include/asm/futex_64.h
|
|
@@ -29,22 +29,14 @@
|
|
: "r" (uaddr), "r" (oparg), "i" (-EFAULT) \
|
|
: "memory")
|
|
|
|
-static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
|
|
+static inline int arch_futex_atomic_op_inuser(int op, int oparg, int *oval,
|
|
+ u32 __user *uaddr)
|
|
{
|
|
- int op = (encoded_op >> 28) & 7;
|
|
- int cmp = (encoded_op >> 24) & 15;
|
|
- int oparg = (encoded_op << 8) >> 20;
|
|
- int cmparg = (encoded_op << 20) >> 20;
|
|
int oldval = 0, ret, tem;
|
|
|
|
- if (unlikely(!access_ok(VERIFY_WRITE, uaddr, sizeof(u32))))
|
|
- return -EFAULT;
|
|
if (unlikely((((unsigned long) uaddr) & 0x3UL)))
|
|
return -EINVAL;
|
|
|
|
- if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
|
|
- oparg = 1 << oparg;
|
|
-
|
|
pagefault_disable();
|
|
|
|
switch (op) {
|
|
@@ -69,17 +61,9 @@ static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
|
|
|
|
pagefault_enable();
|
|
|
|
- if (!ret) {
|
|
- switch (cmp) {
|
|
- case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break;
|
|
- case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break;
|
|
- case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break;
|
|
- case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break;
|
|
- case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break;
|
|
- case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break;
|
|
- default: ret = -ENOSYS;
|
|
- }
|
|
- }
|
|
+ if (!ret)
|
|
+ *oval = oldval;
|
|
+
|
|
return ret;
|
|
}
|
|
|
|
diff --git a/arch/tile/include/asm/futex.h b/arch/tile/include/asm/futex.h
|
|
index 1a6ef1b69cb1..d96d9dab5c0b 100644
|
|
--- a/arch/tile/include/asm/futex.h
|
|
+++ b/arch/tile/include/asm/futex.h
|
|
@@ -106,12 +106,9 @@
|
|
lock = __atomic_hashed_lock((int __force *)uaddr)
|
|
#endif
|
|
|
|
-static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
|
|
+static inline int arch_futex_atomic_op_inuser(int op, u32 oparg, int *oval,
|
|
+ u32 __user *uaddr)
|
|
{
|
|
- int op = (encoded_op >> 28) & 7;
|
|
- int cmp = (encoded_op >> 24) & 15;
|
|
- int oparg = (encoded_op << 8) >> 20;
|
|
- int cmparg = (encoded_op << 20) >> 20;
|
|
int uninitialized_var(val), ret;
|
|
|
|
__futex_prolog();
|
|
@@ -119,12 +116,6 @@ static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
|
|
/* The 32-bit futex code makes this assumption, so validate it here. */
|
|
BUILD_BUG_ON(sizeof(atomic_t) != sizeof(int));
|
|
|
|
- if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
|
|
- oparg = 1 << oparg;
|
|
-
|
|
- if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
|
|
- return -EFAULT;
|
|
-
|
|
pagefault_disable();
|
|
switch (op) {
|
|
case FUTEX_OP_SET:
|
|
@@ -148,30 +139,9 @@ static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
|
|
}
|
|
pagefault_enable();
|
|
|
|
- if (!ret) {
|
|
- switch (cmp) {
|
|
- case FUTEX_OP_CMP_EQ:
|
|
- ret = (val == cmparg);
|
|
- break;
|
|
- case FUTEX_OP_CMP_NE:
|
|
- ret = (val != cmparg);
|
|
- break;
|
|
- case FUTEX_OP_CMP_LT:
|
|
- ret = (val < cmparg);
|
|
- break;
|
|
- case FUTEX_OP_CMP_GE:
|
|
- ret = (val >= cmparg);
|
|
- break;
|
|
- case FUTEX_OP_CMP_LE:
|
|
- ret = (val <= cmparg);
|
|
- break;
|
|
- case FUTEX_OP_CMP_GT:
|
|
- ret = (val > cmparg);
|
|
- break;
|
|
- default:
|
|
- ret = -ENOSYS;
|
|
- }
|
|
- }
|
|
+ if (!ret)
|
|
+ *oval = val;
|
|
+
|
|
return ret;
|
|
}
|
|
|
|
diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
|
|
index 583d539a4197..2bc6651791cc 100644
|
|
--- a/arch/x86/boot/compressed/eboot.c
|
|
+++ b/arch/x86/boot/compressed/eboot.c
|
|
@@ -364,7 +364,8 @@ __setup_efi_pci32(efi_pci_io_protocol_32 *pci, struct pci_setup_rom **__rom)
|
|
if (status != EFI_SUCCESS)
|
|
goto free_struct;
|
|
|
|
- memcpy(rom->romdata, pci->romimage, pci->romsize);
|
|
+ memcpy(rom->romdata, (void *)(unsigned long)pci->romimage,
|
|
+ pci->romsize);
|
|
return status;
|
|
|
|
free_struct:
|
|
@@ -470,7 +471,8 @@ __setup_efi_pci64(efi_pci_io_protocol_64 *pci, struct pci_setup_rom **__rom)
|
|
if (status != EFI_SUCCESS)
|
|
goto free_struct;
|
|
|
|
- memcpy(rom->romdata, pci->romimage, pci->romsize);
|
|
+ memcpy(rom->romdata, (void *)(unsigned long)pci->romimage,
|
|
+ pci->romsize);
|
|
return status;
|
|
|
|
free_struct:
|
|
diff --git a/arch/x86/include/asm/futex.h b/arch/x86/include/asm/futex.h
|
|
index b4c1f5453436..f4dc9b63bdda 100644
|
|
--- a/arch/x86/include/asm/futex.h
|
|
+++ b/arch/x86/include/asm/futex.h
|
|
@@ -41,20 +41,11 @@
|
|
"+m" (*uaddr), "=&r" (tem) \
|
|
: "r" (oparg), "i" (-EFAULT), "1" (0))
|
|
|
|
-static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
|
|
+static inline int arch_futex_atomic_op_inuser(int op, int oparg, int *oval,
|
|
+ u32 __user *uaddr)
|
|
{
|
|
- int op = (encoded_op >> 28) & 7;
|
|
- int cmp = (encoded_op >> 24) & 15;
|
|
- int oparg = (encoded_op << 8) >> 20;
|
|
- int cmparg = (encoded_op << 20) >> 20;
|
|
int oldval = 0, ret, tem;
|
|
|
|
- if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
|
|
- oparg = 1 << oparg;
|
|
-
|
|
- if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
|
|
- return -EFAULT;
|
|
-
|
|
pagefault_disable();
|
|
|
|
switch (op) {
|
|
@@ -80,30 +71,9 @@ static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
|
|
|
|
pagefault_enable();
|
|
|
|
- if (!ret) {
|
|
- switch (cmp) {
|
|
- case FUTEX_OP_CMP_EQ:
|
|
- ret = (oldval == cmparg);
|
|
- break;
|
|
- case FUTEX_OP_CMP_NE:
|
|
- ret = (oldval != cmparg);
|
|
- break;
|
|
- case FUTEX_OP_CMP_LT:
|
|
- ret = (oldval < cmparg);
|
|
- break;
|
|
- case FUTEX_OP_CMP_GE:
|
|
- ret = (oldval >= cmparg);
|
|
- break;
|
|
- case FUTEX_OP_CMP_LE:
|
|
- ret = (oldval <= cmparg);
|
|
- break;
|
|
- case FUTEX_OP_CMP_GT:
|
|
- ret = (oldval > cmparg);
|
|
- break;
|
|
- default:
|
|
- ret = -ENOSYS;
|
|
- }
|
|
- }
|
|
+ if (!ret)
|
|
+ *oval = oldval;
|
|
+
|
|
return ret;
|
|
}
|
|
|
|
diff --git a/arch/x86/kernel/machine_kexec_32.c b/arch/x86/kernel/machine_kexec_32.c
|
|
index 469b23d6acc2..fd7e9937ddd6 100644
|
|
--- a/arch/x86/kernel/machine_kexec_32.c
|
|
+++ b/arch/x86/kernel/machine_kexec_32.c
|
|
@@ -71,12 +71,17 @@ static void load_segments(void)
|
|
static void machine_kexec_free_page_tables(struct kimage *image)
|
|
{
|
|
free_page((unsigned long)image->arch.pgd);
|
|
+ image->arch.pgd = NULL;
|
|
#ifdef CONFIG_X86_PAE
|
|
free_page((unsigned long)image->arch.pmd0);
|
|
+ image->arch.pmd0 = NULL;
|
|
free_page((unsigned long)image->arch.pmd1);
|
|
+ image->arch.pmd1 = NULL;
|
|
#endif
|
|
free_page((unsigned long)image->arch.pte0);
|
|
+ image->arch.pte0 = NULL;
|
|
free_page((unsigned long)image->arch.pte1);
|
|
+ image->arch.pte1 = NULL;
|
|
}
|
|
|
|
static int machine_kexec_alloc_page_tables(struct kimage *image)
|
|
@@ -93,7 +98,6 @@ static int machine_kexec_alloc_page_tables(struct kimage *image)
|
|
!image->arch.pmd0 || !image->arch.pmd1 ||
|
|
#endif
|
|
!image->arch.pte0 || !image->arch.pte1) {
|
|
- machine_kexec_free_page_tables(image);
|
|
return -ENOMEM;
|
|
}
|
|
return 0;
|
|
diff --git a/arch/x86/kernel/machine_kexec_64.c b/arch/x86/kernel/machine_kexec_64.c
|
|
index ca6e65250b1a..13d6b8ac0b0b 100644
|
|
--- a/arch/x86/kernel/machine_kexec_64.c
|
|
+++ b/arch/x86/kernel/machine_kexec_64.c
|
|
@@ -37,8 +37,11 @@ static struct kexec_file_ops *kexec_file_loaders[] = {
|
|
static void free_transition_pgtable(struct kimage *image)
|
|
{
|
|
free_page((unsigned long)image->arch.pud);
|
|
+ image->arch.pud = NULL;
|
|
free_page((unsigned long)image->arch.pmd);
|
|
+ image->arch.pmd = NULL;
|
|
free_page((unsigned long)image->arch.pte);
|
|
+ image->arch.pte = NULL;
|
|
}
|
|
|
|
static int init_transition_pgtable(struct kimage *image, pgd_t *pgd)
|
|
@@ -79,7 +82,6 @@ static int init_transition_pgtable(struct kimage *image, pgd_t *pgd)
|
|
set_pte(pte, pfn_pte(paddr >> PAGE_SHIFT, PAGE_KERNEL_EXEC));
|
|
return 0;
|
|
err:
|
|
- free_transition_pgtable(image);
|
|
return result;
|
|
}
|
|
|
|
diff --git a/arch/x86/xen/mmu.c b/arch/x86/xen/mmu.c
|
|
index 63146c378f1e..2b05f681a1fd 100644
|
|
--- a/arch/x86/xen/mmu.c
|
|
+++ b/arch/x86/xen/mmu.c
|
|
@@ -1316,8 +1316,6 @@ void xen_flush_tlb_all(void)
|
|
struct mmuext_op *op;
|
|
struct multicall_space mcs;
|
|
|
|
- trace_xen_mmu_flush_tlb_all(0);
|
|
-
|
|
preempt_disable();
|
|
|
|
mcs = xen_mc_entry(sizeof(*op));
|
|
@@ -1335,8 +1333,6 @@ static void xen_flush_tlb(void)
|
|
struct mmuext_op *op;
|
|
struct multicall_space mcs;
|
|
|
|
- trace_xen_mmu_flush_tlb(0);
|
|
-
|
|
preempt_disable();
|
|
|
|
mcs = xen_mc_entry(sizeof(*op));
|
|
diff --git a/arch/xtensa/include/asm/futex.h b/arch/xtensa/include/asm/futex.h
|
|
index 72bfc1cbc2b5..5bfbc1c401d4 100644
|
|
--- a/arch/xtensa/include/asm/futex.h
|
|
+++ b/arch/xtensa/include/asm/futex.h
|
|
@@ -44,18 +44,10 @@
|
|
: "r" (uaddr), "I" (-EFAULT), "r" (oparg) \
|
|
: "memory")
|
|
|
|
-static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
|
|
+static inline int arch_futex_atomic_op_inuser(int op, int oparg, int *oval,
|
|
+ u32 __user *uaddr)
|
|
{
|
|
- int op = (encoded_op >> 28) & 7;
|
|
- int cmp = (encoded_op >> 24) & 15;
|
|
- int oparg = (encoded_op << 8) >> 20;
|
|
- int cmparg = (encoded_op << 20) >> 20;
|
|
int oldval = 0, ret;
|
|
- if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
|
|
- oparg = 1 << oparg;
|
|
-
|
|
- if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
|
|
- return -EFAULT;
|
|
|
|
#if !XCHAL_HAVE_S32C1I
|
|
return -ENOSYS;
|
|
@@ -89,19 +81,10 @@ static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
|
|
|
|
pagefault_enable();
|
|
|
|
- if (ret)
|
|
- return ret;
|
|
+ if (!ret)
|
|
+ *oval = oldval;
|
|
|
|
- switch (cmp) {
|
|
- case FUTEX_OP_CMP_EQ: return (oldval == cmparg);
|
|
- case FUTEX_OP_CMP_NE: return (oldval != cmparg);
|
|
- case FUTEX_OP_CMP_LT: return (oldval < cmparg);
|
|
- case FUTEX_OP_CMP_GE: return (oldval >= cmparg);
|
|
- case FUTEX_OP_CMP_LE: return (oldval <= cmparg);
|
|
- case FUTEX_OP_CMP_GT: return (oldval > cmparg);
|
|
- }
|
|
-
|
|
- return -ENOSYS;
|
|
+ return ret;
|
|
}
|
|
|
|
static inline int
|
|
diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c
|
|
index 7ff8b15a3422..88728d997088 100644
|
|
--- a/drivers/cpufreq/intel_pstate.c
|
|
+++ b/drivers/cpufreq/intel_pstate.c
|
|
@@ -1361,6 +1361,11 @@ static inline bool intel_pstate_platform_pwr_mgmt_exists(void) { return false; }
|
|
static inline bool intel_pstate_has_acpi_ppc(void) { return false; }
|
|
#endif /* CONFIG_ACPI */
|
|
|
|
+static const struct x86_cpu_id hwp_support_ids[] __initconst = {
|
|
+ { X86_VENDOR_INTEL, 6, X86_MODEL_ANY, X86_FEATURE_HWP },
|
|
+ {}
|
|
+};
|
|
+
|
|
static int __init intel_pstate_init(void)
|
|
{
|
|
int cpu, rc = 0;
|
|
@@ -1370,17 +1375,16 @@ static int __init intel_pstate_init(void)
|
|
if (no_load)
|
|
return -ENODEV;
|
|
|
|
+ if (x86_match_cpu(hwp_support_ids) && !no_hwp) {
|
|
+ copy_cpu_funcs(&core_params.funcs);
|
|
+ hwp_active++;
|
|
+ goto hwp_cpu_matched;
|
|
+ }
|
|
+
|
|
id = x86_match_cpu(intel_pstate_cpu_ids);
|
|
if (!id)
|
|
return -ENODEV;
|
|
|
|
- /*
|
|
- * The Intel pstate driver will be ignored if the platform
|
|
- * firmware has its own power management modes.
|
|
- */
|
|
- if (intel_pstate_platform_pwr_mgmt_exists())
|
|
- return -ENODEV;
|
|
-
|
|
cpu_def = (struct cpu_defaults *)id->driver_data;
|
|
|
|
copy_pid_params(&cpu_def->pid_policy);
|
|
@@ -1389,17 +1393,20 @@ static int __init intel_pstate_init(void)
|
|
if (intel_pstate_msrs_not_valid())
|
|
return -ENODEV;
|
|
|
|
+hwp_cpu_matched:
|
|
+ /*
|
|
+ * The Intel pstate driver will be ignored if the platform
|
|
+ * firmware has its own power management modes.
|
|
+ */
|
|
+ if (intel_pstate_platform_pwr_mgmt_exists())
|
|
+ return -ENODEV;
|
|
+
|
|
pr_info("Intel P-state driver initializing.\n");
|
|
|
|
all_cpu_data = vzalloc(sizeof(void *) * num_possible_cpus());
|
|
if (!all_cpu_data)
|
|
return -ENOMEM;
|
|
|
|
- if (static_cpu_has_safe(X86_FEATURE_HWP) && !no_hwp) {
|
|
- pr_info("intel_pstate: HWP enabled\n");
|
|
- hwp_active++;
|
|
- }
|
|
-
|
|
if (!hwp_active && hwp_only)
|
|
goto out;
|
|
|
|
@@ -1410,6 +1417,9 @@ static int __init intel_pstate_init(void)
|
|
intel_pstate_debug_expose_params();
|
|
intel_pstate_sysfs_expose_params();
|
|
|
|
+ if (hwp_active)
|
|
+ pr_info("intel_pstate: HWP enabled\n");
|
|
+
|
|
return rc;
|
|
out:
|
|
get_online_cpus();
|
|
diff --git a/drivers/cpufreq/powernv-cpufreq.c b/drivers/cpufreq/powernv-cpufreq.c
|
|
index c4b0ef65988c..57e6c45724e7 100644
|
|
--- a/drivers/cpufreq/powernv-cpufreq.c
|
|
+++ b/drivers/cpufreq/powernv-cpufreq.c
|
|
@@ -592,7 +592,7 @@ static int __init powernv_cpufreq_init(void)
|
|
int rc = 0;
|
|
|
|
/* Don't probe on pseries (guest) platforms */
|
|
- if (!firmware_has_feature(FW_FEATURE_OPALv3))
|
|
+ if (!firmware_has_feature(FW_FEATURE_OPAL))
|
|
return -ENODEV;
|
|
|
|
/* Discover pstates from device tree and init */
|
|
diff --git a/drivers/cpuidle/coupled.c b/drivers/cpuidle/coupled.c
|
|
index 344058f8501a..d5657d50ac40 100644
|
|
--- a/drivers/cpuidle/coupled.c
|
|
+++ b/drivers/cpuidle/coupled.c
|
|
@@ -119,7 +119,6 @@ struct cpuidle_coupled {
|
|
|
|
#define CPUIDLE_COUPLED_NOT_IDLE (-1)
|
|
|
|
-static DEFINE_MUTEX(cpuidle_coupled_lock);
|
|
static DEFINE_PER_CPU(struct call_single_data, cpuidle_coupled_poke_cb);
|
|
|
|
/*
|
|
diff --git a/drivers/cpuidle/cpuidle-powernv.c b/drivers/cpuidle/cpuidle-powernv.c
|
|
index d5c5a476360f..c44a843cb405 100644
|
|
--- a/drivers/cpuidle/cpuidle-powernv.c
|
|
+++ b/drivers/cpuidle/cpuidle-powernv.c
|
|
@@ -282,7 +282,7 @@ static int powernv_idle_probe(void)
|
|
if (cpuidle_disable != IDLE_NO_OVERRIDE)
|
|
return -ENODEV;
|
|
|
|
- if (firmware_has_feature(FW_FEATURE_OPALv3)) {
|
|
+ if (firmware_has_feature(FW_FEATURE_OPAL)) {
|
|
cpuidle_state_table = powernv_states;
|
|
/* Device tree can indicate more idle states */
|
|
max_idle_state = powernv_add_idle_states();
|
|
diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c
|
|
index 2a8122444614..9ba4aaa9f755 100644
|
|
--- a/drivers/gpio/gpio-rcar.c
|
|
+++ b/drivers/gpio/gpio-rcar.c
|
|
@@ -200,6 +200,48 @@ static int gpio_rcar_irq_set_wake(struct irq_data *d, unsigned int on)
|
|
return 0;
|
|
}
|
|
|
|
+static void gpio_rcar_irq_bus_lock(struct irq_data *d)
|
|
+{
|
|
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
|
+ struct gpio_rcar_priv *p = container_of(gc, struct gpio_rcar_priv,
|
|
+ gpio_chip);
|
|
+
|
|
+ pm_runtime_get_sync(&p->pdev->dev);
|
|
+}
|
|
+
|
|
+static void gpio_rcar_irq_bus_sync_unlock(struct irq_data *d)
|
|
+{
|
|
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
|
+ struct gpio_rcar_priv *p = container_of(gc, struct gpio_rcar_priv,
|
|
+ gpio_chip);
|
|
+
|
|
+ pm_runtime_put(&p->pdev->dev);
|
|
+}
|
|
+
|
|
+
|
|
+static int gpio_rcar_irq_request_resources(struct irq_data *d)
|
|
+{
|
|
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
|
+ struct gpio_rcar_priv *p = container_of(gc, struct gpio_rcar_priv,
|
|
+ gpio_chip);
|
|
+ int error;
|
|
+
|
|
+ error = pm_runtime_get_sync(&p->pdev->dev);
|
|
+ if (error < 0)
|
|
+ return error;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void gpio_rcar_irq_release_resources(struct irq_data *d)
|
|
+{
|
|
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
|
+ struct gpio_rcar_priv *p = container_of(gc, struct gpio_rcar_priv,
|
|
+ gpio_chip);
|
|
+
|
|
+ pm_runtime_put(&p->pdev->dev);
|
|
+}
|
|
+
|
|
static irqreturn_t gpio_rcar_irq_handler(int irq, void *dev_id)
|
|
{
|
|
struct gpio_rcar_priv *p = dev_id;
|
|
@@ -460,6 +502,10 @@ static int gpio_rcar_probe(struct platform_device *pdev)
|
|
irq_chip->irq_unmask = gpio_rcar_irq_enable;
|
|
irq_chip->irq_set_type = gpio_rcar_irq_set_type;
|
|
irq_chip->irq_set_wake = gpio_rcar_irq_set_wake;
|
|
+ irq_chip->irq_bus_lock = gpio_rcar_irq_bus_lock;
|
|
+ irq_chip->irq_bus_sync_unlock = gpio_rcar_irq_bus_sync_unlock;
|
|
+ irq_chip->irq_request_resources = gpio_rcar_irq_request_resources;
|
|
+ irq_chip->irq_release_resources = gpio_rcar_irq_release_resources;
|
|
irq_chip->flags = IRQCHIP_SET_TYPE_MASKED | IRQCHIP_MASK_ON_SUSPEND;
|
|
|
|
ret = gpiochip_add(gpio_chip);
|
|
diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c
|
|
index bb9e9fc45e1b..82d23bd3a742 100644
|
|
--- a/drivers/net/bonding/bond_alb.c
|
|
+++ b/drivers/net/bonding/bond_alb.c
|
|
@@ -453,7 +453,7 @@ static void rlb_update_client(struct rlb_client_info *client_info)
|
|
{
|
|
int i;
|
|
|
|
- if (!client_info->slave)
|
|
+ if (!client_info->slave || !is_valid_ether_addr(client_info->mac_dst))
|
|
return;
|
|
|
|
for (i = 0; i < RLB_ARP_BURST_SIZE; i++) {
|
|
diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c
|
|
index 3bba92fc9c1a..1325825d5225 100644
|
|
--- a/drivers/net/ethernet/broadcom/tg3.c
|
|
+++ b/drivers/net/ethernet/broadcom/tg3.c
|
|
@@ -8722,14 +8722,15 @@ static void tg3_free_consistent(struct tg3 *tp)
|
|
tg3_mem_rx_release(tp);
|
|
tg3_mem_tx_release(tp);
|
|
|
|
- /* Protect tg3_get_stats64() from reading freed tp->hw_stats. */
|
|
- tg3_full_lock(tp, 0);
|
|
+ /* tp->hw_stats can be referenced safely:
|
|
+ * 1. under rtnl_lock
|
|
+ * 2. or under tp->lock if TG3_FLAG_INIT_COMPLETE is set.
|
|
+ */
|
|
if (tp->hw_stats) {
|
|
dma_free_coherent(&tp->pdev->dev, sizeof(struct tg3_hw_stats),
|
|
tp->hw_stats, tp->stats_mapping);
|
|
tp->hw_stats = NULL;
|
|
}
|
|
- tg3_full_unlock(tp);
|
|
}
|
|
|
|
/*
|
|
@@ -14163,7 +14164,7 @@ static struct rtnl_link_stats64 *tg3_get_stats64(struct net_device *dev,
|
|
struct tg3 *tp = netdev_priv(dev);
|
|
|
|
spin_lock_bh(&tp->lock);
|
|
- if (!tp->hw_stats) {
|
|
+ if (!tp->hw_stats || !tg3_flag(tp, INIT_COMPLETE)) {
|
|
*stats = tp->net_stats_prev;
|
|
spin_unlock_bh(&tp->lock);
|
|
return stats;
|
|
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
|
|
index ddb5541882f5..bcfac000199e 100644
|
|
--- a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
|
|
+++ b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
|
|
@@ -967,6 +967,22 @@ static int mlx4_en_set_coalesce(struct net_device *dev,
|
|
if (!coal->tx_max_coalesced_frames_irq)
|
|
return -EINVAL;
|
|
|
|
+ if (coal->tx_coalesce_usecs > MLX4_EN_MAX_COAL_TIME ||
|
|
+ coal->rx_coalesce_usecs > MLX4_EN_MAX_COAL_TIME ||
|
|
+ coal->rx_coalesce_usecs_low > MLX4_EN_MAX_COAL_TIME ||
|
|
+ coal->rx_coalesce_usecs_high > MLX4_EN_MAX_COAL_TIME) {
|
|
+ netdev_info(dev, "%s: maximum coalesce time supported is %d usecs\n",
|
|
+ __func__, MLX4_EN_MAX_COAL_TIME);
|
|
+ return -ERANGE;
|
|
+ }
|
|
+
|
|
+ if (coal->tx_max_coalesced_frames > MLX4_EN_MAX_COAL_PKTS ||
|
|
+ coal->rx_max_coalesced_frames > MLX4_EN_MAX_COAL_PKTS) {
|
|
+ netdev_info(dev, "%s: maximum coalesced frames supported is %d\n",
|
|
+ __func__, MLX4_EN_MAX_COAL_PKTS);
|
|
+ return -ERANGE;
|
|
+ }
|
|
+
|
|
priv->rx_frames = (coal->rx_max_coalesced_frames ==
|
|
MLX4_EN_AUTO_CONF) ?
|
|
MLX4_EN_RX_COAL_TARGET :
|
|
diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
|
|
index 10aa6544cf4d..607daaffae98 100644
|
|
--- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
|
|
+++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
|
|
@@ -140,6 +140,9 @@ enum {
|
|
#define MLX4_EN_TX_COAL_PKTS 16
|
|
#define MLX4_EN_TX_COAL_TIME 0x10
|
|
|
|
+#define MLX4_EN_MAX_COAL_PKTS U16_MAX
|
|
+#define MLX4_EN_MAX_COAL_TIME U16_MAX
|
|
+
|
|
#define MLX4_EN_RX_RATE_LOW 400000
|
|
#define MLX4_EN_RX_COAL_TIME_LOW 0
|
|
#define MLX4_EN_RX_RATE_HIGH 450000
|
|
@@ -518,8 +521,8 @@ struct mlx4_en_priv {
|
|
u16 rx_usecs_low;
|
|
u32 pkt_rate_high;
|
|
u16 rx_usecs_high;
|
|
- u16 sample_interval;
|
|
- u16 adaptive_rx_coal;
|
|
+ u32 sample_interval;
|
|
+ u32 adaptive_rx_coal;
|
|
u32 msg_enable;
|
|
u32 loopback_ok;
|
|
u32 validate_loopback;
|
|
diff --git a/drivers/net/ethernet/realtek/8139too.c b/drivers/net/ethernet/realtek/8139too.c
|
|
index ef668d300800..d987d571fdd6 100644
|
|
--- a/drivers/net/ethernet/realtek/8139too.c
|
|
+++ b/drivers/net/ethernet/realtek/8139too.c
|
|
@@ -2229,7 +2229,7 @@ static void rtl8139_poll_controller(struct net_device *dev)
|
|
struct rtl8139_private *tp = netdev_priv(dev);
|
|
const int irq = tp->pci_dev->irq;
|
|
|
|
- disable_irq(irq);
|
|
+ disable_irq_nosync(irq);
|
|
rtl8139_interrupt(irq, dev);
|
|
enable_irq(irq);
|
|
}
|
|
diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c
|
|
index a82c89af7124..8b4069ea52ce 100644
|
|
--- a/drivers/net/ethernet/realtek/r8169.c
|
|
+++ b/drivers/net/ethernet/realtek/r8169.c
|
|
@@ -4832,6 +4832,9 @@ static void rtl_pll_power_down(struct rtl8169_private *tp)
|
|
static void rtl_pll_power_up(struct rtl8169_private *tp)
|
|
{
|
|
rtl_generic_op(tp, tp->pll_power_ops.up);
|
|
+
|
|
+ /* give MAC/PHY some time to resume */
|
|
+ msleep(20);
|
|
}
|
|
|
|
static void rtl_init_pll_power_ops(struct rtl8169_private *tp)
|
|
diff --git a/drivers/net/ethernet/sun/niu.c b/drivers/net/ethernet/sun/niu.c
|
|
index ab6051a43134..ccebf89aa1e4 100644
|
|
--- a/drivers/net/ethernet/sun/niu.c
|
|
+++ b/drivers/net/ethernet/sun/niu.c
|
|
@@ -3442,7 +3442,7 @@ static int niu_process_rx_pkt(struct napi_struct *napi, struct niu *np,
|
|
|
|
len = (val & RCR_ENTRY_L2_LEN) >>
|
|
RCR_ENTRY_L2_LEN_SHIFT;
|
|
- len -= ETH_FCS_LEN;
|
|
+ append_size = len + ETH_HLEN + ETH_FCS_LEN;
|
|
|
|
addr = (val & RCR_ENTRY_PKT_BUF_ADDR) <<
|
|
RCR_ENTRY_PKT_BUF_ADDR_SHIFT;
|
|
@@ -3452,7 +3452,6 @@ static int niu_process_rx_pkt(struct napi_struct *napi, struct niu *np,
|
|
RCR_ENTRY_PKTBUFSZ_SHIFT];
|
|
|
|
off = addr & ~PAGE_MASK;
|
|
- append_size = rcr_size;
|
|
if (num_rcr == 1) {
|
|
int ptype;
|
|
|
|
@@ -3465,7 +3464,7 @@ static int niu_process_rx_pkt(struct napi_struct *napi, struct niu *np,
|
|
else
|
|
skb_checksum_none_assert(skb);
|
|
} else if (!(val & RCR_ENTRY_MULTI))
|
|
- append_size = len - skb->len;
|
|
+ append_size = append_size - skb->len;
|
|
|
|
niu_rx_skb_append(skb, page, off, append_size, rcr_size);
|
|
if ((page->index + rp->rbr_block_size) - rcr_size == addr) {
|
|
diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
|
|
index a6f0a8f516d6..8aaa09b3c753 100644
|
|
--- a/drivers/net/usb/qmi_wwan.c
|
|
+++ b/drivers/net/usb/qmi_wwan.c
|
|
@@ -855,6 +855,18 @@ static int qmi_wwan_probe(struct usb_interface *intf,
|
|
id->driver_info = (unsigned long)&qmi_wwan_info;
|
|
}
|
|
|
|
+ /* There are devices where the same interface number can be
|
|
+ * configured as different functions. We should only bind to
|
|
+ * vendor specific functions when matching on interface number
|
|
+ */
|
|
+ if (id->match_flags & USB_DEVICE_ID_MATCH_INT_NUMBER &&
|
|
+ desc->bInterfaceClass != USB_CLASS_VENDOR_SPEC) {
|
|
+ dev_dbg(&intf->dev,
|
|
+ "Rejecting interface number match for class %02x\n",
|
|
+ desc->bInterfaceClass);
|
|
+ return -ENODEV;
|
|
+ }
|
|
+
|
|
/* Quectel EC20 quirk where we've QMI on interface 4 instead of 0 */
|
|
if (quectel_ec20_detected(intf) && desc->bInterfaceNumber == 0) {
|
|
dev_dbg(&intf->dev, "Quectel EC20 quirk, skipping interface 0\n");
|
|
diff --git a/drivers/s390/cio/qdio_setup.c b/drivers/s390/cio/qdio_setup.c
|
|
index 48b3866a9ded..35286907c636 100644
|
|
--- a/drivers/s390/cio/qdio_setup.c
|
|
+++ b/drivers/s390/cio/qdio_setup.c
|
|
@@ -140,7 +140,7 @@ static int __qdio_allocate_qs(struct qdio_q **irq_ptr_qs, int nr_queues)
|
|
int i;
|
|
|
|
for (i = 0; i < nr_queues; i++) {
|
|
- q = kmem_cache_alloc(qdio_q_cache, GFP_KERNEL);
|
|
+ q = kmem_cache_zalloc(qdio_q_cache, GFP_KERNEL);
|
|
if (!q)
|
|
return -ENOMEM;
|
|
|
|
@@ -456,7 +456,6 @@ int qdio_setup_irq(struct qdio_initialize *init_data)
|
|
{
|
|
struct ciw *ciw;
|
|
struct qdio_irq *irq_ptr = init_data->cdev->private->qdio_data;
|
|
- int rc;
|
|
|
|
memset(&irq_ptr->qib, 0, sizeof(irq_ptr->qib));
|
|
memset(&irq_ptr->siga_flag, 0, sizeof(irq_ptr->siga_flag));
|
|
@@ -493,16 +492,14 @@ int qdio_setup_irq(struct qdio_initialize *init_data)
|
|
ciw = ccw_device_get_ciw(init_data->cdev, CIW_TYPE_EQUEUE);
|
|
if (!ciw) {
|
|
DBF_ERROR("%4x NO EQ", irq_ptr->schid.sch_no);
|
|
- rc = -EINVAL;
|
|
- goto out_err;
|
|
+ return -EINVAL;
|
|
}
|
|
irq_ptr->equeue = *ciw;
|
|
|
|
ciw = ccw_device_get_ciw(init_data->cdev, CIW_TYPE_AQUEUE);
|
|
if (!ciw) {
|
|
DBF_ERROR("%4x NO AQ", irq_ptr->schid.sch_no);
|
|
- rc = -EINVAL;
|
|
- goto out_err;
|
|
+ return -EINVAL;
|
|
}
|
|
irq_ptr->aqueue = *ciw;
|
|
|
|
@@ -510,9 +507,6 @@ int qdio_setup_irq(struct qdio_initialize *init_data)
|
|
irq_ptr->orig_handler = init_data->cdev->handler;
|
|
init_data->cdev->handler = qdio_int_handler;
|
|
return 0;
|
|
-out_err:
|
|
- qdio_release_memory(irq_ptr);
|
|
- return rc;
|
|
}
|
|
|
|
void qdio_print_subchannel_info(struct qdio_irq *irq_ptr,
|
|
diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c
|
|
index 34367d172961..4534a7ce77b8 100644
|
|
--- a/drivers/s390/scsi/zfcp_dbf.c
|
|
+++ b/drivers/s390/scsi/zfcp_dbf.c
|
|
@@ -3,7 +3,7 @@
|
|
*
|
|
* Debug traces for zfcp.
|
|
*
|
|
- * Copyright IBM Corp. 2002, 2017
|
|
+ * Copyright IBM Corp. 2002, 2018
|
|
*/
|
|
|
|
#define KMSG_COMPONENT "zfcp"
|
|
@@ -287,6 +287,27 @@ void zfcp_dbf_rec_trig(char *tag, struct zfcp_adapter *adapter,
|
|
spin_unlock_irqrestore(&dbf->rec_lock, flags);
|
|
}
|
|
|
|
+/**
|
|
+ * zfcp_dbf_rec_trig_lock - trace event related to triggered recovery with lock
|
|
+ * @tag: identifier for event
|
|
+ * @adapter: adapter on which the erp_action should run
|
|
+ * @port: remote port involved in the erp_action
|
|
+ * @sdev: scsi device involved in the erp_action
|
|
+ * @want: wanted erp_action
|
|
+ * @need: required erp_action
|
|
+ *
|
|
+ * The adapter->erp_lock must not be held.
|
|
+ */
|
|
+void zfcp_dbf_rec_trig_lock(char *tag, struct zfcp_adapter *adapter,
|
|
+ struct zfcp_port *port, struct scsi_device *sdev,
|
|
+ u8 want, u8 need)
|
|
+{
|
|
+ unsigned long flags;
|
|
+
|
|
+ read_lock_irqsave(&adapter->erp_lock, flags);
|
|
+ zfcp_dbf_rec_trig(tag, adapter, port, sdev, want, need);
|
|
+ read_unlock_irqrestore(&adapter->erp_lock, flags);
|
|
+}
|
|
|
|
/**
|
|
* zfcp_dbf_rec_run_lvl - trace event related to running recovery
|
|
diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h
|
|
index 21c8c689b02b..7a7984a50683 100644
|
|
--- a/drivers/s390/scsi/zfcp_ext.h
|
|
+++ b/drivers/s390/scsi/zfcp_ext.h
|
|
@@ -3,7 +3,7 @@
|
|
*
|
|
* External function declarations.
|
|
*
|
|
- * Copyright IBM Corp. 2002, 2016
|
|
+ * Copyright IBM Corp. 2002, 2018
|
|
*/
|
|
|
|
#ifndef ZFCP_EXT_H
|
|
@@ -34,6 +34,9 @@ extern int zfcp_dbf_adapter_register(struct zfcp_adapter *);
|
|
extern void zfcp_dbf_adapter_unregister(struct zfcp_adapter *);
|
|
extern void zfcp_dbf_rec_trig(char *, struct zfcp_adapter *,
|
|
struct zfcp_port *, struct scsi_device *, u8, u8);
|
|
+extern void zfcp_dbf_rec_trig_lock(char *tag, struct zfcp_adapter *adapter,
|
|
+ struct zfcp_port *port,
|
|
+ struct scsi_device *sdev, u8 want, u8 need);
|
|
extern void zfcp_dbf_rec_run(char *, struct zfcp_erp_action *);
|
|
extern void zfcp_dbf_rec_run_lvl(int level, char *tag,
|
|
struct zfcp_erp_action *erp);
|
|
diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c
|
|
index a9b8104b982e..bb99db2948ab 100644
|
|
--- a/drivers/s390/scsi/zfcp_scsi.c
|
|
+++ b/drivers/s390/scsi/zfcp_scsi.c
|
|
@@ -3,7 +3,7 @@
|
|
*
|
|
* Interface to Linux SCSI midlayer.
|
|
*
|
|
- * Copyright IBM Corp. 2002, 2017
|
|
+ * Copyright IBM Corp. 2002, 2018
|
|
*/
|
|
|
|
#define KMSG_COMPONENT "zfcp"
|
|
@@ -616,9 +616,9 @@ static void zfcp_scsi_rport_register(struct zfcp_port *port)
|
|
ids.port_id = port->d_id;
|
|
ids.roles = FC_RPORT_ROLE_FCP_TARGET;
|
|
|
|
- zfcp_dbf_rec_trig("scpaddy", port->adapter, port, NULL,
|
|
- ZFCP_PSEUDO_ERP_ACTION_RPORT_ADD,
|
|
- ZFCP_PSEUDO_ERP_ACTION_RPORT_ADD);
|
|
+ zfcp_dbf_rec_trig_lock("scpaddy", port->adapter, port, NULL,
|
|
+ ZFCP_PSEUDO_ERP_ACTION_RPORT_ADD,
|
|
+ ZFCP_PSEUDO_ERP_ACTION_RPORT_ADD);
|
|
rport = fc_remote_port_add(port->adapter->scsi_host, 0, &ids);
|
|
if (!rport) {
|
|
dev_err(&port->adapter->ccw_device->dev,
|
|
@@ -640,9 +640,9 @@ static void zfcp_scsi_rport_block(struct zfcp_port *port)
|
|
struct fc_rport *rport = port->rport;
|
|
|
|
if (rport) {
|
|
- zfcp_dbf_rec_trig("scpdely", port->adapter, port, NULL,
|
|
- ZFCP_PSEUDO_ERP_ACTION_RPORT_DEL,
|
|
- ZFCP_PSEUDO_ERP_ACTION_RPORT_DEL);
|
|
+ zfcp_dbf_rec_trig_lock("scpdely", port->adapter, port, NULL,
|
|
+ ZFCP_PSEUDO_ERP_ACTION_RPORT_DEL,
|
|
+ ZFCP_PSEUDO_ERP_ACTION_RPORT_DEL);
|
|
fc_remote_port_delete(rport);
|
|
port->rport = NULL;
|
|
}
|
|
diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c
|
|
index 519dac4e341e..9a8c2f97ed70 100644
|
|
--- a/drivers/scsi/libsas/sas_scsi_host.c
|
|
+++ b/drivers/scsi/libsas/sas_scsi_host.c
|
|
@@ -222,6 +222,7 @@ out_done:
|
|
static void sas_eh_finish_cmd(struct scsi_cmnd *cmd)
|
|
{
|
|
struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(cmd->device->host);
|
|
+ struct domain_device *dev = cmd_to_domain_dev(cmd);
|
|
struct sas_task *task = TO_SAS_TASK(cmd);
|
|
|
|
/* At this point, we only get called following an actual abort
|
|
@@ -230,6 +231,14 @@ static void sas_eh_finish_cmd(struct scsi_cmnd *cmd)
|
|
*/
|
|
sas_end_task(cmd, task);
|
|
|
|
+ if (dev_is_sata(dev)) {
|
|
+ /* defer commands to libata so that libata EH can
|
|
+ * handle ata qcs correctly
|
|
+ */
|
|
+ list_move_tail(&cmd->eh_entry, &sas_ha->eh_ata_q);
|
|
+ return;
|
|
+ }
|
|
+
|
|
/* now finish the command and move it on to the error
|
|
* handler done list, this also takes it off the
|
|
* error handler pending list.
|
|
@@ -237,22 +246,6 @@ static void sas_eh_finish_cmd(struct scsi_cmnd *cmd)
|
|
scsi_eh_finish_cmd(cmd, &sas_ha->eh_done_q);
|
|
}
|
|
|
|
-static void sas_eh_defer_cmd(struct scsi_cmnd *cmd)
|
|
-{
|
|
- struct domain_device *dev = cmd_to_domain_dev(cmd);
|
|
- struct sas_ha_struct *ha = dev->port->ha;
|
|
- struct sas_task *task = TO_SAS_TASK(cmd);
|
|
-
|
|
- if (!dev_is_sata(dev)) {
|
|
- sas_eh_finish_cmd(cmd);
|
|
- return;
|
|
- }
|
|
-
|
|
- /* report the timeout to libata */
|
|
- sas_end_task(cmd, task);
|
|
- list_move_tail(&cmd->eh_entry, &ha->eh_ata_q);
|
|
-}
|
|
-
|
|
static void sas_scsi_clear_queue_lu(struct list_head *error_q, struct scsi_cmnd *my_cmd)
|
|
{
|
|
struct scsi_cmnd *cmd, *n;
|
|
@@ -260,7 +253,7 @@ static void sas_scsi_clear_queue_lu(struct list_head *error_q, struct scsi_cmnd
|
|
list_for_each_entry_safe(cmd, n, error_q, eh_entry) {
|
|
if (cmd->device->sdev_target == my_cmd->device->sdev_target &&
|
|
cmd->device->lun == my_cmd->device->lun)
|
|
- sas_eh_defer_cmd(cmd);
|
|
+ sas_eh_finish_cmd(cmd);
|
|
}
|
|
}
|
|
|
|
@@ -622,12 +615,12 @@ static void sas_eh_handle_sas_errors(struct Scsi_Host *shost, struct list_head *
|
|
case TASK_IS_DONE:
|
|
SAS_DPRINTK("%s: task 0x%p is done\n", __func__,
|
|
task);
|
|
- sas_eh_defer_cmd(cmd);
|
|
+ sas_eh_finish_cmd(cmd);
|
|
continue;
|
|
case TASK_IS_ABORTED:
|
|
SAS_DPRINTK("%s: task 0x%p is aborted\n",
|
|
__func__, task);
|
|
- sas_eh_defer_cmd(cmd);
|
|
+ sas_eh_finish_cmd(cmd);
|
|
continue;
|
|
case TASK_IS_AT_LU:
|
|
SAS_DPRINTK("task 0x%p is at LU: lu recover\n", task);
|
|
@@ -638,7 +631,7 @@ static void sas_eh_handle_sas_errors(struct Scsi_Host *shost, struct list_head *
|
|
"recovered\n",
|
|
SAS_ADDR(task->dev),
|
|
cmd->device->lun);
|
|
- sas_eh_defer_cmd(cmd);
|
|
+ sas_eh_finish_cmd(cmd);
|
|
sas_scsi_clear_queue_lu(work_q, cmd);
|
|
goto Again;
|
|
}
|
|
diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c
|
|
index cb19c9ad1b57..841f3fbec77c 100644
|
|
--- a/drivers/scsi/sg.c
|
|
+++ b/drivers/scsi/sg.c
|
|
@@ -1903,7 +1903,7 @@ retry:
|
|
num = (rem_sz > scatter_elem_sz_prev) ?
|
|
scatter_elem_sz_prev : rem_sz;
|
|
|
|
- schp->pages[k] = alloc_pages(gfp_mask, order);
|
|
+ schp->pages[k] = alloc_pages(gfp_mask | __GFP_ZERO, order);
|
|
if (!schp->pages[k])
|
|
goto out;
|
|
|
|
diff --git a/drivers/spi/spi-pxa2xx.h b/drivers/spi/spi-pxa2xx.h
|
|
index 58efa98313aa..24c07fea9de2 100644
|
|
--- a/drivers/spi/spi-pxa2xx.h
|
|
+++ b/drivers/spi/spi-pxa2xx.h
|
|
@@ -38,7 +38,7 @@ struct driver_data {
|
|
|
|
/* SSP register addresses */
|
|
void __iomem *ioaddr;
|
|
- u32 ssdr_physical;
|
|
+ phys_addr_t ssdr_physical;
|
|
|
|
/* SSP masks*/
|
|
u32 dma_cr1;
|
|
diff --git a/drivers/usb/usbip/stub.h b/drivers/usb/usbip/stub.h
|
|
index 266e2b0ce9a8..47ccd73a74f0 100644
|
|
--- a/drivers/usb/usbip/stub.h
|
|
+++ b/drivers/usb/usbip/stub.h
|
|
@@ -88,6 +88,7 @@ struct bus_id_priv {
|
|
struct stub_device *sdev;
|
|
struct usb_device *udev;
|
|
char shutdown_busid;
|
|
+ spinlock_t busid_lock;
|
|
};
|
|
|
|
/* stub_priv is allocated from stub_priv_cache */
|
|
@@ -98,6 +99,7 @@ extern struct usb_device_driver stub_driver;
|
|
|
|
/* stub_main.c */
|
|
struct bus_id_priv *get_busid_priv(const char *busid);
|
|
+void put_busid_priv(struct bus_id_priv *bid);
|
|
int del_match_busid(char *busid);
|
|
void stub_device_cleanup_urbs(struct stub_device *sdev);
|
|
|
|
diff --git a/drivers/usb/usbip/stub_dev.c b/drivers/usb/usbip/stub_dev.c
|
|
index 0931f3271119..4aad99a59958 100644
|
|
--- a/drivers/usb/usbip/stub_dev.c
|
|
+++ b/drivers/usb/usbip/stub_dev.c
|
|
@@ -314,9 +314,9 @@ static int stub_probe(struct usb_device *udev)
|
|
struct stub_device *sdev = NULL;
|
|
const char *udev_busid = dev_name(&udev->dev);
|
|
struct bus_id_priv *busid_priv;
|
|
- int rc;
|
|
+ int rc = 0;
|
|
|
|
- dev_dbg(&udev->dev, "Enter\n");
|
|
+ dev_dbg(&udev->dev, "Enter probe\n");
|
|
|
|
/* check we should claim or not by busid_table */
|
|
busid_priv = get_busid_priv(udev_busid);
|
|
@@ -331,13 +331,15 @@ static int stub_probe(struct usb_device *udev)
|
|
* other matched drivers by the driver core.
|
|
* See driver_probe_device() in driver/base/dd.c
|
|
*/
|
|
- return -ENODEV;
|
|
+ rc = -ENODEV;
|
|
+ goto call_put_busid_priv;
|
|
}
|
|
|
|
if (udev->descriptor.bDeviceClass == USB_CLASS_HUB) {
|
|
dev_dbg(&udev->dev, "%s is a usb hub device... skip!\n",
|
|
udev_busid);
|
|
- return -ENODEV;
|
|
+ rc = -ENODEV;
|
|
+ goto call_put_busid_priv;
|
|
}
|
|
|
|
if (!strcmp(udev->bus->bus_name, "vhci_hcd")) {
|
|
@@ -345,13 +347,16 @@ static int stub_probe(struct usb_device *udev)
|
|
"%s is attached on vhci_hcd... skip!\n",
|
|
udev_busid);
|
|
|
|
- return -ENODEV;
|
|
+ rc = -ENODEV;
|
|
+ goto call_put_busid_priv;
|
|
}
|
|
|
|
/* ok, this is my device */
|
|
sdev = stub_device_alloc(udev);
|
|
- if (!sdev)
|
|
- return -ENOMEM;
|
|
+ if (!sdev) {
|
|
+ rc = -ENOMEM;
|
|
+ goto call_put_busid_priv;
|
|
+ }
|
|
|
|
dev_info(&udev->dev,
|
|
"usbip-host: register new device (bus %u dev %u)\n",
|
|
@@ -383,7 +388,9 @@ static int stub_probe(struct usb_device *udev)
|
|
}
|
|
busid_priv->status = STUB_BUSID_ALLOC;
|
|
|
|
- return 0;
|
|
+ rc = 0;
|
|
+ goto call_put_busid_priv;
|
|
+
|
|
err_files:
|
|
usb_hub_release_port(udev->parent, udev->portnum,
|
|
(struct usb_dev_state *) udev);
|
|
@@ -394,6 +401,9 @@ err_port:
|
|
|
|
busid_priv->sdev = NULL;
|
|
stub_device_free(sdev);
|
|
+
|
|
+call_put_busid_priv:
|
|
+ put_busid_priv(busid_priv);
|
|
return rc;
|
|
}
|
|
|
|
@@ -419,7 +429,7 @@ static void stub_disconnect(struct usb_device *udev)
|
|
struct bus_id_priv *busid_priv;
|
|
int rc;
|
|
|
|
- dev_dbg(&udev->dev, "Enter\n");
|
|
+ dev_dbg(&udev->dev, "Enter disconnect\n");
|
|
|
|
busid_priv = get_busid_priv(udev_busid);
|
|
if (!busid_priv) {
|
|
@@ -432,7 +442,7 @@ static void stub_disconnect(struct usb_device *udev)
|
|
/* get stub_device */
|
|
if (!sdev) {
|
|
dev_err(&udev->dev, "could not get device");
|
|
- return;
|
|
+ goto call_put_busid_priv;
|
|
}
|
|
|
|
dev_set_drvdata(&udev->dev, NULL);
|
|
@@ -447,12 +457,12 @@ static void stub_disconnect(struct usb_device *udev)
|
|
(struct usb_dev_state *) udev);
|
|
if (rc) {
|
|
dev_dbg(&udev->dev, "unable to release port\n");
|
|
- return;
|
|
+ goto call_put_busid_priv;
|
|
}
|
|
|
|
/* If usb reset is called from event handler */
|
|
if (busid_priv->sdev->ud.eh == current)
|
|
- return;
|
|
+ goto call_put_busid_priv;
|
|
|
|
/* shutdown the current connection */
|
|
shutdown_busid(busid_priv);
|
|
@@ -463,12 +473,11 @@ static void stub_disconnect(struct usb_device *udev)
|
|
busid_priv->sdev = NULL;
|
|
stub_device_free(sdev);
|
|
|
|
- if (busid_priv->status == STUB_BUSID_ALLOC) {
|
|
+ if (busid_priv->status == STUB_BUSID_ALLOC)
|
|
busid_priv->status = STUB_BUSID_ADDED;
|
|
- } else {
|
|
- busid_priv->status = STUB_BUSID_OTHER;
|
|
- del_match_busid((char *)udev_busid);
|
|
- }
|
|
+
|
|
+call_put_busid_priv:
|
|
+ put_busid_priv(busid_priv);
|
|
}
|
|
|
|
#ifdef CONFIG_PM
|
|
diff --git a/drivers/usb/usbip/stub_main.c b/drivers/usb/usbip/stub_main.c
|
|
index f761e02e75c9..fa90496ca7a8 100644
|
|
--- a/drivers/usb/usbip/stub_main.c
|
|
+++ b/drivers/usb/usbip/stub_main.c
|
|
@@ -28,6 +28,7 @@
|
|
#define DRIVER_DESC "USB/IP Host Driver"
|
|
|
|
struct kmem_cache *stub_priv_cache;
|
|
+
|
|
/*
|
|
* busid_tables defines matching busids that usbip can grab. A user can change
|
|
* dynamically what device is locally used and what device is exported to a
|
|
@@ -39,6 +40,8 @@ static spinlock_t busid_table_lock;
|
|
|
|
static void init_busid_table(void)
|
|
{
|
|
+ int i;
|
|
+
|
|
/*
|
|
* This also sets the bus_table[i].status to
|
|
* STUB_BUSID_OTHER, which is 0.
|
|
@@ -46,6 +49,9 @@ static void init_busid_table(void)
|
|
memset(busid_table, 0, sizeof(busid_table));
|
|
|
|
spin_lock_init(&busid_table_lock);
|
|
+
|
|
+ for (i = 0; i < MAX_BUSID; i++)
|
|
+ spin_lock_init(&busid_table[i].busid_lock);
|
|
}
|
|
|
|
/*
|
|
@@ -57,15 +63,20 @@ static int get_busid_idx(const char *busid)
|
|
int i;
|
|
int idx = -1;
|
|
|
|
- for (i = 0; i < MAX_BUSID; i++)
|
|
+ for (i = 0; i < MAX_BUSID; i++) {
|
|
+ spin_lock(&busid_table[i].busid_lock);
|
|
if (busid_table[i].name[0])
|
|
if (!strncmp(busid_table[i].name, busid, BUSID_SIZE)) {
|
|
idx = i;
|
|
+ spin_unlock(&busid_table[i].busid_lock);
|
|
break;
|
|
}
|
|
+ spin_unlock(&busid_table[i].busid_lock);
|
|
+ }
|
|
return idx;
|
|
}
|
|
|
|
+/* Returns holding busid_lock. Should call put_busid_priv() to unlock */
|
|
struct bus_id_priv *get_busid_priv(const char *busid)
|
|
{
|
|
int idx;
|
|
@@ -73,13 +84,22 @@ struct bus_id_priv *get_busid_priv(const char *busid)
|
|
|
|
spin_lock(&busid_table_lock);
|
|
idx = get_busid_idx(busid);
|
|
- if (idx >= 0)
|
|
+ if (idx >= 0) {
|
|
bid = &(busid_table[idx]);
|
|
+ /* get busid_lock before returning */
|
|
+ spin_lock(&bid->busid_lock);
|
|
+ }
|
|
spin_unlock(&busid_table_lock);
|
|
|
|
return bid;
|
|
}
|
|
|
|
+void put_busid_priv(struct bus_id_priv *bid)
|
|
+{
|
|
+ if (bid)
|
|
+ spin_unlock(&bid->busid_lock);
|
|
+}
|
|
+
|
|
static int add_match_busid(char *busid)
|
|
{
|
|
int i;
|
|
@@ -92,15 +112,19 @@ static int add_match_busid(char *busid)
|
|
goto out;
|
|
}
|
|
|
|
- for (i = 0; i < MAX_BUSID; i++)
|
|
+ for (i = 0; i < MAX_BUSID; i++) {
|
|
+ spin_lock(&busid_table[i].busid_lock);
|
|
if (!busid_table[i].name[0]) {
|
|
strlcpy(busid_table[i].name, busid, BUSID_SIZE);
|
|
if ((busid_table[i].status != STUB_BUSID_ALLOC) &&
|
|
(busid_table[i].status != STUB_BUSID_REMOV))
|
|
busid_table[i].status = STUB_BUSID_ADDED;
|
|
ret = 0;
|
|
+ spin_unlock(&busid_table[i].busid_lock);
|
|
break;
|
|
}
|
|
+ spin_unlock(&busid_table[i].busid_lock);
|
|
+ }
|
|
|
|
out:
|
|
spin_unlock(&busid_table_lock);
|
|
@@ -121,6 +145,8 @@ int del_match_busid(char *busid)
|
|
/* found */
|
|
ret = 0;
|
|
|
|
+ spin_lock(&busid_table[idx].busid_lock);
|
|
+
|
|
if (busid_table[idx].status == STUB_BUSID_OTHER)
|
|
memset(busid_table[idx].name, 0, BUSID_SIZE);
|
|
|
|
@@ -128,6 +154,7 @@ int del_match_busid(char *busid)
|
|
(busid_table[idx].status != STUB_BUSID_ADDED))
|
|
busid_table[idx].status = STUB_BUSID_REMOV;
|
|
|
|
+ spin_unlock(&busid_table[idx].busid_lock);
|
|
out:
|
|
spin_unlock(&busid_table_lock);
|
|
|
|
@@ -140,9 +167,12 @@ static ssize_t show_match_busid(struct device_driver *drv, char *buf)
|
|
char *out = buf;
|
|
|
|
spin_lock(&busid_table_lock);
|
|
- for (i = 0; i < MAX_BUSID; i++)
|
|
+ for (i = 0; i < MAX_BUSID; i++) {
|
|
+ spin_lock(&busid_table[i].busid_lock);
|
|
if (busid_table[i].name[0])
|
|
out += sprintf(out, "%s ", busid_table[i].name);
|
|
+ spin_unlock(&busid_table[i].busid_lock);
|
|
+ }
|
|
spin_unlock(&busid_table_lock);
|
|
out += sprintf(out, "\n");
|
|
|
|
@@ -184,6 +214,51 @@ static ssize_t store_match_busid(struct device_driver *dev, const char *buf,
|
|
static DRIVER_ATTR(match_busid, S_IRUSR | S_IWUSR, show_match_busid,
|
|
store_match_busid);
|
|
|
|
+static int do_rebind(char *busid, struct bus_id_priv *busid_priv)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ /* device_attach() callers should hold parent lock for USB */
|
|
+ if (busid_priv->udev->dev.parent)
|
|
+ device_lock(busid_priv->udev->dev.parent);
|
|
+ ret = device_attach(&busid_priv->udev->dev);
|
|
+ if (busid_priv->udev->dev.parent)
|
|
+ device_unlock(busid_priv->udev->dev.parent);
|
|
+ if (ret < 0) {
|
|
+ dev_err(&busid_priv->udev->dev, "rebind failed\n");
|
|
+ return ret;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void stub_device_rebind(void)
|
|
+{
|
|
+#if IS_MODULE(CONFIG_USBIP_HOST)
|
|
+ struct bus_id_priv *busid_priv;
|
|
+ int i;
|
|
+
|
|
+ /* update status to STUB_BUSID_OTHER so probe ignores the device */
|
|
+ spin_lock(&busid_table_lock);
|
|
+ for (i = 0; i < MAX_BUSID; i++) {
|
|
+ if (busid_table[i].name[0] &&
|
|
+ busid_table[i].shutdown_busid) {
|
|
+ busid_priv = &(busid_table[i]);
|
|
+ busid_priv->status = STUB_BUSID_OTHER;
|
|
+ }
|
|
+ }
|
|
+ spin_unlock(&busid_table_lock);
|
|
+
|
|
+ /* now run rebind - no need to hold locks. driver files are removed */
|
|
+ for (i = 0; i < MAX_BUSID; i++) {
|
|
+ if (busid_table[i].name[0] &&
|
|
+ busid_table[i].shutdown_busid) {
|
|
+ busid_priv = &(busid_table[i]);
|
|
+ do_rebind(busid_table[i].name, busid_priv);
|
|
+ }
|
|
+ }
|
|
+#endif
|
|
+}
|
|
+
|
|
static ssize_t rebind_store(struct device_driver *dev, const char *buf,
|
|
size_t count)
|
|
{
|
|
@@ -201,16 +276,17 @@ static ssize_t rebind_store(struct device_driver *dev, const char *buf,
|
|
if (!bid)
|
|
return -ENODEV;
|
|
|
|
- /* device_attach() callers should hold parent lock for USB */
|
|
- if (bid->udev->dev.parent)
|
|
- device_lock(bid->udev->dev.parent);
|
|
- ret = device_attach(&bid->udev->dev);
|
|
- if (bid->udev->dev.parent)
|
|
- device_unlock(bid->udev->dev.parent);
|
|
- if (ret < 0) {
|
|
- dev_err(&bid->udev->dev, "rebind failed\n");
|
|
+ /* mark the device for deletion so probe ignores it during rescan */
|
|
+ bid->status = STUB_BUSID_OTHER;
|
|
+ /* release the busid lock */
|
|
+ put_busid_priv(bid);
|
|
+
|
|
+ ret = do_rebind((char *) buf, bid);
|
|
+ if (ret < 0)
|
|
return ret;
|
|
- }
|
|
+
|
|
+ /* delete device from busid_table */
|
|
+ del_match_busid((char *) buf);
|
|
|
|
return count;
|
|
}
|
|
@@ -333,6 +409,9 @@ static void __exit usbip_host_exit(void)
|
|
*/
|
|
usb_deregister_device_driver(&stub_driver);
|
|
|
|
+ /* initiate scan to attach devices */
|
|
+ stub_device_rebind();
|
|
+
|
|
kmem_cache_destroy(stub_priv_cache);
|
|
}
|
|
|
|
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c
|
|
index 0f2b7c622ce3..e2f5be261532 100644
|
|
--- a/fs/btrfs/ctree.c
|
|
+++ b/fs/btrfs/ctree.c
|
|
@@ -2497,10 +2497,8 @@ read_block_for_search(struct btrfs_trans_handle *trans,
|
|
if (p->reada)
|
|
reada_for_search(root, p, level, slot, key->objectid);
|
|
|
|
- btrfs_release_path(p);
|
|
-
|
|
ret = -EAGAIN;
|
|
- tmp = read_tree_block(root, blocknr, 0);
|
|
+ tmp = read_tree_block(root, blocknr, gen);
|
|
if (!IS_ERR(tmp)) {
|
|
/*
|
|
* If the read above didn't mark this buffer up to date,
|
|
@@ -2512,6 +2510,8 @@ read_block_for_search(struct btrfs_trans_handle *trans,
|
|
ret = -EIO;
|
|
free_extent_buffer(tmp);
|
|
}
|
|
+
|
|
+ btrfs_release_path(p);
|
|
return ret;
|
|
}
|
|
|
|
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
|
|
index d6359af9789d..6ba022ed4a52 100644
|
|
--- a/fs/btrfs/tree-log.c
|
|
+++ b/fs/btrfs/tree-log.c
|
|
@@ -4568,6 +4568,7 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans,
|
|
struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree;
|
|
u64 logged_isize = 0;
|
|
bool need_log_inode_item = true;
|
|
+ bool xattrs_logged = false;
|
|
|
|
path = btrfs_alloc_path();
|
|
if (!path)
|
|
@@ -4808,6 +4809,7 @@ next_slot:
|
|
err = btrfs_log_all_xattrs(trans, root, inode, path, dst_path);
|
|
if (err)
|
|
goto out_unlock;
|
|
+ xattrs_logged = true;
|
|
if (max_key.type >= BTRFS_EXTENT_DATA_KEY && !fast_search) {
|
|
btrfs_release_path(path);
|
|
btrfs_release_path(dst_path);
|
|
@@ -4820,6 +4822,11 @@ log_extents:
|
|
btrfs_release_path(dst_path);
|
|
if (need_log_inode_item) {
|
|
err = log_inode_item(trans, log, dst_path, inode);
|
|
+ if (!err && !xattrs_logged) {
|
|
+ err = btrfs_log_all_xattrs(trans, root, inode, path,
|
|
+ dst_path);
|
|
+ btrfs_release_path(path);
|
|
+ }
|
|
if (err)
|
|
goto out_unlock;
|
|
}
|
|
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
|
|
index 6d874b1cd53c..ed75d70b4bc2 100644
|
|
--- a/fs/btrfs/volumes.c
|
|
+++ b/fs/btrfs/volumes.c
|
|
@@ -3850,6 +3850,15 @@ int btrfs_resume_balance_async(struct btrfs_fs_info *fs_info)
|
|
return 0;
|
|
}
|
|
|
|
+ /*
|
|
+ * A ro->rw remount sequence should continue with the paused balance
|
|
+ * regardless of who pauses it, system or the user as of now, so set
|
|
+ * the resume flag.
|
|
+ */
|
|
+ spin_lock(&fs_info->balance_lock);
|
|
+ fs_info->balance_ctl->flags |= BTRFS_BALANCE_RESUME;
|
|
+ spin_unlock(&fs_info->balance_lock);
|
|
+
|
|
tsk = kthread_run(balance_kthread, fs_info, "btrfs-balance");
|
|
return PTR_ERR_OR_ZERO(tsk);
|
|
}
|
|
diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c
|
|
index 0aa9bf6e6e53..f600c43f0047 100644
|
|
--- a/fs/ext2/inode.c
|
|
+++ b/fs/ext2/inode.c
|
|
@@ -1175,21 +1175,11 @@ do_indirects:
|
|
|
|
static void ext2_truncate_blocks(struct inode *inode, loff_t offset)
|
|
{
|
|
- /*
|
|
- * XXX: it seems like a bug here that we don't allow
|
|
- * IS_APPEND inode to have blocks-past-i_size trimmed off.
|
|
- * review and fix this.
|
|
- *
|
|
- * Also would be nice to be able to handle IO errors and such,
|
|
- * but that's probably too much to ask.
|
|
- */
|
|
if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
|
|
S_ISLNK(inode->i_mode)))
|
|
return;
|
|
if (ext2_inode_is_fast_symlink(inode))
|
|
return;
|
|
- if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
|
|
- return;
|
|
|
|
dax_sem_down_write(EXT2_I(inode));
|
|
__ext2_truncate_blocks(inode, offset);
|
|
diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c
|
|
index 7302d96ae8bf..fa40e756c501 100644
|
|
--- a/fs/hfsplus/super.c
|
|
+++ b/fs/hfsplus/super.c
|
|
@@ -585,6 +585,7 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent)
|
|
return 0;
|
|
|
|
out_put_hidden_dir:
|
|
+ cancel_delayed_work_sync(&sbi->sync_work);
|
|
iput(sbi->hidden_dir);
|
|
out_put_root:
|
|
dput(sb->s_root);
|
|
diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c
|
|
index a2edb0049eb5..f038d4ac9aec 100644
|
|
--- a/fs/lockd/svc.c
|
|
+++ b/fs/lockd/svc.c
|
|
@@ -271,6 +271,8 @@ static void lockd_down_net(struct svc_serv *serv, struct net *net)
|
|
if (ln->nlmsvc_users) {
|
|
if (--ln->nlmsvc_users == 0) {
|
|
nlm_shutdown_hosts_net(net);
|
|
+ cancel_delayed_work_sync(&ln->grace_period_end);
|
|
+ locks_end_grace(&ln->lockd_manager);
|
|
svc_shutdown_net(serv, net);
|
|
dprintk("lockd_down_net: per-net data destroyed; net=%p\n", net);
|
|
}
|
|
diff --git a/fs/pipe.c b/fs/pipe.c
|
|
index 39eff9a67253..1e7263bb837a 100644
|
|
--- a/fs/pipe.c
|
|
+++ b/fs/pipe.c
|
|
@@ -616,6 +616,9 @@ struct pipe_inode_info *alloc_pipe_info(void)
|
|
unsigned long pipe_bufs = PIPE_DEF_BUFFERS;
|
|
struct user_struct *user = get_current_user();
|
|
|
|
+ if (pipe_bufs * PAGE_SIZE > pipe_max_size && !capable(CAP_SYS_RESOURCE))
|
|
+ pipe_bufs = pipe_max_size >> PAGE_SHIFT;
|
|
+
|
|
if (!too_many_pipe_buffers_hard(user)) {
|
|
if (too_many_pipe_buffers_soft(user))
|
|
pipe_bufs = 1;
|
|
diff --git a/fs/proc/meminfo.c b/fs/proc/meminfo.c
|
|
index 9155a5a0d3b9..df4661abadc4 100644
|
|
--- a/fs/proc/meminfo.c
|
|
+++ b/fs/proc/meminfo.c
|
|
@@ -57,11 +57,8 @@ static int meminfo_proc_show(struct seq_file *m, void *v)
|
|
/*
|
|
* Estimate the amount of memory available for userspace allocations,
|
|
* without causing swapping.
|
|
- *
|
|
- * Free memory cannot be taken below the low watermark, before the
|
|
- * system starts swapping.
|
|
*/
|
|
- available = i.freeram - wmark_low;
|
|
+ available = i.freeram - totalreserve_pages;
|
|
|
|
/*
|
|
* Not all the page cache can be freed, otherwise the system will
|
|
diff --git a/include/asm-generic/futex.h b/include/asm-generic/futex.h
|
|
index bf2d34c9d804..f0d8b1c51343 100644
|
|
--- a/include/asm-generic/futex.h
|
|
+++ b/include/asm-generic/futex.h
|
|
@@ -13,7 +13,7 @@
|
|
*/
|
|
|
|
/**
|
|
- * futex_atomic_op_inuser() - Atomic arithmetic operation with constant
|
|
+ * arch_futex_atomic_op_inuser() - Atomic arithmetic operation with constant
|
|
* argument and comparison of the previous
|
|
* futex value with another constant.
|
|
*
|
|
@@ -25,18 +25,11 @@
|
|
* <0 - On error
|
|
*/
|
|
static inline int
|
|
-futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
|
|
+arch_futex_atomic_op_inuser(int op, u32 oparg, int *oval, u32 __user *uaddr)
|
|
{
|
|
- int op = (encoded_op >> 28) & 7;
|
|
- int cmp = (encoded_op >> 24) & 15;
|
|
- int oparg = (encoded_op << 8) >> 20;
|
|
- int cmparg = (encoded_op << 20) >> 20;
|
|
int oldval, ret;
|
|
u32 tmp;
|
|
|
|
- if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
|
|
- oparg = 1 << oparg;
|
|
-
|
|
preempt_disable();
|
|
pagefault_disable();
|
|
|
|
@@ -74,17 +67,9 @@ out_pagefault_enable:
|
|
pagefault_enable();
|
|
preempt_enable();
|
|
|
|
- if (ret == 0) {
|
|
- switch (cmp) {
|
|
- case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break;
|
|
- case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break;
|
|
- case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break;
|
|
- case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break;
|
|
- case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break;
|
|
- case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break;
|
|
- default: ret = -ENOSYS;
|
|
- }
|
|
- }
|
|
+ if (ret == 0)
|
|
+ *oval = oldval;
|
|
+
|
|
return ret;
|
|
}
|
|
|
|
@@ -126,18 +111,9 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
|
|
|
|
#else
|
|
static inline int
|
|
-futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
|
|
+arch_futex_atomic_op_inuser(int op, u32 oparg, int *oval, u32 __user *uaddr)
|
|
{
|
|
- int op = (encoded_op >> 28) & 7;
|
|
- int cmp = (encoded_op >> 24) & 15;
|
|
- int oparg = (encoded_op << 8) >> 20;
|
|
- int cmparg = (encoded_op << 20) >> 20;
|
|
int oldval = 0, ret;
|
|
- if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
|
|
- oparg = 1 << oparg;
|
|
-
|
|
- if (! access_ok (VERIFY_WRITE, uaddr, sizeof(u32)))
|
|
- return -EFAULT;
|
|
|
|
pagefault_disable();
|
|
|
|
@@ -153,17 +129,9 @@ futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
|
|
|
|
pagefault_enable();
|
|
|
|
- if (!ret) {
|
|
- switch (cmp) {
|
|
- case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break;
|
|
- case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break;
|
|
- case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break;
|
|
- case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break;
|
|
- case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break;
|
|
- case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break;
|
|
- default: ret = -ENOSYS;
|
|
- }
|
|
- }
|
|
+ if (!ret)
|
|
+ *oval = oldval;
|
|
+
|
|
return ret;
|
|
}
|
|
|
|
diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
|
|
index c47c68e535e8..a16d1851cfb1 100644
|
|
--- a/include/linux/dmaengine.h
|
|
+++ b/include/linux/dmaengine.h
|
|
@@ -767,6 +767,9 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_slave_single(
|
|
sg_dma_address(&sg) = buf;
|
|
sg_dma_len(&sg) = len;
|
|
|
|
+ if (!chan || !chan->device || !chan->device->device_prep_slave_sg)
|
|
+ return NULL;
|
|
+
|
|
return chan->device->device_prep_slave_sg(chan, &sg, 1,
|
|
dir, flags, NULL);
|
|
}
|
|
@@ -775,6 +778,9 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_slave_sg(
|
|
struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len,
|
|
enum dma_transfer_direction dir, unsigned long flags)
|
|
{
|
|
+ if (!chan || !chan->device || !chan->device->device_prep_slave_sg)
|
|
+ return NULL;
|
|
+
|
|
return chan->device->device_prep_slave_sg(chan, sgl, sg_len,
|
|
dir, flags, NULL);
|
|
}
|
|
@@ -786,6 +792,9 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_rio_sg(
|
|
enum dma_transfer_direction dir, unsigned long flags,
|
|
struct rio_dma_ext *rio_ext)
|
|
{
|
|
+ if (!chan || !chan->device || !chan->device->device_prep_slave_sg)
|
|
+ return NULL;
|
|
+
|
|
return chan->device->device_prep_slave_sg(chan, sgl, sg_len,
|
|
dir, flags, rio_ext);
|
|
}
|
|
@@ -796,6 +805,9 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_dma_cyclic(
|
|
size_t period_len, enum dma_transfer_direction dir,
|
|
unsigned long flags)
|
|
{
|
|
+ if (!chan || !chan->device || !chan->device->device_prep_dma_cyclic)
|
|
+ return NULL;
|
|
+
|
|
return chan->device->device_prep_dma_cyclic(chan, buf_addr, buf_len,
|
|
period_len, dir, flags);
|
|
}
|
|
@@ -804,6 +816,9 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_interleaved_dma(
|
|
struct dma_chan *chan, struct dma_interleaved_template *xt,
|
|
unsigned long flags)
|
|
{
|
|
+ if (!chan || !chan->device || !chan->device->device_prep_interleaved_dma)
|
|
+ return NULL;
|
|
+
|
|
return chan->device->device_prep_interleaved_dma(chan, xt, flags);
|
|
}
|
|
|
|
@@ -811,7 +826,7 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_dma_memset(
|
|
struct dma_chan *chan, dma_addr_t dest, int value, size_t len,
|
|
unsigned long flags)
|
|
{
|
|
- if (!chan || !chan->device)
|
|
+ if (!chan || !chan->device || !chan->device->device_prep_dma_memset)
|
|
return NULL;
|
|
|
|
return chan->device->device_prep_dma_memset(chan, dest, value,
|
|
@@ -824,6 +839,9 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_dma_sg(
|
|
struct scatterlist *src_sg, unsigned int src_nents,
|
|
unsigned long flags)
|
|
{
|
|
+ if (!chan || !chan->device || !chan->device->device_prep_dma_sg)
|
|
+ return NULL;
|
|
+
|
|
return chan->device->device_prep_dma_sg(chan, dst_sg, dst_nents,
|
|
src_sg, src_nents, flags);
|
|
}
|
|
diff --git a/include/linux/efi.h b/include/linux/efi.h
|
|
index 47be3ad7d3e5..7af95b58ebf3 100644
|
|
--- a/include/linux/efi.h
|
|
+++ b/include/linux/efi.h
|
|
@@ -364,8 +364,8 @@ typedef struct {
|
|
u32 attributes;
|
|
u32 get_bar_attributes;
|
|
u32 set_bar_attributes;
|
|
- uint64_t romsize;
|
|
- void *romimage;
|
|
+ u64 romsize;
|
|
+ u32 romimage;
|
|
} efi_pci_io_protocol_32;
|
|
|
|
typedef struct {
|
|
@@ -384,8 +384,8 @@ typedef struct {
|
|
u64 attributes;
|
|
u64 get_bar_attributes;
|
|
u64 set_bar_attributes;
|
|
- uint64_t romsize;
|
|
- void *romimage;
|
|
+ u64 romsize;
|
|
+ u64 romimage;
|
|
} efi_pci_io_protocol_64;
|
|
|
|
typedef struct {
|
|
diff --git a/include/linux/signal.h b/include/linux/signal.h
|
|
index d80259afb9e5..bcc094cb697c 100644
|
|
--- a/include/linux/signal.h
|
|
+++ b/include/linux/signal.h
|
|
@@ -97,6 +97,23 @@ static inline int sigisemptyset(sigset_t *set)
|
|
}
|
|
}
|
|
|
|
+static inline int sigequalsets(const sigset_t *set1, const sigset_t *set2)
|
|
+{
|
|
+ switch (_NSIG_WORDS) {
|
|
+ case 4:
|
|
+ return (set1->sig[3] == set2->sig[3]) &&
|
|
+ (set1->sig[2] == set2->sig[2]) &&
|
|
+ (set1->sig[1] == set2->sig[1]) &&
|
|
+ (set1->sig[0] == set2->sig[0]);
|
|
+ case 2:
|
|
+ return (set1->sig[1] == set2->sig[1]) &&
|
|
+ (set1->sig[0] == set2->sig[0]);
|
|
+ case 1:
|
|
+ return set1->sig[0] == set2->sig[0];
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
#define sigmask(sig) (1UL << ((sig) - 1))
|
|
|
|
#ifndef __HAVE_ARCH_SIG_SETOPS
|
|
diff --git a/include/trace/events/xen.h b/include/trace/events/xen.h
|
|
index bce990f5a35d..d6be935caa50 100644
|
|
--- a/include/trace/events/xen.h
|
|
+++ b/include/trace/events/xen.h
|
|
@@ -377,22 +377,6 @@ DECLARE_EVENT_CLASS(xen_mmu_pgd,
|
|
DEFINE_XEN_MMU_PGD_EVENT(xen_mmu_pgd_pin);
|
|
DEFINE_XEN_MMU_PGD_EVENT(xen_mmu_pgd_unpin);
|
|
|
|
-TRACE_EVENT(xen_mmu_flush_tlb_all,
|
|
- TP_PROTO(int x),
|
|
- TP_ARGS(x),
|
|
- TP_STRUCT__entry(__array(char, x, 0)),
|
|
- TP_fast_assign((void)x),
|
|
- TP_printk("%s", "")
|
|
- );
|
|
-
|
|
-TRACE_EVENT(xen_mmu_flush_tlb,
|
|
- TP_PROTO(int x),
|
|
- TP_ARGS(x),
|
|
- TP_STRUCT__entry(__array(char, x, 0)),
|
|
- TP_fast_assign((void)x),
|
|
- TP_printk("%s", "")
|
|
- );
|
|
-
|
|
TRACE_EVENT(xen_mmu_flush_tlb_single,
|
|
TP_PROTO(unsigned long addr),
|
|
TP_ARGS(addr),
|
|
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
|
|
index 1f0b4cf5dd03..f4227173b5d8 100644
|
|
--- a/include/uapi/linux/nl80211.h
|
|
+++ b/include/uapi/linux/nl80211.h
|
|
@@ -2195,6 +2195,8 @@ enum nl80211_attrs {
|
|
#define NL80211_ATTR_KEYS NL80211_ATTR_KEYS
|
|
#define NL80211_ATTR_FEATURE_FLAGS NL80211_ATTR_FEATURE_FLAGS
|
|
|
|
+#define NL80211_WIPHY_NAME_MAXLEN 128
|
|
+
|
|
#define NL80211_MAX_SUPP_RATES 32
|
|
#define NL80211_MAX_SUPP_HT_RATES 77
|
|
#define NL80211_MAX_SUPP_REG_RULES 64
|
|
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
|
|
index 4bdea31cf6ce..7444f95f3ee9 100644
|
|
--- a/kernel/auditsc.c
|
|
+++ b/kernel/auditsc.c
|
|
@@ -1981,14 +1981,16 @@ static void audit_log_set_loginuid(kuid_t koldloginuid, kuid_t kloginuid,
|
|
if (!audit_enabled)
|
|
return;
|
|
|
|
+ ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_LOGIN);
|
|
+ if (!ab)
|
|
+ return;
|
|
+
|
|
+
|
|
uid = from_kuid(&init_user_ns, task_uid(current));
|
|
oldloginuid = from_kuid(&init_user_ns, koldloginuid);
|
|
loginuid = from_kuid(&init_user_ns, kloginuid),
|
|
tty = audit_get_tty(current);
|
|
|
|
- ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_LOGIN);
|
|
- if (!ab)
|
|
- return;
|
|
audit_log_format(ab, "pid=%d uid=%u", task_pid_nr(current), uid);
|
|
audit_log_task_context(ab);
|
|
audit_log_format(ab, " old-auid=%u auid=%u tty=%s old-ses=%u ses=%u res=%d",
|
|
diff --git a/kernel/exit.c b/kernel/exit.c
|
|
index ffba5df4abd5..f20e6339761b 100644
|
|
--- a/kernel/exit.c
|
|
+++ b/kernel/exit.c
|
|
@@ -1608,6 +1608,10 @@ SYSCALL_DEFINE4(wait4, pid_t, upid, int __user *, stat_addr,
|
|
__WNOTHREAD|__WCLONE|__WALL))
|
|
return -EINVAL;
|
|
|
|
+ /* -INT_MIN is not defined */
|
|
+ if (upid == INT_MIN)
|
|
+ return -ESRCH;
|
|
+
|
|
if (upid == -1)
|
|
type = PIDTYPE_MAX;
|
|
else if (upid < 0) {
|
|
diff --git a/kernel/futex.c b/kernel/futex.c
|
|
index a12aa6785361..a26d217c99fe 100644
|
|
--- a/kernel/futex.c
|
|
+++ b/kernel/futex.c
|
|
@@ -666,13 +666,14 @@ again:
|
|
* this reference was taken by ihold under the page lock
|
|
* pinning the inode in place so i_lock was unnecessary. The
|
|
* only way for this check to fail is if the inode was
|
|
- * truncated in parallel so warn for now if this happens.
|
|
+ * truncated in parallel which is almost certainly an
|
|
+ * application bug. In such a case, just retry.
|
|
*
|
|
* We are not calling into get_futex_key_refs() in file-backed
|
|
* cases, therefore a successful atomic_inc return below will
|
|
* guarantee that get_futex_key() will still imply smp_mb(); (B).
|
|
*/
|
|
- if (WARN_ON_ONCE(!atomic_inc_not_zero(&inode->i_count))) {
|
|
+ if (!atomic_inc_not_zero(&inode->i_count)) {
|
|
rcu_read_unlock();
|
|
put_page(page_head);
|
|
|
|
@@ -1452,6 +1453,45 @@ out:
|
|
return ret;
|
|
}
|
|
|
|
+static int futex_atomic_op_inuser(unsigned int encoded_op, u32 __user *uaddr)
|
|
+{
|
|
+ unsigned int op = (encoded_op & 0x70000000) >> 28;
|
|
+ unsigned int cmp = (encoded_op & 0x0f000000) >> 24;
|
|
+ int oparg = sign_extend32((encoded_op & 0x00fff000) >> 12, 11);
|
|
+ int cmparg = sign_extend32(encoded_op & 0x00000fff, 11);
|
|
+ int oldval, ret;
|
|
+
|
|
+ if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) {
|
|
+ if (oparg < 0 || oparg > 31)
|
|
+ return -EINVAL;
|
|
+ oparg = 1 << oparg;
|
|
+ }
|
|
+
|
|
+ if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
|
|
+ return -EFAULT;
|
|
+
|
|
+ ret = arch_futex_atomic_op_inuser(op, oparg, &oldval, uaddr);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ switch (cmp) {
|
|
+ case FUTEX_OP_CMP_EQ:
|
|
+ return oldval == cmparg;
|
|
+ case FUTEX_OP_CMP_NE:
|
|
+ return oldval != cmparg;
|
|
+ case FUTEX_OP_CMP_LT:
|
|
+ return oldval < cmparg;
|
|
+ case FUTEX_OP_CMP_GE:
|
|
+ return oldval >= cmparg;
|
|
+ case FUTEX_OP_CMP_LE:
|
|
+ return oldval <= cmparg;
|
|
+ case FUTEX_OP_CMP_GT:
|
|
+ return oldval > cmparg;
|
|
+ default:
|
|
+ return -ENOSYS;
|
|
+ }
|
|
+}
|
|
+
|
|
/*
|
|
* Wake up all waiters hashed on the physical page that is mapped
|
|
* to this virtual address:
|
|
diff --git a/kernel/signal.c b/kernel/signal.c
|
|
index 4a548c6a4118..7d75bc2d042f 100644
|
|
--- a/kernel/signal.c
|
|
+++ b/kernel/signal.c
|
|
@@ -2495,6 +2495,13 @@ void __set_current_blocked(const sigset_t *newset)
|
|
{
|
|
struct task_struct *tsk = current;
|
|
|
|
+ /*
|
|
+ * In case the signal mask hasn't changed, there is nothing we need
|
|
+ * to do. The current->blocked shouldn't be modified by other task.
|
|
+ */
|
|
+ if (sigequalsets(&tsk->blocked, newset))
|
|
+ return;
|
|
+
|
|
spin_lock_irq(&tsk->sighand->siglock);
|
|
__set_task_blocked(tsk, newset);
|
|
spin_unlock_irq(&tsk->sighand->siglock);
|
|
diff --git a/kernel/time/tick-broadcast.c b/kernel/time/tick-broadcast.c
|
|
index d2a20e83ebae..22d7454b387b 100644
|
|
--- a/kernel/time/tick-broadcast.c
|
|
+++ b/kernel/time/tick-broadcast.c
|
|
@@ -610,6 +610,14 @@ static void tick_handle_oneshot_broadcast(struct clock_event_device *dev)
|
|
now = ktime_get();
|
|
/* Find all expired events */
|
|
for_each_cpu(cpu, tick_broadcast_oneshot_mask) {
|
|
+ /*
|
|
+ * Required for !SMP because for_each_cpu() reports
|
|
+ * unconditionally CPU0 as set on UP kernels.
|
|
+ */
|
|
+ if (!IS_ENABLED(CONFIG_SMP) &&
|
|
+ cpumask_empty(tick_broadcast_oneshot_mask))
|
|
+ break;
|
|
+
|
|
td = &per_cpu(tick_cpu_device, cpu);
|
|
if (td->evtdev->next_event.tv64 <= now.tv64) {
|
|
cpumask_set_cpu(cpu, tmpmask);
|
|
diff --git a/mm/Kconfig b/mm/Kconfig
|
|
index 97a4e06b15c0..5753f69b23f4 100644
|
|
--- a/mm/Kconfig
|
|
+++ b/mm/Kconfig
|
|
@@ -628,6 +628,7 @@ config DEFERRED_STRUCT_PAGE_INIT
|
|
default n
|
|
depends on ARCH_SUPPORTS_DEFERRED_STRUCT_PAGE_INIT
|
|
depends on MEMORY_HOTPLUG
|
|
+ depends on !NEED_PER_CPU_KM
|
|
help
|
|
Ordinarily all struct pages are initialised during early boot in a
|
|
single thread. On very large machines this can take a considerable
|
|
diff --git a/mm/filemap.c b/mm/filemap.c
|
|
index b15f1d8bba43..21e750b6e810 100644
|
|
--- a/mm/filemap.c
|
|
+++ b/mm/filemap.c
|
|
@@ -1581,6 +1581,15 @@ find_page:
|
|
index, last_index - index);
|
|
}
|
|
if (!PageUptodate(page)) {
|
|
+ /*
|
|
+ * See comment in do_read_cache_page on why
|
|
+ * wait_on_page_locked is used to avoid unnecessarily
|
|
+ * serialisations and why it's safe.
|
|
+ */
|
|
+ wait_on_page_locked_killable(page);
|
|
+ if (PageUptodate(page))
|
|
+ goto page_ok;
|
|
+
|
|
if (inode->i_blkbits == PAGE_CACHE_SHIFT ||
|
|
!mapping->a_ops->is_partially_uptodate)
|
|
goto page_not_up_to_date;
|
|
@@ -2215,7 +2224,7 @@ static struct page *wait_on_page_read(struct page *page)
|
|
return page;
|
|
}
|
|
|
|
-static struct page *__read_cache_page(struct address_space *mapping,
|
|
+static struct page *do_read_cache_page(struct address_space *mapping,
|
|
pgoff_t index,
|
|
int (*filler)(void *, struct page *),
|
|
void *data,
|
|
@@ -2237,53 +2246,74 @@ repeat:
|
|
/* Presumably ENOMEM for radix tree node */
|
|
return ERR_PTR(err);
|
|
}
|
|
+
|
|
+filler:
|
|
err = filler(data, page);
|
|
if (err < 0) {
|
|
page_cache_release(page);
|
|
- page = ERR_PTR(err);
|
|
- } else {
|
|
- page = wait_on_page_read(page);
|
|
+ return ERR_PTR(err);
|
|
}
|
|
- }
|
|
- return page;
|
|
-}
|
|
|
|
-static struct page *do_read_cache_page(struct address_space *mapping,
|
|
- pgoff_t index,
|
|
- int (*filler)(void *, struct page *),
|
|
- void *data,
|
|
- gfp_t gfp)
|
|
-
|
|
-{
|
|
- struct page *page;
|
|
- int err;
|
|
+ page = wait_on_page_read(page);
|
|
+ if (IS_ERR(page))
|
|
+ return page;
|
|
+ goto out;
|
|
+ }
|
|
+ if (PageUptodate(page))
|
|
+ goto out;
|
|
|
|
-retry:
|
|
- page = __read_cache_page(mapping, index, filler, data, gfp);
|
|
- if (IS_ERR(page))
|
|
- return page;
|
|
+ /*
|
|
+ * Page is not up to date and may be locked due one of the following
|
|
+ * case a: Page is being filled and the page lock is held
|
|
+ * case b: Read/write error clearing the page uptodate status
|
|
+ * case c: Truncation in progress (page locked)
|
|
+ * case d: Reclaim in progress
|
|
+ *
|
|
+ * Case a, the page will be up to date when the page is unlocked.
|
|
+ * There is no need to serialise on the page lock here as the page
|
|
+ * is pinned so the lock gives no additional protection. Even if the
|
|
+ * the page is truncated, the data is still valid if PageUptodate as
|
|
+ * it's a race vs truncate race.
|
|
+ * Case b, the page will not be up to date
|
|
+ * Case c, the page may be truncated but in itself, the data may still
|
|
+ * be valid after IO completes as it's a read vs truncate race. The
|
|
+ * operation must restart if the page is not uptodate on unlock but
|
|
+ * otherwise serialising on page lock to stabilise the mapping gives
|
|
+ * no additional guarantees to the caller as the page lock is
|
|
+ * released before return.
|
|
+ * Case d, similar to truncation. If reclaim holds the page lock, it
|
|
+ * will be a race with remove_mapping that determines if the mapping
|
|
+ * is valid on unlock but otherwise the data is valid and there is
|
|
+ * no need to serialise with page lock.
|
|
+ *
|
|
+ * As the page lock gives no additional guarantee, we optimistically
|
|
+ * wait on the page to be unlocked and check if it's up to date and
|
|
+ * use the page if it is. Otherwise, the page lock is required to
|
|
+ * distinguish between the different cases. The motivation is that we
|
|
+ * avoid spurious serialisations and wakeups when multiple processes
|
|
+ * wait on the same page for IO to complete.
|
|
+ */
|
|
+ wait_on_page_locked(page);
|
|
if (PageUptodate(page))
|
|
goto out;
|
|
|
|
+ /* Distinguish between all the cases under the safety of the lock */
|
|
lock_page(page);
|
|
+
|
|
+ /* Case c or d, restart the operation */
|
|
if (!page->mapping) {
|
|
unlock_page(page);
|
|
page_cache_release(page);
|
|
- goto retry;
|
|
+ goto repeat;
|
|
}
|
|
+
|
|
+ /* Someone else locked and filled the page in a very small window */
|
|
if (PageUptodate(page)) {
|
|
unlock_page(page);
|
|
goto out;
|
|
}
|
|
- err = filler(data, page);
|
|
- if (err < 0) {
|
|
- page_cache_release(page);
|
|
- return ERR_PTR(err);
|
|
- } else {
|
|
- page = wait_on_page_read(page);
|
|
- if (IS_ERR(page))
|
|
- return page;
|
|
- }
|
|
+ goto filler;
|
|
+
|
|
out:
|
|
mark_page_accessed(page);
|
|
return page;
|
|
diff --git a/mm/util.c b/mm/util.c
|
|
index 818bbae84721..5fae5b9c2885 100644
|
|
--- a/mm/util.c
|
|
+++ b/mm/util.c
|
|
@@ -428,17 +428,25 @@ int get_cmdline(struct task_struct *task, char *buffer, int buflen)
|
|
int res = 0;
|
|
unsigned int len;
|
|
struct mm_struct *mm = get_task_mm(task);
|
|
+ unsigned long arg_start, arg_end, env_start, env_end;
|
|
if (!mm)
|
|
goto out;
|
|
if (!mm->arg_end)
|
|
goto out_mm; /* Shh! No looking before we're done */
|
|
|
|
- len = mm->arg_end - mm->arg_start;
|
|
+ down_read(&mm->mmap_sem);
|
|
+ arg_start = mm->arg_start;
|
|
+ arg_end = mm->arg_end;
|
|
+ env_start = mm->env_start;
|
|
+ env_end = mm->env_end;
|
|
+ up_read(&mm->mmap_sem);
|
|
+
|
|
+ len = arg_end - arg_start;
|
|
|
|
if (len > buflen)
|
|
len = buflen;
|
|
|
|
- res = access_process_vm(task, mm->arg_start, buffer, len, 0);
|
|
+ res = access_process_vm(task, arg_start, buffer, len, 0);
|
|
|
|
/*
|
|
* If the nul at the end of args has been overwritten, then
|
|
@@ -449,10 +457,10 @@ int get_cmdline(struct task_struct *task, char *buffer, int buflen)
|
|
if (len < res) {
|
|
res = len;
|
|
} else {
|
|
- len = mm->env_end - mm->env_start;
|
|
+ len = env_end - env_start;
|
|
if (len > buflen - res)
|
|
len = buflen - res;
|
|
- res += access_process_vm(task, mm->env_start,
|
|
+ res += access_process_vm(task, env_start,
|
|
buffer+res, len, 0);
|
|
res = strnlen(buffer, res);
|
|
}
|
|
diff --git a/mm/vmscan.c b/mm/vmscan.c
|
|
index 930f7c67a9c1..12a69e6c10ba 100644
|
|
--- a/mm/vmscan.c
|
|
+++ b/mm/vmscan.c
|
|
@@ -2057,10 +2057,16 @@ static void get_scan_count(struct lruvec *lruvec, int swappiness,
|
|
}
|
|
|
|
/*
|
|
- * There is enough inactive page cache, do not reclaim
|
|
- * anything from the anonymous working set right now.
|
|
+ * If there is enough inactive page cache, i.e. if the size of the
|
|
+ * inactive list is greater than that of the active list *and* the
|
|
+ * inactive list actually has some pages to scan on this priority, we
|
|
+ * do not reclaim anything from the anonymous working set right now.
|
|
+ * Without the second condition we could end up never scanning an
|
|
+ * lruvec even if it has plenty of old anonymous pages unless the
|
|
+ * system is under heavy pressure.
|
|
*/
|
|
- if (!inactive_file_is_low(lruvec)) {
|
|
+ if (!inactive_file_is_low(lruvec) &&
|
|
+ get_lru_size(lruvec, LRU_INACTIVE_FILE) >> sc->priority) {
|
|
scan_balance = SCAN_FILE;
|
|
goto out;
|
|
}
|
|
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
|
|
index ec02f5869a78..3400b1e47668 100644
|
|
--- a/net/bridge/br_if.c
|
|
+++ b/net/bridge/br_if.c
|
|
@@ -456,8 +456,8 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
|
|
if (dev->netdev_ops->ndo_start_xmit == br_dev_xmit)
|
|
return -ELOOP;
|
|
|
|
- /* Device is already being bridged */
|
|
- if (br_port_exists(dev))
|
|
+ /* Device has master upper dev */
|
|
+ if (netdev_master_upper_dev_get(dev))
|
|
return -EBUSY;
|
|
|
|
/* No bridging devices that dislike that (e.g. wireless) */
|
|
diff --git a/net/compat.c b/net/compat.c
|
|
index 0ccf3ecf6bbb..17e97b106458 100644
|
|
--- a/net/compat.c
|
|
+++ b/net/compat.c
|
|
@@ -358,7 +358,8 @@ static int compat_sock_setsockopt(struct socket *sock, int level, int optname,
|
|
if (optname == SO_ATTACH_FILTER)
|
|
return do_set_attach_filter(sock, level, optname,
|
|
optval, optlen);
|
|
- if (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO)
|
|
+ if (!COMPAT_USE_64BIT_TIME &&
|
|
+ (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO))
|
|
return do_set_sock_timeout(sock, level, optname, optval, optlen);
|
|
|
|
return sock_setsockopt(sock, level, optname, optval, optlen);
|
|
@@ -423,7 +424,8 @@ static int do_get_sock_timeout(struct socket *sock, int level, int optname,
|
|
static int compat_sock_getsockopt(struct socket *sock, int level, int optname,
|
|
char __user *optval, int __user *optlen)
|
|
{
|
|
- if (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO)
|
|
+ if (!COMPAT_USE_64BIT_TIME &&
|
|
+ (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO))
|
|
return do_get_sock_timeout(sock, level, optname, optval, optlen);
|
|
return sock_getsockopt(sock, level, optname, optval, optlen);
|
|
}
|
|
diff --git a/net/core/sock.c b/net/core/sock.c
|
|
index cd12cb6fe366..4238835a0e4e 100644
|
|
--- a/net/core/sock.c
|
|
+++ b/net/core/sock.c
|
|
@@ -1474,7 +1474,7 @@ void sk_destruct(struct sock *sk)
|
|
|
|
static void __sk_free(struct sock *sk)
|
|
{
|
|
- if (unlikely(sock_diag_has_destroy_listeners(sk) && sk->sk_net_refcnt))
|
|
+ if (unlikely(sk->sk_net_refcnt && sock_diag_has_destroy_listeners(sk)))
|
|
sock_diag_broadcast_destroy(sk);
|
|
else
|
|
sk_destruct(sk);
|
|
diff --git a/net/dccp/ccids/ccid2.c b/net/dccp/ccids/ccid2.c
|
|
index 7753681195c1..86a2ed0fb219 100644
|
|
--- a/net/dccp/ccids/ccid2.c
|
|
+++ b/net/dccp/ccids/ccid2.c
|
|
@@ -126,6 +126,16 @@ static void ccid2_change_l_seq_window(struct sock *sk, u64 val)
|
|
DCCPF_SEQ_WMAX));
|
|
}
|
|
|
|
+static void dccp_tasklet_schedule(struct sock *sk)
|
|
+{
|
|
+ struct tasklet_struct *t = &dccp_sk(sk)->dccps_xmitlet;
|
|
+
|
|
+ if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) {
|
|
+ sock_hold(sk);
|
|
+ __tasklet_schedule(t);
|
|
+ }
|
|
+}
|
|
+
|
|
static void ccid2_hc_tx_rto_expire(unsigned long data)
|
|
{
|
|
struct sock *sk = (struct sock *)data;
|
|
@@ -166,7 +176,7 @@ static void ccid2_hc_tx_rto_expire(unsigned long data)
|
|
|
|
/* if we were blocked before, we may now send cwnd=1 packet */
|
|
if (sender_was_blocked)
|
|
- tasklet_schedule(&dccp_sk(sk)->dccps_xmitlet);
|
|
+ dccp_tasklet_schedule(sk);
|
|
/* restart backed-off timer */
|
|
sk_reset_timer(sk, &hc->tx_rtotimer, jiffies + hc->tx_rto);
|
|
out:
|
|
@@ -706,7 +716,7 @@ static void ccid2_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb)
|
|
done:
|
|
/* check if incoming Acks allow pending packets to be sent */
|
|
if (sender_was_blocked && !ccid2_cwnd_network_limited(hc))
|
|
- tasklet_schedule(&dccp_sk(sk)->dccps_xmitlet);
|
|
+ dccp_tasklet_schedule(sk);
|
|
dccp_ackvec_parsed_cleanup(&hc->tx_av_chunks);
|
|
}
|
|
|
|
diff --git a/net/dccp/timer.c b/net/dccp/timer.c
|
|
index 3ef7acef3ce8..aa7c7dad7f96 100644
|
|
--- a/net/dccp/timer.c
|
|
+++ b/net/dccp/timer.c
|
|
@@ -230,12 +230,12 @@ static void dccp_write_xmitlet(unsigned long data)
|
|
else
|
|
dccp_write_xmit(sk);
|
|
bh_unlock_sock(sk);
|
|
+ sock_put(sk);
|
|
}
|
|
|
|
static void dccp_write_xmit_timer(unsigned long data)
|
|
{
|
|
dccp_write_xmitlet(data);
|
|
- sock_put((struct sock *)data);
|
|
}
|
|
|
|
void dccp_init_xmit_timers(struct sock *sk)
|
|
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
|
|
index 09c73dd541c5..10286432f684 100644
|
|
--- a/net/ipv4/ip_output.c
|
|
+++ b/net/ipv4/ip_output.c
|
|
@@ -1062,7 +1062,8 @@ alloc_new_skb:
|
|
if (copy > length)
|
|
copy = length;
|
|
|
|
- if (!(rt->dst.dev->features&NETIF_F_SG)) {
|
|
+ if (!(rt->dst.dev->features&NETIF_F_SG) &&
|
|
+ skb_tailroom(skb) >= copy) {
|
|
unsigned int off;
|
|
|
|
off = skb->len;
|
|
diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c
|
|
index 37a3b05d175c..82c878224bfc 100644
|
|
--- a/net/ipv4/ping.c
|
|
+++ b/net/ipv4/ping.c
|
|
@@ -777,8 +777,10 @@ static int ping_v4_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
|
|
ipc.addr = faddr = daddr;
|
|
|
|
if (ipc.opt && ipc.opt->opt.srr) {
|
|
- if (!daddr)
|
|
- return -EINVAL;
|
|
+ if (!daddr) {
|
|
+ err = -EINVAL;
|
|
+ goto out_free;
|
|
+ }
|
|
faddr = ipc.opt->opt.faddr;
|
|
}
|
|
tos = get_rttos(&ipc, inet);
|
|
@@ -843,6 +845,7 @@ back_from_confirm:
|
|
|
|
out:
|
|
ip_rt_put(rt);
|
|
+out_free:
|
|
if (free)
|
|
kfree(ipc.opt);
|
|
if (!err) {
|
|
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
|
|
index b531a0997664..a0f0a7db946b 100644
|
|
--- a/net/ipv4/tcp.c
|
|
+++ b/net/ipv4/tcp.c
|
|
@@ -1108,7 +1108,7 @@ int tcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)
|
|
lock_sock(sk);
|
|
|
|
flags = msg->msg_flags;
|
|
- if (flags & MSG_FASTOPEN) {
|
|
+ if ((flags & MSG_FASTOPEN) && !tp->repair) {
|
|
err = tcp_sendmsg_fastopen(sk, msg, &copied_syn, size);
|
|
if (err == -EINPROGRESS && copied_syn > 0)
|
|
goto out;
|
|
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
|
|
index 39c2919fe0d3..2854db094864 100644
|
|
--- a/net/ipv4/tcp_output.c
|
|
+++ b/net/ipv4/tcp_output.c
|
|
@@ -2587,8 +2587,10 @@ int __tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb)
|
|
return -EBUSY;
|
|
|
|
if (before(TCP_SKB_CB(skb)->seq, tp->snd_una)) {
|
|
- if (before(TCP_SKB_CB(skb)->end_seq, tp->snd_una))
|
|
- BUG();
|
|
+ if (unlikely(before(TCP_SKB_CB(skb)->end_seq, tp->snd_una))) {
|
|
+ WARN_ON_ONCE(1);
|
|
+ return -EINVAL;
|
|
+ }
|
|
if (tcp_trim_head(sk, skb, tp->snd_una - TCP_SKB_CB(skb)->seq))
|
|
return -ENOMEM;
|
|
}
|
|
@@ -3117,6 +3119,7 @@ static void tcp_connect_init(struct sock *sk)
|
|
sock_reset_flag(sk, SOCK_DONE);
|
|
tp->snd_wnd = 0;
|
|
tcp_init_wl(tp, 0);
|
|
+ tcp_write_queue_purge(sk);
|
|
tp->snd_una = tp->write_seq;
|
|
tp->snd_sml = tp->write_seq;
|
|
tp->snd_up = tp->write_seq;
|
|
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
|
|
index a98ae890adb9..6f929689fd03 100644
|
|
--- a/net/ipv4/udp.c
|
|
+++ b/net/ipv4/udp.c
|
|
@@ -991,8 +991,10 @@ int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
|
|
ipc.addr = faddr = daddr;
|
|
|
|
if (ipc.opt && ipc.opt->opt.srr) {
|
|
- if (!daddr)
|
|
- return -EINVAL;
|
|
+ if (!daddr) {
|
|
+ err = -EINVAL;
|
|
+ goto out_free;
|
|
+ }
|
|
faddr = ipc.opt->opt.faddr;
|
|
connected = 0;
|
|
}
|
|
@@ -1105,6 +1107,7 @@ do_append_data:
|
|
|
|
out:
|
|
ip_rt_put(rt);
|
|
+out_free:
|
|
if (free)
|
|
kfree(ipc.opt);
|
|
if (!err)
|
|
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
|
|
index bfa710e8b615..74786783834b 100644
|
|
--- a/net/ipv6/ip6_output.c
|
|
+++ b/net/ipv6/ip6_output.c
|
|
@@ -1529,7 +1529,8 @@ alloc_new_skb:
|
|
if (copy > length)
|
|
copy = length;
|
|
|
|
- if (!(rt->dst.dev->features&NETIF_F_SG)) {
|
|
+ if (!(rt->dst.dev->features&NETIF_F_SG) &&
|
|
+ skb_tailroom(skb) >= copy) {
|
|
unsigned int off;
|
|
|
|
off = skb->len;
|
|
diff --git a/net/l2tp/l2tp_netlink.c b/net/l2tp/l2tp_netlink.c
|
|
index ae3438685caa..fb3248ff8b48 100644
|
|
--- a/net/l2tp/l2tp_netlink.c
|
|
+++ b/net/l2tp/l2tp_netlink.c
|
|
@@ -732,8 +732,6 @@ static int l2tp_nl_session_send(struct sk_buff *skb, u32 portid, u32 seq, int fl
|
|
|
|
if ((session->ifname[0] &&
|
|
nla_put_string(skb, L2TP_ATTR_IFNAME, session->ifname)) ||
|
|
- (session->offset &&
|
|
- nla_put_u16(skb, L2TP_ATTR_OFFSET, session->offset)) ||
|
|
(session->cookie_len &&
|
|
nla_put(skb, L2TP_ATTR_COOKIE, session->cookie_len,
|
|
&session->cookie[0])) ||
|
|
diff --git a/net/llc/af_llc.c b/net/llc/af_llc.c
|
|
index 09f2f3471ad6..83e8a295c806 100644
|
|
--- a/net/llc/af_llc.c
|
|
+++ b/net/llc/af_llc.c
|
|
@@ -926,6 +926,9 @@ static int llc_ui_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
|
|
if (size > llc->dev->mtu)
|
|
size = llc->dev->mtu;
|
|
copied = size - hdrlen;
|
|
+ rc = -EINVAL;
|
|
+ if (copied < 0)
|
|
+ goto release;
|
|
release_sock(sk);
|
|
skb = sock_alloc_send_skb(sk, size, noblock, &rc);
|
|
lock_sock(sk);
|
|
diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c
|
|
index 21e4d339217e..624c4719e404 100644
|
|
--- a/net/openvswitch/flow_netlink.c
|
|
+++ b/net/openvswitch/flow_netlink.c
|
|
@@ -1141,13 +1141,10 @@ static void nlattr_set(struct nlattr *attr, u8 val,
|
|
|
|
/* The nlattr stream should already have been validated */
|
|
nla_for_each_nested(nla, attr, rem) {
|
|
- if (tbl[nla_type(nla)].len == OVS_ATTR_NESTED) {
|
|
- if (tbl[nla_type(nla)].next)
|
|
- tbl = tbl[nla_type(nla)].next;
|
|
- nlattr_set(nla, val, tbl);
|
|
- } else {
|
|
+ if (tbl[nla_type(nla)].len == OVS_ATTR_NESTED)
|
|
+ nlattr_set(nla, val, tbl[nla_type(nla)].next ? : tbl);
|
|
+ else
|
|
memset(nla_data(nla), val, nla_len(nla));
|
|
- }
|
|
|
|
if (nla_type(nla) == OVS_KEY_ATTR_CT_STATE)
|
|
*(u32 *)nla_data(nla) &= CT_SUPPORTED_MASK;
|
|
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
|
|
index f165514a4db5..392d4e2c0a24 100644
|
|
--- a/net/packet/af_packet.c
|
|
+++ b/net/packet/af_packet.c
|
|
@@ -2771,13 +2771,15 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
|
|
if (skb == NULL)
|
|
goto out_unlock;
|
|
|
|
- skb_set_network_header(skb, reserve);
|
|
+ skb_reset_network_header(skb);
|
|
|
|
err = -EINVAL;
|
|
if (sock->type == SOCK_DGRAM) {
|
|
offset = dev_hard_header(skb, dev, ntohs(proto), addr, NULL, len);
|
|
if (unlikely(offset < 0))
|
|
goto out_free;
|
|
+ } else if (reserve) {
|
|
+ skb_push(skb, reserve);
|
|
}
|
|
|
|
/* Returns -EFAULT on error */
|
|
diff --git a/net/sched/sch_fq.c b/net/sched/sch_fq.c
|
|
index 3c6a47d66a04..117ed90c5f21 100644
|
|
--- a/net/sched/sch_fq.c
|
|
+++ b/net/sched/sch_fq.c
|
|
@@ -126,6 +126,28 @@ static bool fq_flow_is_detached(const struct fq_flow *f)
|
|
return f->next == &detached;
|
|
}
|
|
|
|
+static bool fq_flow_is_throttled(const struct fq_flow *f)
|
|
+{
|
|
+ return f->next == &throttled;
|
|
+}
|
|
+
|
|
+static void fq_flow_add_tail(struct fq_flow_head *head, struct fq_flow *flow)
|
|
+{
|
|
+ if (head->first)
|
|
+ head->last->next = flow;
|
|
+ else
|
|
+ head->first = flow;
|
|
+ head->last = flow;
|
|
+ flow->next = NULL;
|
|
+}
|
|
+
|
|
+static void fq_flow_unset_throttled(struct fq_sched_data *q, struct fq_flow *f)
|
|
+{
|
|
+ rb_erase(&f->rate_node, &q->delayed);
|
|
+ q->throttled_flows--;
|
|
+ fq_flow_add_tail(&q->old_flows, f);
|
|
+}
|
|
+
|
|
static void fq_flow_set_throttled(struct fq_sched_data *q, struct fq_flow *f)
|
|
{
|
|
struct rb_node **p = &q->delayed.rb_node, *parent = NULL;
|
|
@@ -153,15 +175,6 @@ static void fq_flow_set_throttled(struct fq_sched_data *q, struct fq_flow *f)
|
|
|
|
static struct kmem_cache *fq_flow_cachep __read_mostly;
|
|
|
|
-static void fq_flow_add_tail(struct fq_flow_head *head, struct fq_flow *flow)
|
|
-{
|
|
- if (head->first)
|
|
- head->last->next = flow;
|
|
- else
|
|
- head->first = flow;
|
|
- head->last = flow;
|
|
- flow->next = NULL;
|
|
-}
|
|
|
|
/* limit number of collected flows per round */
|
|
#define FQ_GC_MAX 8
|
|
@@ -265,6 +278,8 @@ static struct fq_flow *fq_classify(struct sk_buff *skb, struct fq_sched_data *q)
|
|
f->socket_hash != sk->sk_hash)) {
|
|
f->credit = q->initial_quantum;
|
|
f->socket_hash = sk->sk_hash;
|
|
+ if (fq_flow_is_throttled(f))
|
|
+ fq_flow_unset_throttled(q, f);
|
|
f->time_next_packet = 0ULL;
|
|
}
|
|
return f;
|
|
@@ -419,9 +434,7 @@ static void fq_check_throttled(struct fq_sched_data *q, u64 now)
|
|
q->time_next_delayed_flow = f->time_next_packet;
|
|
break;
|
|
}
|
|
- rb_erase(p, &q->delayed);
|
|
- q->throttled_flows--;
|
|
- fq_flow_add_tail(&q->old_flows, f);
|
|
+ fq_flow_unset_throttled(q, f);
|
|
}
|
|
}
|
|
|
|
diff --git a/net/sctp/associola.c b/net/sctp/associola.c
|
|
index 559afd0ee7de..a40b8b0ef0d5 100644
|
|
--- a/net/sctp/associola.c
|
|
+++ b/net/sctp/associola.c
|
|
@@ -1000,9 +1000,10 @@ static void sctp_assoc_bh_rcv(struct work_struct *work)
|
|
struct sctp_endpoint *ep;
|
|
struct sctp_chunk *chunk;
|
|
struct sctp_inq *inqueue;
|
|
- int state;
|
|
sctp_subtype_t subtype;
|
|
+ int first_time = 1; /* is this the first time through the loop */
|
|
int error = 0;
|
|
+ int state;
|
|
|
|
/* The association should be held so we should be safe. */
|
|
ep = asoc->ep;
|
|
@@ -1013,6 +1014,30 @@ static void sctp_assoc_bh_rcv(struct work_struct *work)
|
|
state = asoc->state;
|
|
subtype = SCTP_ST_CHUNK(chunk->chunk_hdr->type);
|
|
|
|
+ /* If the first chunk in the packet is AUTH, do special
|
|
+ * processing specified in Section 6.3 of SCTP-AUTH spec
|
|
+ */
|
|
+ if (first_time && subtype.chunk == SCTP_CID_AUTH) {
|
|
+ struct sctp_chunkhdr *next_hdr;
|
|
+
|
|
+ next_hdr = sctp_inq_peek(inqueue);
|
|
+ if (!next_hdr)
|
|
+ goto normal;
|
|
+
|
|
+ /* If the next chunk is COOKIE-ECHO, skip the AUTH
|
|
+ * chunk while saving a pointer to it so we can do
|
|
+ * Authentication later (during cookie-echo
|
|
+ * processing).
|
|
+ */
|
|
+ if (next_hdr->type == SCTP_CID_COOKIE_ECHO) {
|
|
+ chunk->auth_chunk = skb_clone(chunk->skb,
|
|
+ GFP_ATOMIC);
|
|
+ chunk->auth = 1;
|
|
+ continue;
|
|
+ }
|
|
+ }
|
|
+
|
|
+normal:
|
|
/* SCTP-AUTH, Section 6.3:
|
|
* The receiver has a list of chunk types which it expects
|
|
* to be received only after an AUTH-chunk. This list has
|
|
@@ -1051,6 +1076,9 @@ static void sctp_assoc_bh_rcv(struct work_struct *work)
|
|
/* If there is an error on chunk, discard this packet. */
|
|
if (error && chunk)
|
|
chunk->pdiscard = 1;
|
|
+
|
|
+ if (first_time)
|
|
+ first_time = 0;
|
|
}
|
|
sctp_association_put(asoc);
|
|
}
|
|
diff --git a/net/sctp/inqueue.c b/net/sctp/inqueue.c
|
|
index 7e8a16c77039..8d9b7ad25b65 100644
|
|
--- a/net/sctp/inqueue.c
|
|
+++ b/net/sctp/inqueue.c
|
|
@@ -178,7 +178,7 @@ struct sctp_chunk *sctp_inq_pop(struct sctp_inq *queue)
|
|
skb_pull(chunk->skb, sizeof(sctp_chunkhdr_t));
|
|
chunk->subh.v = NULL; /* Subheader is no longer valid. */
|
|
|
|
- if (chunk->chunk_end + sizeof(sctp_chunkhdr_t) <
|
|
+ if (chunk->chunk_end + sizeof(sctp_chunkhdr_t) <=
|
|
skb_tail_pointer(chunk->skb)) {
|
|
/* This is not a singleton */
|
|
chunk->singleton = 0;
|
|
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
|
|
index 1cd7b7e33fa3..5ca8309ea7b1 100644
|
|
--- a/net/sctp/ipv6.c
|
|
+++ b/net/sctp/ipv6.c
|
|
@@ -863,6 +863,9 @@ static int sctp_inet6_cmp_addr(const union sctp_addr *addr1,
|
|
if (sctp_is_any(sk, addr1) || sctp_is_any(sk, addr2))
|
|
return 1;
|
|
|
|
+ if (addr1->sa.sa_family == AF_INET && addr2->sa.sa_family == AF_INET)
|
|
+ return addr1->v4.sin_addr.s_addr == addr2->v4.sin_addr.s_addr;
|
|
+
|
|
return __sctp_v6_cmp_addr(addr1, addr2);
|
|
}
|
|
|
|
diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
|
|
index 29c7c43de108..df9ac3746c1b 100644
|
|
--- a/net/sctp/sm_statefuns.c
|
|
+++ b/net/sctp/sm_statefuns.c
|
|
@@ -144,10 +144,8 @@ static sctp_disposition_t sctp_sf_violation_chunk(
|
|
void *arg,
|
|
sctp_cmd_seq_t *commands);
|
|
|
|
-static sctp_ierror_t sctp_sf_authenticate(struct net *net,
|
|
- const struct sctp_endpoint *ep,
|
|
+static sctp_ierror_t sctp_sf_authenticate(
|
|
const struct sctp_association *asoc,
|
|
- const sctp_subtype_t type,
|
|
struct sctp_chunk *chunk);
|
|
|
|
static sctp_disposition_t __sctp_sf_do_9_1_abort(struct net *net,
|
|
@@ -615,6 +613,38 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(struct net *net,
|
|
return SCTP_DISPOSITION_CONSUME;
|
|
}
|
|
|
|
+static bool sctp_auth_chunk_verify(struct net *net, struct sctp_chunk *chunk,
|
|
+ const struct sctp_association *asoc)
|
|
+{
|
|
+ struct sctp_chunk auth;
|
|
+
|
|
+ if (!chunk->auth_chunk)
|
|
+ return true;
|
|
+
|
|
+ /* SCTP-AUTH: auth_chunk pointer is only set when the cookie-echo
|
|
+ * is supposed to be authenticated and we have to do delayed
|
|
+ * authentication. We've just recreated the association using
|
|
+ * the information in the cookie and now it's much easier to
|
|
+ * do the authentication.
|
|
+ */
|
|
+
|
|
+ /* Make sure that we and the peer are AUTH capable */
|
|
+ if (!net->sctp.auth_enable || !asoc->peer.auth_capable)
|
|
+ return false;
|
|
+
|
|
+ /* set-up our fake chunk so that we can process it */
|
|
+ auth.skb = chunk->auth_chunk;
|
|
+ auth.asoc = chunk->asoc;
|
|
+ auth.sctp_hdr = chunk->sctp_hdr;
|
|
+ auth.chunk_hdr = (struct sctp_chunkhdr *)
|
|
+ skb_push(chunk->auth_chunk,
|
|
+ sizeof(struct sctp_chunkhdr));
|
|
+ skb_pull(chunk->auth_chunk, sizeof(struct sctp_chunkhdr));
|
|
+ auth.transport = chunk->transport;
|
|
+
|
|
+ return sctp_sf_authenticate(asoc, &auth) == SCTP_IERROR_NO_ERROR;
|
|
+}
|
|
+
|
|
/*
|
|
* Respond to a normal COOKIE ECHO chunk.
|
|
* We are the side that is being asked for an association.
|
|
@@ -751,36 +781,9 @@ sctp_disposition_t sctp_sf_do_5_1D_ce(struct net *net,
|
|
if (error)
|
|
goto nomem_init;
|
|
|
|
- /* SCTP-AUTH: auth_chunk pointer is only set when the cookie-echo
|
|
- * is supposed to be authenticated and we have to do delayed
|
|
- * authentication. We've just recreated the association using
|
|
- * the information in the cookie and now it's much easier to
|
|
- * do the authentication.
|
|
- */
|
|
- if (chunk->auth_chunk) {
|
|
- struct sctp_chunk auth;
|
|
- sctp_ierror_t ret;
|
|
-
|
|
- /* Make sure that we and the peer are AUTH capable */
|
|
- if (!net->sctp.auth_enable || !new_asoc->peer.auth_capable) {
|
|
- sctp_association_free(new_asoc);
|
|
- return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
|
|
- }
|
|
-
|
|
- /* set-up our fake chunk so that we can process it */
|
|
- auth.skb = chunk->auth_chunk;
|
|
- auth.asoc = chunk->asoc;
|
|
- auth.sctp_hdr = chunk->sctp_hdr;
|
|
- auth.chunk_hdr = (sctp_chunkhdr_t *)skb_push(chunk->auth_chunk,
|
|
- sizeof(sctp_chunkhdr_t));
|
|
- skb_pull(chunk->auth_chunk, sizeof(sctp_chunkhdr_t));
|
|
- auth.transport = chunk->transport;
|
|
-
|
|
- ret = sctp_sf_authenticate(net, ep, new_asoc, type, &auth);
|
|
- if (ret != SCTP_IERROR_NO_ERROR) {
|
|
- sctp_association_free(new_asoc);
|
|
- return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
|
|
- }
|
|
+ if (!sctp_auth_chunk_verify(net, chunk, new_asoc)) {
|
|
+ sctp_association_free(new_asoc);
|
|
+ return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
|
|
}
|
|
|
|
repl = sctp_make_cookie_ack(new_asoc, chunk);
|
|
@@ -1717,13 +1720,15 @@ static sctp_disposition_t sctp_sf_do_dupcook_a(struct net *net,
|
|
GFP_ATOMIC))
|
|
goto nomem;
|
|
|
|
+ if (!sctp_auth_chunk_verify(net, chunk, new_asoc))
|
|
+ return SCTP_DISPOSITION_DISCARD;
|
|
+
|
|
/* Make sure no new addresses are being added during the
|
|
* restart. Though this is a pretty complicated attack
|
|
* since you'd have to get inside the cookie.
|
|
*/
|
|
- if (!sctp_sf_check_restart_addrs(new_asoc, asoc, chunk, commands)) {
|
|
+ if (!sctp_sf_check_restart_addrs(new_asoc, asoc, chunk, commands))
|
|
return SCTP_DISPOSITION_CONSUME;
|
|
- }
|
|
|
|
/* If the endpoint is in the SHUTDOWN-ACK-SENT state and recognizes
|
|
* the peer has restarted (Action A), it MUST NOT setup a new
|
|
@@ -1828,6 +1833,9 @@ static sctp_disposition_t sctp_sf_do_dupcook_b(struct net *net,
|
|
GFP_ATOMIC))
|
|
goto nomem;
|
|
|
|
+ if (!sctp_auth_chunk_verify(net, chunk, new_asoc))
|
|
+ return SCTP_DISPOSITION_DISCARD;
|
|
+
|
|
/* Update the content of current association. */
|
|
sctp_add_cmd_sf(commands, SCTP_CMD_UPDATE_ASSOC, SCTP_ASOC(new_asoc));
|
|
sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
|
|
@@ -1920,6 +1928,9 @@ static sctp_disposition_t sctp_sf_do_dupcook_d(struct net *net,
|
|
* a COOKIE ACK.
|
|
*/
|
|
|
|
+ if (!sctp_auth_chunk_verify(net, chunk, asoc))
|
|
+ return SCTP_DISPOSITION_DISCARD;
|
|
+
|
|
/* Don't accidentally move back into established state. */
|
|
if (asoc->state < SCTP_STATE_ESTABLISHED) {
|
|
sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
|
|
@@ -1959,7 +1970,7 @@ static sctp_disposition_t sctp_sf_do_dupcook_d(struct net *net,
|
|
}
|
|
}
|
|
|
|
- repl = sctp_make_cookie_ack(new_asoc, chunk);
|
|
+ repl = sctp_make_cookie_ack(asoc, chunk);
|
|
if (!repl)
|
|
goto nomem;
|
|
|
|
@@ -3985,10 +3996,8 @@ gen_shutdown:
|
|
*
|
|
* The return value is the disposition of the chunk.
|
|
*/
|
|
-static sctp_ierror_t sctp_sf_authenticate(struct net *net,
|
|
- const struct sctp_endpoint *ep,
|
|
+static sctp_ierror_t sctp_sf_authenticate(
|
|
const struct sctp_association *asoc,
|
|
- const sctp_subtype_t type,
|
|
struct sctp_chunk *chunk)
|
|
{
|
|
struct sctp_authhdr *auth_hdr;
|
|
@@ -4087,7 +4096,7 @@ sctp_disposition_t sctp_sf_eat_auth(struct net *net,
|
|
commands);
|
|
|
|
auth_hdr = (struct sctp_authhdr *)chunk->skb->data;
|
|
- error = sctp_sf_authenticate(net, ep, asoc, type, chunk);
|
|
+ error = sctp_sf_authenticate(asoc, chunk);
|
|
switch (error) {
|
|
case SCTP_IERROR_AUTH_BAD_HMAC:
|
|
/* Generate the ERROR chunk and discard the rest
|
|
diff --git a/net/wireless/core.c b/net/wireless/core.c
|
|
index eeaf83acba1b..a1e909ae0f78 100644
|
|
--- a/net/wireless/core.c
|
|
+++ b/net/wireless/core.c
|
|
@@ -94,6 +94,9 @@ static int cfg80211_dev_check_name(struct cfg80211_registered_device *rdev,
|
|
|
|
ASSERT_RTNL();
|
|
|
|
+ if (strlen(newname) > NL80211_WIPHY_NAME_MAXLEN)
|
|
+ return -EINVAL;
|
|
+
|
|
/* prohibit calling the thing phy%d when %d is not its number */
|
|
sscanf(newname, PHY_NAME "%d%n", &wiphy_idx, &taken);
|
|
if (taken == strlen(newname) && wiphy_idx != rdev->wiphy_idx) {
|
|
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
|
|
index 62d87f859566..d6a11af0bab1 100644
|
|
--- a/net/xfrm/xfrm_state.c
|
|
+++ b/net/xfrm/xfrm_state.c
|
|
@@ -1159,6 +1159,7 @@ static struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig)
|
|
|
|
if (orig->aead) {
|
|
x->aead = xfrm_algo_aead_clone(orig->aead);
|
|
+ x->geniv = orig->geniv;
|
|
if (!x->aead)
|
|
goto error;
|
|
}
|
|
diff --git a/sound/core/control_compat.c b/sound/core/control_compat.c
|
|
index 0608f216f359..ac0a40b9ba1e 100644
|
|
--- a/sound/core/control_compat.c
|
|
+++ b/sound/core/control_compat.c
|
|
@@ -400,8 +400,7 @@ static int snd_ctl_elem_add_compat(struct snd_ctl_file *file,
|
|
if (copy_from_user(&data->id, &data32->id, sizeof(data->id)) ||
|
|
copy_from_user(&data->type, &data32->type, 3 * sizeof(u32)))
|
|
goto error;
|
|
- if (get_user(data->owner, &data32->owner) ||
|
|
- get_user(data->type, &data32->type))
|
|
+ if (get_user(data->owner, &data32->owner))
|
|
goto error;
|
|
switch (data->type) {
|
|
case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
|
|
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
|
|
index 3be91696ac35..d0b55c866370 100644
|
|
--- a/sound/pci/hda/hda_intel.c
|
|
+++ b/sound/pci/hda/hda_intel.c
|
|
@@ -2072,6 +2072,8 @@ static struct snd_pci_quirk power_save_blacklist[] = {
|
|
SND_PCI_QUIRK(0x1849, 0x0c0c, "Asrock B85M-ITX", 0),
|
|
/* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */
|
|
SND_PCI_QUIRK(0x1043, 0x8733, "Asus Prime X370-Pro", 0),
|
|
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1572975 */
|
|
+ SND_PCI_QUIRK(0x17aa, 0x36a7, "Lenovo C50 All in one", 0),
|
|
/* https://bugzilla.kernel.org/show_bug.cgi?id=198611 */
|
|
SND_PCI_QUIRK(0x17aa, 0x2227, "Lenovo X1 Carbon 3rd Gen", 0),
|
|
{}
|
|
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
|
|
index c5447ff078b3..97d6a18e6956 100644
|
|
--- a/sound/usb/mixer.c
|
|
+++ b/sound/usb/mixer.c
|
|
@@ -904,6 +904,14 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval,
|
|
}
|
|
break;
|
|
|
|
+ case USB_ID(0x0d8c, 0x0103):
|
|
+ if (!strcmp(kctl->id.name, "PCM Playback Volume")) {
|
|
+ usb_audio_info(chip,
|
|
+ "set volume quirk for CM102-A+/102S+\n");
|
|
+ cval->min = -256;
|
|
+ }
|
|
+ break;
|
|
+
|
|
case USB_ID(0x0471, 0x0101):
|
|
case USB_ID(0x0471, 0x0104):
|
|
case USB_ID(0x0471, 0x0105):
|