mirror of
https://github.com/Fishwaldo/build.git
synced 2025-03-27 09:11:49 +00:00
4835 lines
151 KiB
Diff
4835 lines
151 KiB
Diff
diff --git a/Makefile b/Makefile
|
|
index fcfef30ca9a6..47a9f9883bdd 100644
|
|
--- a/Makefile
|
|
+++ b/Makefile
|
|
@@ -1,7 +1,7 @@
|
|
# SPDX-License-Identifier: GPL-2.0
|
|
VERSION = 4
|
|
PATCHLEVEL = 14
|
|
-SUBLEVEL = 113
|
|
+SUBLEVEL = 114
|
|
EXTRAVERSION =
|
|
NAME = Petit Gorille
|
|
|
|
@@ -653,8 +653,7 @@ KBUILD_CFLAGS += $(call cc-disable-warning, int-in-bool-context)
|
|
KBUILD_CFLAGS += $(call cc-disable-warning, attribute-alias)
|
|
|
|
ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE
|
|
-KBUILD_CFLAGS += $(call cc-option,-Oz,-Os)
|
|
-KBUILD_CFLAGS += $(call cc-disable-warning,maybe-uninitialized,)
|
|
+KBUILD_CFLAGS += -Os $(call cc-disable-warning,maybe-uninitialized,)
|
|
else
|
|
ifdef CONFIG_PROFILE_ALL_BRANCHES
|
|
KBUILD_CFLAGS += -O2 $(call cc-disable-warning,maybe-uninitialized,)
|
|
diff --git a/arch/arm64/include/asm/futex.h b/arch/arm64/include/asm/futex.h
|
|
index b447b4db423a..fd1e722f3821 100644
|
|
--- a/arch/arm64/include/asm/futex.h
|
|
+++ b/arch/arm64/include/asm/futex.h
|
|
@@ -50,7 +50,7 @@ do { \
|
|
static inline int
|
|
arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *_uaddr)
|
|
{
|
|
- int oldval, ret, tmp;
|
|
+ int oldval = 0, ret, tmp;
|
|
u32 __user *uaddr = __uaccess_mask_ptr(_uaddr);
|
|
|
|
pagefault_disable();
|
|
diff --git a/arch/x86/crypto/poly1305-avx2-x86_64.S b/arch/x86/crypto/poly1305-avx2-x86_64.S
|
|
index 3b6e70d085da..8457cdd47f75 100644
|
|
--- a/arch/x86/crypto/poly1305-avx2-x86_64.S
|
|
+++ b/arch/x86/crypto/poly1305-avx2-x86_64.S
|
|
@@ -323,6 +323,12 @@ ENTRY(poly1305_4block_avx2)
|
|
vpaddq t2,t1,t1
|
|
vmovq t1x,d4
|
|
|
|
+ # Now do a partial reduction mod (2^130)-5, carrying h0 -> h1 -> h2 ->
|
|
+ # h3 -> h4 -> h0 -> h1 to get h0,h2,h3,h4 < 2^26 and h1 < 2^26 + a small
|
|
+ # amount. Careful: we must not assume the carry bits 'd0 >> 26',
|
|
+ # 'd1 >> 26', 'd2 >> 26', 'd3 >> 26', and '(d4 >> 26) * 5' fit in 32-bit
|
|
+ # integers. It's true in a single-block implementation, but not here.
|
|
+
|
|
# d1 += d0 >> 26
|
|
mov d0,%rax
|
|
shr $26,%rax
|
|
@@ -361,16 +367,16 @@ ENTRY(poly1305_4block_avx2)
|
|
# h0 += (d4 >> 26) * 5
|
|
mov d4,%rax
|
|
shr $26,%rax
|
|
- lea (%eax,%eax,4),%eax
|
|
- add %eax,%ebx
|
|
+ lea (%rax,%rax,4),%rax
|
|
+ add %rax,%rbx
|
|
# h4 = d4 & 0x3ffffff
|
|
mov d4,%rax
|
|
and $0x3ffffff,%eax
|
|
mov %eax,h4
|
|
|
|
# h1 += h0 >> 26
|
|
- mov %ebx,%eax
|
|
- shr $26,%eax
|
|
+ mov %rbx,%rax
|
|
+ shr $26,%rax
|
|
add %eax,h1
|
|
# h0 = h0 & 0x3ffffff
|
|
andl $0x3ffffff,%ebx
|
|
diff --git a/arch/x86/crypto/poly1305-sse2-x86_64.S b/arch/x86/crypto/poly1305-sse2-x86_64.S
|
|
index c88c670cb5fc..5851c7418fb7 100644
|
|
--- a/arch/x86/crypto/poly1305-sse2-x86_64.S
|
|
+++ b/arch/x86/crypto/poly1305-sse2-x86_64.S
|
|
@@ -253,16 +253,16 @@ ENTRY(poly1305_block_sse2)
|
|
# h0 += (d4 >> 26) * 5
|
|
mov d4,%rax
|
|
shr $26,%rax
|
|
- lea (%eax,%eax,4),%eax
|
|
- add %eax,%ebx
|
|
+ lea (%rax,%rax,4),%rax
|
|
+ add %rax,%rbx
|
|
# h4 = d4 & 0x3ffffff
|
|
mov d4,%rax
|
|
and $0x3ffffff,%eax
|
|
mov %eax,h4
|
|
|
|
# h1 += h0 >> 26
|
|
- mov %ebx,%eax
|
|
- shr $26,%eax
|
|
+ mov %rbx,%rax
|
|
+ shr $26,%rax
|
|
add %eax,h1
|
|
# h0 = h0 & 0x3ffffff
|
|
andl $0x3ffffff,%ebx
|
|
@@ -520,6 +520,12 @@ ENTRY(poly1305_2block_sse2)
|
|
paddq t2,t1
|
|
movq t1,d4
|
|
|
|
+ # Now do a partial reduction mod (2^130)-5, carrying h0 -> h1 -> h2 ->
|
|
+ # h3 -> h4 -> h0 -> h1 to get h0,h2,h3,h4 < 2^26 and h1 < 2^26 + a small
|
|
+ # amount. Careful: we must not assume the carry bits 'd0 >> 26',
|
|
+ # 'd1 >> 26', 'd2 >> 26', 'd3 >> 26', and '(d4 >> 26) * 5' fit in 32-bit
|
|
+ # integers. It's true in a single-block implementation, but not here.
|
|
+
|
|
# d1 += d0 >> 26
|
|
mov d0,%rax
|
|
shr $26,%rax
|
|
@@ -558,16 +564,16 @@ ENTRY(poly1305_2block_sse2)
|
|
# h0 += (d4 >> 26) * 5
|
|
mov d4,%rax
|
|
shr $26,%rax
|
|
- lea (%eax,%eax,4),%eax
|
|
- add %eax,%ebx
|
|
+ lea (%rax,%rax,4),%rax
|
|
+ add %rax,%rbx
|
|
# h4 = d4 & 0x3ffffff
|
|
mov d4,%rax
|
|
and $0x3ffffff,%eax
|
|
mov %eax,h4
|
|
|
|
# h1 += h0 >> 26
|
|
- mov %ebx,%eax
|
|
- shr $26,%eax
|
|
+ mov %rbx,%rax
|
|
+ shr $26,%rax
|
|
add %eax,h1
|
|
# h0 = h0 & 0x3ffffff
|
|
andl $0x3ffffff,%ebx
|
|
diff --git a/arch/x86/events/amd/core.c b/arch/x86/events/amd/core.c
|
|
index 3e5dd85b019a..263af6312329 100644
|
|
--- a/arch/x86/events/amd/core.c
|
|
+++ b/arch/x86/events/amd/core.c
|
|
@@ -117,22 +117,39 @@ static __initconst const u64 amd_hw_cache_event_ids
|
|
};
|
|
|
|
/*
|
|
- * AMD Performance Monitor K7 and later.
|
|
+ * AMD Performance Monitor K7 and later, up to and including Family 16h:
|
|
*/
|
|
static const u64 amd_perfmon_event_map[PERF_COUNT_HW_MAX] =
|
|
{
|
|
- [PERF_COUNT_HW_CPU_CYCLES] = 0x0076,
|
|
- [PERF_COUNT_HW_INSTRUCTIONS] = 0x00c0,
|
|
- [PERF_COUNT_HW_CACHE_REFERENCES] = 0x077d,
|
|
- [PERF_COUNT_HW_CACHE_MISSES] = 0x077e,
|
|
- [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = 0x00c2,
|
|
- [PERF_COUNT_HW_BRANCH_MISSES] = 0x00c3,
|
|
- [PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] = 0x00d0, /* "Decoder empty" event */
|
|
- [PERF_COUNT_HW_STALLED_CYCLES_BACKEND] = 0x00d1, /* "Dispatch stalls" event */
|
|
+ [PERF_COUNT_HW_CPU_CYCLES] = 0x0076,
|
|
+ [PERF_COUNT_HW_INSTRUCTIONS] = 0x00c0,
|
|
+ [PERF_COUNT_HW_CACHE_REFERENCES] = 0x077d,
|
|
+ [PERF_COUNT_HW_CACHE_MISSES] = 0x077e,
|
|
+ [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = 0x00c2,
|
|
+ [PERF_COUNT_HW_BRANCH_MISSES] = 0x00c3,
|
|
+ [PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] = 0x00d0, /* "Decoder empty" event */
|
|
+ [PERF_COUNT_HW_STALLED_CYCLES_BACKEND] = 0x00d1, /* "Dispatch stalls" event */
|
|
+};
|
|
+
|
|
+/*
|
|
+ * AMD Performance Monitor Family 17h and later:
|
|
+ */
|
|
+static const u64 amd_f17h_perfmon_event_map[PERF_COUNT_HW_MAX] =
|
|
+{
|
|
+ [PERF_COUNT_HW_CPU_CYCLES] = 0x0076,
|
|
+ [PERF_COUNT_HW_INSTRUCTIONS] = 0x00c0,
|
|
+ [PERF_COUNT_HW_CACHE_REFERENCES] = 0xff60,
|
|
+ [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = 0x00c2,
|
|
+ [PERF_COUNT_HW_BRANCH_MISSES] = 0x00c3,
|
|
+ [PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] = 0x0287,
|
|
+ [PERF_COUNT_HW_STALLED_CYCLES_BACKEND] = 0x0187,
|
|
};
|
|
|
|
static u64 amd_pmu_event_map(int hw_event)
|
|
{
|
|
+ if (boot_cpu_data.x86 >= 0x17)
|
|
+ return amd_f17h_perfmon_event_map[hw_event];
|
|
+
|
|
return amd_perfmon_event_map[hw_event];
|
|
}
|
|
|
|
diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c
|
|
index dc8f8b3e6cec..99d45660242e 100644
|
|
--- a/arch/x86/events/intel/core.c
|
|
+++ b/arch/x86/events/intel/core.c
|
|
@@ -3001,7 +3001,7 @@ static unsigned long intel_pmu_free_running_flags(struct perf_event *event)
|
|
flags &= ~PERF_SAMPLE_TIME;
|
|
if (!event->attr.exclude_kernel)
|
|
flags &= ~PERF_SAMPLE_REGS_USER;
|
|
- if (event->attr.sample_regs_user & ~PEBS_REGS)
|
|
+ if (event->attr.sample_regs_user & ~PEBS_GP_REGS)
|
|
flags &= ~(PERF_SAMPLE_REGS_USER | PERF_SAMPLE_REGS_INTR);
|
|
return flags;
|
|
}
|
|
diff --git a/arch/x86/events/perf_event.h b/arch/x86/events/perf_event.h
|
|
index 84b3841c131d..bfe16631fd1d 100644
|
|
--- a/arch/x86/events/perf_event.h
|
|
+++ b/arch/x86/events/perf_event.h
|
|
@@ -95,25 +95,25 @@ struct amd_nb {
|
|
PERF_SAMPLE_TRANSACTION | PERF_SAMPLE_PHYS_ADDR | \
|
|
PERF_SAMPLE_REGS_INTR | PERF_SAMPLE_REGS_USER)
|
|
|
|
-#define PEBS_REGS \
|
|
- (PERF_REG_X86_AX | \
|
|
- PERF_REG_X86_BX | \
|
|
- PERF_REG_X86_CX | \
|
|
- PERF_REG_X86_DX | \
|
|
- PERF_REG_X86_DI | \
|
|
- PERF_REG_X86_SI | \
|
|
- PERF_REG_X86_SP | \
|
|
- PERF_REG_X86_BP | \
|
|
- PERF_REG_X86_IP | \
|
|
- PERF_REG_X86_FLAGS | \
|
|
- PERF_REG_X86_R8 | \
|
|
- PERF_REG_X86_R9 | \
|
|
- PERF_REG_X86_R10 | \
|
|
- PERF_REG_X86_R11 | \
|
|
- PERF_REG_X86_R12 | \
|
|
- PERF_REG_X86_R13 | \
|
|
- PERF_REG_X86_R14 | \
|
|
- PERF_REG_X86_R15)
|
|
+#define PEBS_GP_REGS \
|
|
+ ((1ULL << PERF_REG_X86_AX) | \
|
|
+ (1ULL << PERF_REG_X86_BX) | \
|
|
+ (1ULL << PERF_REG_X86_CX) | \
|
|
+ (1ULL << PERF_REG_X86_DX) | \
|
|
+ (1ULL << PERF_REG_X86_DI) | \
|
|
+ (1ULL << PERF_REG_X86_SI) | \
|
|
+ (1ULL << PERF_REG_X86_SP) | \
|
|
+ (1ULL << PERF_REG_X86_BP) | \
|
|
+ (1ULL << PERF_REG_X86_IP) | \
|
|
+ (1ULL << PERF_REG_X86_FLAGS) | \
|
|
+ (1ULL << PERF_REG_X86_R8) | \
|
|
+ (1ULL << PERF_REG_X86_R9) | \
|
|
+ (1ULL << PERF_REG_X86_R10) | \
|
|
+ (1ULL << PERF_REG_X86_R11) | \
|
|
+ (1ULL << PERF_REG_X86_R12) | \
|
|
+ (1ULL << PERF_REG_X86_R13) | \
|
|
+ (1ULL << PERF_REG_X86_R14) | \
|
|
+ (1ULL << PERF_REG_X86_R15))
|
|
|
|
/*
|
|
* Per register state.
|
|
diff --git a/arch/x86/kernel/cpu/bugs.c b/arch/x86/kernel/cpu/bugs.c
|
|
index ec7aedba3d74..5567705e0601 100644
|
|
--- a/arch/x86/kernel/cpu/bugs.c
|
|
+++ b/arch/x86/kernel/cpu/bugs.c
|
|
@@ -271,7 +271,7 @@ static const struct {
|
|
const char *option;
|
|
enum spectre_v2_user_cmd cmd;
|
|
bool secure;
|
|
-} v2_user_options[] __initdata = {
|
|
+} v2_user_options[] __initconst = {
|
|
{ "auto", SPECTRE_V2_USER_CMD_AUTO, false },
|
|
{ "off", SPECTRE_V2_USER_CMD_NONE, false },
|
|
{ "on", SPECTRE_V2_USER_CMD_FORCE, true },
|
|
@@ -406,7 +406,7 @@ static const struct {
|
|
const char *option;
|
|
enum spectre_v2_mitigation_cmd cmd;
|
|
bool secure;
|
|
-} mitigation_options[] __initdata = {
|
|
+} mitigation_options[] __initconst = {
|
|
{ "off", SPECTRE_V2_CMD_NONE, false },
|
|
{ "on", SPECTRE_V2_CMD_FORCE, true },
|
|
{ "retpoline", SPECTRE_V2_CMD_RETPOLINE, false },
|
|
@@ -642,7 +642,7 @@ static const char * const ssb_strings[] = {
|
|
static const struct {
|
|
const char *option;
|
|
enum ssb_mitigation_cmd cmd;
|
|
-} ssb_mitigation_options[] __initdata = {
|
|
+} ssb_mitigation_options[] __initconst = {
|
|
{ "auto", SPEC_STORE_BYPASS_CMD_AUTO }, /* Platform decides */
|
|
{ "on", SPEC_STORE_BYPASS_CMD_ON }, /* Disable Speculative Store Bypass */
|
|
{ "off", SPEC_STORE_BYPASS_CMD_NONE }, /* Don't touch Speculative Store Bypass */
|
|
diff --git a/arch/x86/kernel/kprobes/core.c b/arch/x86/kernel/kprobes/core.c
|
|
index 65452d555f05..56cf6c263254 100644
|
|
--- a/arch/x86/kernel/kprobes/core.c
|
|
+++ b/arch/x86/kernel/kprobes/core.c
|
|
@@ -553,6 +553,7 @@ void arch_prepare_kretprobe(struct kretprobe_instance *ri, struct pt_regs *regs)
|
|
unsigned long *sara = stack_addr(regs);
|
|
|
|
ri->ret_addr = (kprobe_opcode_t *) *sara;
|
|
+ ri->fp = sara;
|
|
|
|
/* Replace the return addr with trampoline addr */
|
|
*sara = (unsigned long) &kretprobe_trampoline;
|
|
@@ -754,15 +755,21 @@ __visible __used void *trampoline_handler(struct pt_regs *regs)
|
|
unsigned long flags, orig_ret_address = 0;
|
|
unsigned long trampoline_address = (unsigned long)&kretprobe_trampoline;
|
|
kprobe_opcode_t *correct_ret_addr = NULL;
|
|
+ void *frame_pointer;
|
|
+ bool skipped = false;
|
|
|
|
INIT_HLIST_HEAD(&empty_rp);
|
|
kretprobe_hash_lock(current, &head, &flags);
|
|
/* fixup registers */
|
|
#ifdef CONFIG_X86_64
|
|
regs->cs = __KERNEL_CS;
|
|
+ /* On x86-64, we use pt_regs->sp for return address holder. */
|
|
+ frame_pointer = ®s->sp;
|
|
#else
|
|
regs->cs = __KERNEL_CS | get_kernel_rpl();
|
|
regs->gs = 0;
|
|
+ /* On x86-32, we use pt_regs->flags for return address holder. */
|
|
+ frame_pointer = ®s->flags;
|
|
#endif
|
|
regs->ip = trampoline_address;
|
|
regs->orig_ax = ~0UL;
|
|
@@ -784,8 +791,25 @@ __visible __used void *trampoline_handler(struct pt_regs *regs)
|
|
if (ri->task != current)
|
|
/* another task is sharing our hash bucket */
|
|
continue;
|
|
+ /*
|
|
+ * Return probes must be pushed on this hash list correct
|
|
+ * order (same as return order) so that it can be poped
|
|
+ * correctly. However, if we find it is pushed it incorrect
|
|
+ * order, this means we find a function which should not be
|
|
+ * probed, because the wrong order entry is pushed on the
|
|
+ * path of processing other kretprobe itself.
|
|
+ */
|
|
+ if (ri->fp != frame_pointer) {
|
|
+ if (!skipped)
|
|
+ pr_warn("kretprobe is stacked incorrectly. Trying to fixup.\n");
|
|
+ skipped = true;
|
|
+ continue;
|
|
+ }
|
|
|
|
orig_ret_address = (unsigned long)ri->ret_addr;
|
|
+ if (skipped)
|
|
+ pr_warn("%ps must be blacklisted because of incorrect kretprobe order\n",
|
|
+ ri->rp->kp.addr);
|
|
|
|
if (orig_ret_address != trampoline_address)
|
|
/*
|
|
@@ -803,6 +827,8 @@ __visible __used void *trampoline_handler(struct pt_regs *regs)
|
|
if (ri->task != current)
|
|
/* another task is sharing our hash bucket */
|
|
continue;
|
|
+ if (ri->fp != frame_pointer)
|
|
+ continue;
|
|
|
|
orig_ret_address = (unsigned long)ri->ret_addr;
|
|
if (ri->rp && ri->rp->handler) {
|
|
diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c
|
|
index a98d1cdd6299..d2ef967bfafb 100644
|
|
--- a/arch/x86/kernel/process.c
|
|
+++ b/arch/x86/kernel/process.c
|
|
@@ -465,10 +465,12 @@ static unsigned long speculation_ctrl_update_tif(struct task_struct *tsk)
|
|
|
|
void speculation_ctrl_update(unsigned long tif)
|
|
{
|
|
+ unsigned long flags;
|
|
+
|
|
/* Forced update. Make sure all relevant TIF flags are different */
|
|
- preempt_disable();
|
|
+ local_irq_save(flags);
|
|
__speculation_ctrl_update(~tif, tif);
|
|
- preempt_enable();
|
|
+ local_irq_restore(flags);
|
|
}
|
|
|
|
/* Called from seccomp/prctl update */
|
|
diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
|
|
index 5f758568fc44..2bcadfc5b2f0 100644
|
|
--- a/arch/x86/kvm/emulate.c
|
|
+++ b/arch/x86/kvm/emulate.c
|
|
@@ -2588,15 +2588,13 @@ static int em_rsm(struct x86_emulate_ctxt *ctxt)
|
|
* CR0/CR3/CR4/EFER. It's all a bit more complicated if the vCPU
|
|
* supports long mode.
|
|
*/
|
|
- cr4 = ctxt->ops->get_cr(ctxt, 4);
|
|
if (emulator_has_longmode(ctxt)) {
|
|
struct desc_struct cs_desc;
|
|
|
|
/* Zero CR4.PCIDE before CR0.PG. */
|
|
- if (cr4 & X86_CR4_PCIDE) {
|
|
+ cr4 = ctxt->ops->get_cr(ctxt, 4);
|
|
+ if (cr4 & X86_CR4_PCIDE)
|
|
ctxt->ops->set_cr(ctxt, 4, cr4 & ~X86_CR4_PCIDE);
|
|
- cr4 &= ~X86_CR4_PCIDE;
|
|
- }
|
|
|
|
/* A 32-bit code segment is required to clear EFER.LMA. */
|
|
memset(&cs_desc, 0, sizeof(cs_desc));
|
|
@@ -2610,13 +2608,16 @@ static int em_rsm(struct x86_emulate_ctxt *ctxt)
|
|
if (cr0 & X86_CR0_PE)
|
|
ctxt->ops->set_cr(ctxt, 0, cr0 & ~(X86_CR0_PG | X86_CR0_PE));
|
|
|
|
- /* Now clear CR4.PAE (which must be done before clearing EFER.LME). */
|
|
- if (cr4 & X86_CR4_PAE)
|
|
- ctxt->ops->set_cr(ctxt, 4, cr4 & ~X86_CR4_PAE);
|
|
+ if (emulator_has_longmode(ctxt)) {
|
|
+ /* Clear CR4.PAE before clearing EFER.LME. */
|
|
+ cr4 = ctxt->ops->get_cr(ctxt, 4);
|
|
+ if (cr4 & X86_CR4_PAE)
|
|
+ ctxt->ops->set_cr(ctxt, 4, cr4 & ~X86_CR4_PAE);
|
|
|
|
- /* And finally go back to 32-bit mode. */
|
|
- efer = 0;
|
|
- ctxt->ops->set_msr(ctxt, MSR_EFER, efer);
|
|
+ /* And finally go back to 32-bit mode. */
|
|
+ efer = 0;
|
|
+ ctxt->ops->set_msr(ctxt, MSR_EFER, efer);
|
|
+ }
|
|
|
|
smbase = ctxt->ops->get_smbase(ctxt);
|
|
if (emulator_has_longmode(ctxt))
|
|
diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
|
|
index c387047e926a..1296e44fd969 100644
|
|
--- a/arch/x86/kvm/svm.c
|
|
+++ b/arch/x86/kvm/svm.c
|
|
@@ -2211,6 +2211,7 @@ static int pf_interception(struct vcpu_svm *svm)
|
|
static int db_interception(struct vcpu_svm *svm)
|
|
{
|
|
struct kvm_run *kvm_run = svm->vcpu.run;
|
|
+ struct kvm_vcpu *vcpu = &svm->vcpu;
|
|
|
|
if (!(svm->vcpu.guest_debug &
|
|
(KVM_GUESTDBG_SINGLESTEP | KVM_GUESTDBG_USE_HW_BP)) &&
|
|
@@ -2221,6 +2222,8 @@ static int db_interception(struct vcpu_svm *svm)
|
|
|
|
if (svm->nmi_singlestep) {
|
|
disable_nmi_singlestep(svm);
|
|
+ /* Make sure we check for pending NMIs upon entry */
|
|
+ kvm_make_request(KVM_REQ_EVENT, vcpu);
|
|
}
|
|
|
|
if (svm->vcpu.guest_debug &
|
|
@@ -4014,14 +4017,25 @@ static int avic_incomplete_ipi_interception(struct vcpu_svm *svm)
|
|
kvm_lapic_reg_write(apic, APIC_ICR, icrl);
|
|
break;
|
|
case AVIC_IPI_FAILURE_TARGET_NOT_RUNNING: {
|
|
+ int i;
|
|
+ struct kvm_vcpu *vcpu;
|
|
+ struct kvm *kvm = svm->vcpu.kvm;
|
|
struct kvm_lapic *apic = svm->vcpu.arch.apic;
|
|
|
|
/*
|
|
- * Update ICR high and low, then emulate sending IPI,
|
|
- * which is handled when writing APIC_ICR.
|
|
+ * At this point, we expect that the AVIC HW has already
|
|
+ * set the appropriate IRR bits on the valid target
|
|
+ * vcpus. So, we just need to kick the appropriate vcpu.
|
|
*/
|
|
- kvm_lapic_reg_write(apic, APIC_ICR2, icrh);
|
|
- kvm_lapic_reg_write(apic, APIC_ICR, icrl);
|
|
+ kvm_for_each_vcpu(i, vcpu, kvm) {
|
|
+ bool m = kvm_apic_match_dest(vcpu, apic,
|
|
+ icrl & KVM_APIC_SHORT_MASK,
|
|
+ GET_APIC_DEST_FIELD(icrh),
|
|
+ icrl & KVM_APIC_DEST_MASK);
|
|
+
|
|
+ if (m && !avic_vcpu_is_running(vcpu))
|
|
+ kvm_vcpu_wake_up(vcpu);
|
|
+ }
|
|
break;
|
|
}
|
|
case AVIC_IPI_FAILURE_INVALID_TARGET:
|
|
diff --git a/crypto/testmgr.h b/crypto/testmgr.h
|
|
index fbc0fab5e79e..12835f072614 100644
|
|
--- a/crypto/testmgr.h
|
|
+++ b/crypto/testmgr.h
|
|
@@ -4660,7 +4660,49 @@ static const struct hash_testvec poly1305_tv_template[] = {
|
|
.psize = 80,
|
|
.digest = "\x13\x00\x00\x00\x00\x00\x00\x00"
|
|
"\x00\x00\x00\x00\x00\x00\x00\x00",
|
|
- },
|
|
+ }, { /* Regression test for overflow in AVX2 implementation */
|
|
+ .plaintext = "\xff\xff\xff\xff\xff\xff\xff\xff"
|
|
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
|
|
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
|
|
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
|
|
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
|
|
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
|
|
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
|
|
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
|
|
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
|
|
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
|
|
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
|
|
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
|
|
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
|
|
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
|
|
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
|
|
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
|
|
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
|
|
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
|
|
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
|
|
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
|
|
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
|
|
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
|
|
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
|
|
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
|
|
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
|
|
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
|
|
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
|
|
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
|
|
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
|
|
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
|
|
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
|
|
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
|
|
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
|
|
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
|
|
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
|
|
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
|
|
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
|
|
+ "\xff\xff\xff\xff",
|
|
+ .psize = 300,
|
|
+ .digest = "\xfb\x5e\x96\xd8\x61\xd5\xc7\xc8"
|
|
+ "\x78\xe5\x87\xcc\x2d\x5a\x22\xe1",
|
|
+ }
|
|
};
|
|
|
|
/*
|
|
diff --git a/drivers/char/tpm/tpm_i2c_atmel.c b/drivers/char/tpm/tpm_i2c_atmel.c
|
|
index 32a8e27c5382..cc4e642d3180 100644
|
|
--- a/drivers/char/tpm/tpm_i2c_atmel.c
|
|
+++ b/drivers/char/tpm/tpm_i2c_atmel.c
|
|
@@ -69,6 +69,10 @@ static int i2c_atmel_send(struct tpm_chip *chip, u8 *buf, size_t len)
|
|
if (status < 0)
|
|
return status;
|
|
|
|
+ /* The upper layer does not support incomplete sends. */
|
|
+ if (status != len)
|
|
+ return -E2BIG;
|
|
+
|
|
return 0;
|
|
}
|
|
|
|
diff --git a/drivers/crypto/amcc/crypto4xx_alg.c b/drivers/crypto/amcc/crypto4xx_alg.c
|
|
index 4afca3968773..e3b8bebfdd30 100644
|
|
--- a/drivers/crypto/amcc/crypto4xx_alg.c
|
|
+++ b/drivers/crypto/amcc/crypto4xx_alg.c
|
|
@@ -138,7 +138,8 @@ static int crypto4xx_setkey_aes(struct crypto_ablkcipher *cipher,
|
|
sa = (struct dynamic_sa_ctl *) ctx->sa_in;
|
|
ctx->hash_final = 0;
|
|
|
|
- set_dynamic_sa_command_0(sa, SA_NOT_SAVE_HASH, SA_NOT_SAVE_IV,
|
|
+ set_dynamic_sa_command_0(sa, SA_NOT_SAVE_HASH, (cm == CRYPTO_MODE_CBC ?
|
|
+ SA_SAVE_IV : SA_NOT_SAVE_IV),
|
|
SA_LOAD_HASH_FROM_SA, SA_LOAD_IV_FROM_STATE,
|
|
SA_NO_HEADER_PROC, SA_HASH_ALG_NULL,
|
|
SA_CIPHER_ALG_AES, SA_PAD_TYPE_ZERO,
|
|
diff --git a/drivers/crypto/amcc/crypto4xx_core.c b/drivers/crypto/amcc/crypto4xx_core.c
|
|
index 3f9eee7e555f..8d4d8db244e9 100644
|
|
--- a/drivers/crypto/amcc/crypto4xx_core.c
|
|
+++ b/drivers/crypto/amcc/crypto4xx_core.c
|
|
@@ -645,6 +645,15 @@ static u32 crypto4xx_ablkcipher_done(struct crypto4xx_device *dev,
|
|
addr = dma_map_page(dev->core_dev->device, sg_page(dst),
|
|
dst->offset, dst->length, DMA_FROM_DEVICE);
|
|
}
|
|
+
|
|
+ if (pd_uinfo->sa_va->sa_command_0.bf.save_iv == SA_SAVE_IV) {
|
|
+ struct crypto_skcipher *skcipher = crypto_skcipher_reqtfm(req);
|
|
+
|
|
+ crypto4xx_memcpy_from_le32((u32 *)req->iv,
|
|
+ pd_uinfo->sr_va->save_iv,
|
|
+ crypto_skcipher_ivsize(skcipher));
|
|
+ }
|
|
+
|
|
crypto4xx_ret_sg_desc(dev, pd_uinfo);
|
|
if (ablk_req->base.complete != NULL)
|
|
ablk_req->base.complete(&ablk_req->base, 0);
|
|
diff --git a/drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c b/drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c
|
|
index 1d645c9ab417..cac262a912c1 100644
|
|
--- a/drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c
|
|
+++ b/drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c
|
|
@@ -337,7 +337,8 @@ static const struct dmi_system_id i2c_hid_dmi_desc_override_table[] = {
|
|
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "FlexBook edge11 - M-FBE11"),
|
|
},
|
|
.driver_data = (void *)&sipodev_desc
|
|
- }
|
|
+ },
|
|
+ { } /* Terminate list */
|
|
};
|
|
|
|
|
|
diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c
|
|
index 784636800361..780f886ccbfe 100644
|
|
--- a/drivers/iio/accel/kxcjk-1013.c
|
|
+++ b/drivers/iio/accel/kxcjk-1013.c
|
|
@@ -1340,6 +1340,8 @@ static int kxcjk1013_resume(struct device *dev)
|
|
|
|
mutex_lock(&data->mutex);
|
|
ret = kxcjk1013_set_mode(data, OPERATION);
|
|
+ if (ret == 0)
|
|
+ ret = kxcjk1013_set_range(data, data->range);
|
|
mutex_unlock(&data->mutex);
|
|
|
|
return ret;
|
|
diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c
|
|
index 22c4c17cd996..a1d072ecb717 100644
|
|
--- a/drivers/iio/adc/ad_sigma_delta.c
|
|
+++ b/drivers/iio/adc/ad_sigma_delta.c
|
|
@@ -121,6 +121,7 @@ static int ad_sd_read_reg_raw(struct ad_sigma_delta *sigma_delta,
|
|
if (sigma_delta->info->has_registers) {
|
|
data[0] = reg << sigma_delta->info->addr_shift;
|
|
data[0] |= sigma_delta->info->read_mask;
|
|
+ data[0] |= sigma_delta->comm;
|
|
spi_message_add_tail(&t[0], &m);
|
|
}
|
|
spi_message_add_tail(&t[1], &m);
|
|
diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c
|
|
index cd686179aa92..492f6c8ba735 100644
|
|
--- a/drivers/iio/adc/at91_adc.c
|
|
+++ b/drivers/iio/adc/at91_adc.c
|
|
@@ -705,23 +705,29 @@ static int at91_adc_read_raw(struct iio_dev *idev,
|
|
ret = wait_event_interruptible_timeout(st->wq_data_avail,
|
|
st->done,
|
|
msecs_to_jiffies(1000));
|
|
- if (ret == 0)
|
|
- ret = -ETIMEDOUT;
|
|
- if (ret < 0) {
|
|
- mutex_unlock(&st->lock);
|
|
- return ret;
|
|
- }
|
|
-
|
|
- *val = st->last_value;
|
|
|
|
+ /* Disable interrupts, regardless if adc conversion was
|
|
+ * successful or not
|
|
+ */
|
|
at91_adc_writel(st, AT91_ADC_CHDR,
|
|
AT91_ADC_CH(chan->channel));
|
|
at91_adc_writel(st, AT91_ADC_IDR, BIT(chan->channel));
|
|
|
|
- st->last_value = 0;
|
|
- st->done = false;
|
|
+ if (ret > 0) {
|
|
+ /* a valid conversion took place */
|
|
+ *val = st->last_value;
|
|
+ st->last_value = 0;
|
|
+ st->done = false;
|
|
+ ret = IIO_VAL_INT;
|
|
+ } else if (ret == 0) {
|
|
+ /* conversion timeout */
|
|
+ dev_err(&idev->dev, "ADC Channel %d timeout.\n",
|
|
+ chan->channel);
|
|
+ ret = -ETIMEDOUT;
|
|
+ }
|
|
+
|
|
mutex_unlock(&st->lock);
|
|
- return IIO_VAL_INT;
|
|
+ return ret;
|
|
|
|
case IIO_CHAN_INFO_SCALE:
|
|
*val = st->vref_mv;
|
|
diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
|
|
index 38e8783e4b05..287fbe08264d 100644
|
|
--- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
|
|
+++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
|
|
@@ -104,9 +104,10 @@ static int cros_ec_sensors_read(struct iio_dev *indio_dev,
|
|
* Do not use IIO_DEGREE_TO_RAD to avoid precision
|
|
* loss. Round to the nearest integer.
|
|
*/
|
|
- *val = div_s64(val64 * 314159 + 9000000ULL, 1000);
|
|
- *val2 = 18000 << (CROS_EC_SENSOR_BITS - 1);
|
|
- ret = IIO_VAL_FRACTIONAL;
|
|
+ *val = 0;
|
|
+ *val2 = div_s64(val64 * 3141592653ULL,
|
|
+ 180 << (CROS_EC_SENSOR_BITS - 1));
|
|
+ ret = IIO_VAL_INT_PLUS_NANO;
|
|
break;
|
|
case MOTIONSENSE_TYPE_MAG:
|
|
/*
|
|
diff --git a/drivers/iio/dac/mcp4725.c b/drivers/iio/dac/mcp4725.c
|
|
index 6ab1f23e5a79..fe3e42defb33 100644
|
|
--- a/drivers/iio/dac/mcp4725.c
|
|
+++ b/drivers/iio/dac/mcp4725.c
|
|
@@ -98,6 +98,7 @@ static ssize_t mcp4725_store_eeprom(struct device *dev,
|
|
|
|
inoutbuf[0] = 0x60; /* write EEPROM */
|
|
inoutbuf[0] |= data->ref_mode << 3;
|
|
+ inoutbuf[0] |= data->powerdown ? ((data->powerdown_mode + 1) << 1) : 0;
|
|
inoutbuf[1] = data->dac_value >> 4;
|
|
inoutbuf[2] = (data->dac_value & 0xf) << 4;
|
|
|
|
diff --git a/drivers/iio/gyro/bmg160_core.c b/drivers/iio/gyro/bmg160_core.c
|
|
index 821919dd245b..b5a5517e3ce1 100644
|
|
--- a/drivers/iio/gyro/bmg160_core.c
|
|
+++ b/drivers/iio/gyro/bmg160_core.c
|
|
@@ -583,11 +583,10 @@ static int bmg160_read_raw(struct iio_dev *indio_dev,
|
|
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
|
|
return bmg160_get_filter(data, val);
|
|
case IIO_CHAN_INFO_SCALE:
|
|
- *val = 0;
|
|
switch (chan->type) {
|
|
case IIO_TEMP:
|
|
- *val2 = 500000;
|
|
- return IIO_VAL_INT_PLUS_MICRO;
|
|
+ *val = 500;
|
|
+ return IIO_VAL_INT;
|
|
case IIO_ANGL_VEL:
|
|
{
|
|
int i;
|
|
@@ -595,6 +594,7 @@ static int bmg160_read_raw(struct iio_dev *indio_dev,
|
|
for (i = 0; i < ARRAY_SIZE(bmg160_scale_table); ++i) {
|
|
if (bmg160_scale_table[i].dps_range ==
|
|
data->dps_range) {
|
|
+ *val = 0;
|
|
*val2 = bmg160_scale_table[i].scale;
|
|
return IIO_VAL_INT_PLUS_MICRO;
|
|
}
|
|
diff --git a/drivers/iio/gyro/mpu3050-core.c b/drivers/iio/gyro/mpu3050-core.c
|
|
index e0d241a9aa30..a7be4670bf8f 100644
|
|
--- a/drivers/iio/gyro/mpu3050-core.c
|
|
+++ b/drivers/iio/gyro/mpu3050-core.c
|
|
@@ -29,7 +29,8 @@
|
|
|
|
#include "mpu3050.h"
|
|
|
|
-#define MPU3050_CHIP_ID 0x69
|
|
+#define MPU3050_CHIP_ID 0x68
|
|
+#define MPU3050_CHIP_ID_MASK 0x7E
|
|
|
|
/*
|
|
* Register map: anything suffixed *_H is a big-endian high byte and always
|
|
@@ -1178,8 +1179,9 @@ int mpu3050_common_probe(struct device *dev,
|
|
goto err_power_down;
|
|
}
|
|
|
|
- if (val != MPU3050_CHIP_ID) {
|
|
- dev_err(dev, "unsupported chip id %02x\n", (u8)val);
|
|
+ if ((val & MPU3050_CHIP_ID_MASK) != MPU3050_CHIP_ID) {
|
|
+ dev_err(dev, "unsupported chip id %02x\n",
|
|
+ (u8)(val & MPU3050_CHIP_ID_MASK));
|
|
ret = -ENODEV;
|
|
goto err_power_down;
|
|
}
|
|
diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
|
|
index 78482d456c3b..d50125766093 100644
|
|
--- a/drivers/iio/industrialio-buffer.c
|
|
+++ b/drivers/iio/industrialio-buffer.c
|
|
@@ -320,9 +320,8 @@ static int iio_scan_mask_set(struct iio_dev *indio_dev,
|
|
const unsigned long *mask;
|
|
unsigned long *trialmask;
|
|
|
|
- trialmask = kmalloc_array(BITS_TO_LONGS(indio_dev->masklength),
|
|
- sizeof(*trialmask),
|
|
- GFP_KERNEL);
|
|
+ trialmask = kcalloc(BITS_TO_LONGS(indio_dev->masklength),
|
|
+ sizeof(*trialmask), GFP_KERNEL);
|
|
if (trialmask == NULL)
|
|
return -ENOMEM;
|
|
if (!indio_dev->masklength) {
|
|
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
|
|
index e565fd4fc414..97b7266ee0ff 100644
|
|
--- a/drivers/iio/industrialio-core.c
|
|
+++ b/drivers/iio/industrialio-core.c
|
|
@@ -1741,10 +1741,10 @@ EXPORT_SYMBOL(iio_device_register);
|
|
**/
|
|
void iio_device_unregister(struct iio_dev *indio_dev)
|
|
{
|
|
- mutex_lock(&indio_dev->info_exist_lock);
|
|
-
|
|
cdev_device_del(&indio_dev->chrdev, &indio_dev->dev);
|
|
|
|
+ mutex_lock(&indio_dev->info_exist_lock);
|
|
+
|
|
iio_device_unregister_debugfs(indio_dev);
|
|
|
|
iio_disable_all_buffers(indio_dev);
|
|
diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
|
|
index ff5c4ad37a3a..8c0b80a54e4d 100644
|
|
--- a/drivers/mmc/host/sdhci-esdhc-imx.c
|
|
+++ b/drivers/mmc/host/sdhci-esdhc-imx.c
|
|
@@ -425,7 +425,7 @@ static u16 esdhc_readw_le(struct sdhci_host *host, int reg)
|
|
val = readl(host->ioaddr + ESDHC_MIX_CTRL);
|
|
else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING)
|
|
/* the std tuning bits is in ACMD12_ERR for imx6sl */
|
|
- val = readl(host->ioaddr + SDHCI_ACMD12_ERR);
|
|
+ val = readl(host->ioaddr + SDHCI_AUTO_CMD_STATUS);
|
|
}
|
|
|
|
if (val & ESDHC_MIX_CTRL_EXE_TUNE)
|
|
@@ -490,7 +490,7 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
|
|
}
|
|
writel(new_val , host->ioaddr + ESDHC_MIX_CTRL);
|
|
} else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
|
|
- u32 v = readl(host->ioaddr + SDHCI_ACMD12_ERR);
|
|
+ u32 v = readl(host->ioaddr + SDHCI_AUTO_CMD_STATUS);
|
|
u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
|
|
if (val & SDHCI_CTRL_TUNED_CLK) {
|
|
v |= ESDHC_MIX_CTRL_SMPCLK_SEL;
|
|
@@ -508,7 +508,7 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
|
|
v &= ~ESDHC_MIX_CTRL_EXE_TUNE;
|
|
}
|
|
|
|
- writel(v, host->ioaddr + SDHCI_ACMD12_ERR);
|
|
+ writel(v, host->ioaddr + SDHCI_AUTO_CMD_STATUS);
|
|
writel(m, host->ioaddr + ESDHC_MIX_CTRL);
|
|
}
|
|
return;
|
|
@@ -937,9 +937,9 @@ static void esdhc_reset_tuning(struct sdhci_host *host)
|
|
writel(ctrl, host->ioaddr + ESDHC_MIX_CTRL);
|
|
writel(0, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
|
|
} else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
|
|
- ctrl = readl(host->ioaddr + SDHCI_ACMD12_ERR);
|
|
+ ctrl = readl(host->ioaddr + SDHCI_AUTO_CMD_STATUS);
|
|
ctrl &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
|
|
- writel(ctrl, host->ioaddr + SDHCI_ACMD12_ERR);
|
|
+ writel(ctrl, host->ioaddr + SDHCI_AUTO_CMD_STATUS);
|
|
}
|
|
}
|
|
}
|
|
@@ -1303,7 +1303,7 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
|
|
|
|
/* clear tuning bits in case ROM has set it already */
|
|
writel(0x0, host->ioaddr + ESDHC_MIX_CTRL);
|
|
- writel(0x0, host->ioaddr + SDHCI_ACMD12_ERR);
|
|
+ writel(0x0, host->ioaddr + SDHCI_AUTO_CMD_STATUS);
|
|
writel(0x0, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
|
|
}
|
|
|
|
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
|
|
index 0edcc2763f3c..9540fda7fc6b 100644
|
|
--- a/drivers/mmc/host/sdhci.c
|
|
+++ b/drivers/mmc/host/sdhci.c
|
|
@@ -82,8 +82,8 @@ void sdhci_dumpregs(struct sdhci_host *host)
|
|
SDHCI_DUMP("Int enab: 0x%08x | Sig enab: 0x%08x\n",
|
|
sdhci_readl(host, SDHCI_INT_ENABLE),
|
|
sdhci_readl(host, SDHCI_SIGNAL_ENABLE));
|
|
- SDHCI_DUMP("AC12 err: 0x%08x | Slot int: 0x%08x\n",
|
|
- sdhci_readw(host, SDHCI_ACMD12_ERR),
|
|
+ SDHCI_DUMP("ACmd stat: 0x%08x | Slot int: 0x%08x\n",
|
|
+ sdhci_readw(host, SDHCI_AUTO_CMD_STATUS),
|
|
sdhci_readw(host, SDHCI_SLOT_INT_STATUS));
|
|
SDHCI_DUMP("Caps: 0x%08x | Caps_1: 0x%08x\n",
|
|
sdhci_readl(host, SDHCI_CAPABILITIES),
|
|
@@ -790,6 +790,11 @@ static void sdhci_set_transfer_irqs(struct sdhci_host *host)
|
|
else
|
|
host->ier = (host->ier & ~dma_irqs) | pio_irqs;
|
|
|
|
+ if (host->flags & (SDHCI_AUTO_CMD23 | SDHCI_AUTO_CMD12))
|
|
+ host->ier |= SDHCI_INT_AUTO_CMD_ERR;
|
|
+ else
|
|
+ host->ier &= ~SDHCI_INT_AUTO_CMD_ERR;
|
|
+
|
|
sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
|
|
sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
|
|
}
|
|
@@ -1002,8 +1007,7 @@ static bool sdhci_needs_reset(struct sdhci_host *host, struct mmc_request *mrq)
|
|
return (!(host->flags & SDHCI_DEVICE_DEAD) &&
|
|
((mrq->cmd && mrq->cmd->error) ||
|
|
(mrq->sbc && mrq->sbc->error) ||
|
|
- (mrq->data && ((mrq->data->error && !mrq->data->stop) ||
|
|
- (mrq->data->stop && mrq->data->stop->error))) ||
|
|
+ (mrq->data && mrq->data->stop && mrq->data->stop->error) ||
|
|
(host->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST)));
|
|
}
|
|
|
|
@@ -1055,6 +1059,16 @@ static void sdhci_finish_data(struct sdhci_host *host)
|
|
host->data = NULL;
|
|
host->data_cmd = NULL;
|
|
|
|
+ /*
|
|
+ * The controller needs a reset of internal state machines upon error
|
|
+ * conditions.
|
|
+ */
|
|
+ if (data->error) {
|
|
+ if (!host->cmd || host->cmd == data_cmd)
|
|
+ sdhci_do_reset(host, SDHCI_RESET_CMD);
|
|
+ sdhci_do_reset(host, SDHCI_RESET_DATA);
|
|
+ }
|
|
+
|
|
if ((host->flags & (SDHCI_REQ_USE_DMA | SDHCI_USE_ADMA)) ==
|
|
(SDHCI_REQ_USE_DMA | SDHCI_USE_ADMA))
|
|
sdhci_adma_table_post(host, data);
|
|
@@ -1079,17 +1093,6 @@ static void sdhci_finish_data(struct sdhci_host *host)
|
|
if (data->stop &&
|
|
(data->error ||
|
|
!data->mrq->sbc)) {
|
|
-
|
|
- /*
|
|
- * The controller needs a reset of internal state machines
|
|
- * upon error conditions.
|
|
- */
|
|
- if (data->error) {
|
|
- if (!host->cmd || host->cmd == data_cmd)
|
|
- sdhci_do_reset(host, SDHCI_RESET_CMD);
|
|
- sdhci_do_reset(host, SDHCI_RESET_DATA);
|
|
- }
|
|
-
|
|
/*
|
|
* 'cap_cmd_during_tfr' request must not use the command line
|
|
* after mmc_command_done() has been called. It is upper layer's
|
|
@@ -2560,8 +2563,23 @@ static void sdhci_timeout_data_timer(unsigned long data)
|
|
* *
|
|
\*****************************************************************************/
|
|
|
|
-static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask)
|
|
+static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *intmask_p)
|
|
{
|
|
+ /* Handle auto-CMD12 error */
|
|
+ if (intmask & SDHCI_INT_AUTO_CMD_ERR && host->data_cmd) {
|
|
+ struct mmc_request *mrq = host->data_cmd->mrq;
|
|
+ u16 auto_cmd_status = sdhci_readw(host, SDHCI_AUTO_CMD_STATUS);
|
|
+ int data_err_bit = (auto_cmd_status & SDHCI_AUTO_CMD_TIMEOUT) ?
|
|
+ SDHCI_INT_DATA_TIMEOUT :
|
|
+ SDHCI_INT_DATA_CRC;
|
|
+
|
|
+ /* Treat auto-CMD12 error the same as data error */
|
|
+ if (!mrq->sbc && (host->flags & SDHCI_AUTO_CMD12)) {
|
|
+ *intmask_p |= data_err_bit;
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+
|
|
if (!host->cmd) {
|
|
/*
|
|
* SDHCI recovers from errors by resetting the cmd and data
|
|
@@ -2583,20 +2601,12 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask)
|
|
else
|
|
host->cmd->error = -EILSEQ;
|
|
|
|
- /*
|
|
- * If this command initiates a data phase and a response
|
|
- * CRC error is signalled, the card can start transferring
|
|
- * data - the card may have received the command without
|
|
- * error. We must not terminate the mmc_request early.
|
|
- *
|
|
- * If the card did not receive the command or returned an
|
|
- * error which prevented it sending data, the data phase
|
|
- * will time out.
|
|
- */
|
|
+ /* Treat data command CRC error the same as data CRC error */
|
|
if (host->cmd->data &&
|
|
(intmask & (SDHCI_INT_CRC | SDHCI_INT_TIMEOUT)) ==
|
|
SDHCI_INT_CRC) {
|
|
host->cmd = NULL;
|
|
+ *intmask_p |= SDHCI_INT_DATA_CRC;
|
|
return;
|
|
}
|
|
|
|
@@ -2604,6 +2614,21 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask)
|
|
return;
|
|
}
|
|
|
|
+ /* Handle auto-CMD23 error */
|
|
+ if (intmask & SDHCI_INT_AUTO_CMD_ERR) {
|
|
+ struct mmc_request *mrq = host->cmd->mrq;
|
|
+ u16 auto_cmd_status = sdhci_readw(host, SDHCI_AUTO_CMD_STATUS);
|
|
+ int err = (auto_cmd_status & SDHCI_AUTO_CMD_TIMEOUT) ?
|
|
+ -ETIMEDOUT :
|
|
+ -EILSEQ;
|
|
+
|
|
+ if (mrq->sbc && (host->flags & SDHCI_AUTO_CMD23)) {
|
|
+ mrq->sbc->error = err;
|
|
+ sdhci_finish_mrq(host, mrq);
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+
|
|
if (intmask & SDHCI_INT_RESPONSE)
|
|
sdhci_finish_command(host);
|
|
}
|
|
@@ -2824,7 +2849,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
|
|
}
|
|
|
|
if (intmask & SDHCI_INT_CMD_MASK)
|
|
- sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK);
|
|
+ sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK, &intmask);
|
|
|
|
if (intmask & SDHCI_INT_DATA_MASK)
|
|
sdhci_data_irq(host, intmask & SDHCI_INT_DATA_MASK);
|
|
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
|
|
index 1d7d61e25dbf..c0d5458c36d4 100644
|
|
--- a/drivers/mmc/host/sdhci.h
|
|
+++ b/drivers/mmc/host/sdhci.h
|
|
@@ -144,14 +144,15 @@
|
|
#define SDHCI_INT_DATA_CRC 0x00200000
|
|
#define SDHCI_INT_DATA_END_BIT 0x00400000
|
|
#define SDHCI_INT_BUS_POWER 0x00800000
|
|
-#define SDHCI_INT_ACMD12ERR 0x01000000
|
|
+#define SDHCI_INT_AUTO_CMD_ERR 0x01000000
|
|
#define SDHCI_INT_ADMA_ERROR 0x02000000
|
|
|
|
#define SDHCI_INT_NORMAL_MASK 0x00007FFF
|
|
#define SDHCI_INT_ERROR_MASK 0xFFFF8000
|
|
|
|
#define SDHCI_INT_CMD_MASK (SDHCI_INT_RESPONSE | SDHCI_INT_TIMEOUT | \
|
|
- SDHCI_INT_CRC | SDHCI_INT_END_BIT | SDHCI_INT_INDEX)
|
|
+ SDHCI_INT_CRC | SDHCI_INT_END_BIT | SDHCI_INT_INDEX | \
|
|
+ SDHCI_INT_AUTO_CMD_ERR)
|
|
#define SDHCI_INT_DATA_MASK (SDHCI_INT_DATA_END | SDHCI_INT_DMA_END | \
|
|
SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | \
|
|
SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_DATA_CRC | \
|
|
@@ -166,7 +167,11 @@
|
|
|
|
#define SDHCI_CQE_INT_MASK (SDHCI_CQE_INT_ERR_MASK | SDHCI_INT_CQE)
|
|
|
|
-#define SDHCI_ACMD12_ERR 0x3C
|
|
+#define SDHCI_AUTO_CMD_STATUS 0x3C
|
|
+#define SDHCI_AUTO_CMD_TIMEOUT 0x00000002
|
|
+#define SDHCI_AUTO_CMD_CRC 0x00000004
|
|
+#define SDHCI_AUTO_CMD_END_BIT 0x00000008
|
|
+#define SDHCI_AUTO_CMD_INDEX 0x00000010
|
|
|
|
#define SDHCI_HOST_CONTROL2 0x3E
|
|
#define SDHCI_CTRL_UHS_MASK 0x0007
|
|
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
|
|
index 99e60bb5fe07..1edd4ff5382c 100644
|
|
--- a/drivers/net/bonding/bond_main.c
|
|
+++ b/drivers/net/bonding/bond_main.c
|
|
@@ -3169,8 +3169,12 @@ static int bond_netdev_event(struct notifier_block *this,
|
|
return NOTIFY_DONE;
|
|
|
|
if (event_dev->flags & IFF_MASTER) {
|
|
+ int ret;
|
|
+
|
|
netdev_dbg(event_dev, "IFF_MASTER\n");
|
|
- return bond_master_netdev_event(event, event_dev);
|
|
+ ret = bond_master_netdev_event(event, event_dev);
|
|
+ if (ret != NOTIFY_DONE)
|
|
+ return ret;
|
|
}
|
|
|
|
if (event_dev->flags & IFF_SLAVE) {
|
|
diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_main.c b/drivers/net/ethernet/cavium/thunder/nicvf_main.c
|
|
index 59b62b49ad48..98734a37b6f6 100644
|
|
--- a/drivers/net/ethernet/cavium/thunder/nicvf_main.c
|
|
+++ b/drivers/net/ethernet/cavium/thunder/nicvf_main.c
|
|
@@ -29,6 +29,13 @@
|
|
#define DRV_NAME "thunder-nicvf"
|
|
#define DRV_VERSION "1.0"
|
|
|
|
+/* NOTE: Packets bigger than 1530 are split across multiple pages and XDP needs
|
|
+ * the buffer to be contiguous. Allow XDP to be set up only if we don't exceed
|
|
+ * this value, keeping headroom for the 14 byte Ethernet header and two
|
|
+ * VLAN tags (for QinQ)
|
|
+ */
|
|
+#define MAX_XDP_MTU (1530 - ETH_HLEN - VLAN_HLEN * 2)
|
|
+
|
|
/* Supported devices */
|
|
static const struct pci_device_id nicvf_id_table[] = {
|
|
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM,
|
|
@@ -1454,6 +1461,15 @@ static int nicvf_change_mtu(struct net_device *netdev, int new_mtu)
|
|
struct nicvf *nic = netdev_priv(netdev);
|
|
int orig_mtu = netdev->mtu;
|
|
|
|
+ /* For now just support only the usual MTU sized frames,
|
|
+ * plus some headroom for VLAN, QinQ.
|
|
+ */
|
|
+ if (nic->xdp_prog && new_mtu > MAX_XDP_MTU) {
|
|
+ netdev_warn(netdev, "Jumbo frames not yet supported with XDP, current MTU %d.\n",
|
|
+ netdev->mtu);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
netdev->mtu = new_mtu;
|
|
|
|
if (!netif_running(netdev))
|
|
@@ -1702,8 +1718,10 @@ static int nicvf_xdp_setup(struct nicvf *nic, struct bpf_prog *prog)
|
|
bool bpf_attached = false;
|
|
int ret = 0;
|
|
|
|
- /* For now just support only the usual MTU sized frames */
|
|
- if (prog && (dev->mtu > 1500)) {
|
|
+ /* For now just support only the usual MTU sized frames,
|
|
+ * plus some headroom for VLAN, QinQ.
|
|
+ */
|
|
+ if (prog && dev->mtu > MAX_XDP_MTU) {
|
|
netdev_warn(dev, "Jumbo frames not yet supported with XDP, current MTU %d.\n",
|
|
dev->mtu);
|
|
return -EOPNOTSUPP;
|
|
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
|
|
index bb96153f496e..fea141e71705 100644
|
|
--- a/drivers/net/team/team.c
|
|
+++ b/drivers/net/team/team.c
|
|
@@ -1245,6 +1245,23 @@ static int team_port_add(struct team *team, struct net_device *port_dev)
|
|
goto err_option_port_add;
|
|
}
|
|
|
|
+ /* set promiscuity level to new slave */
|
|
+ if (dev->flags & IFF_PROMISC) {
|
|
+ err = dev_set_promiscuity(port_dev, 1);
|
|
+ if (err)
|
|
+ goto err_set_slave_promisc;
|
|
+ }
|
|
+
|
|
+ /* set allmulti level to new slave */
|
|
+ if (dev->flags & IFF_ALLMULTI) {
|
|
+ err = dev_set_allmulti(port_dev, 1);
|
|
+ if (err) {
|
|
+ if (dev->flags & IFF_PROMISC)
|
|
+ dev_set_promiscuity(port_dev, -1);
|
|
+ goto err_set_slave_promisc;
|
|
+ }
|
|
+ }
|
|
+
|
|
netif_addr_lock_bh(dev);
|
|
dev_uc_sync_multiple(port_dev, dev);
|
|
dev_mc_sync_multiple(port_dev, dev);
|
|
@@ -1261,6 +1278,9 @@ static int team_port_add(struct team *team, struct net_device *port_dev)
|
|
|
|
return 0;
|
|
|
|
+err_set_slave_promisc:
|
|
+ __team_option_inst_del_port(team, port);
|
|
+
|
|
err_option_port_add:
|
|
team_upper_dev_unlink(team, port);
|
|
|
|
@@ -1306,6 +1326,12 @@ static int team_port_del(struct team *team, struct net_device *port_dev)
|
|
|
|
team_port_disable(team, port);
|
|
list_del_rcu(&port->list);
|
|
+
|
|
+ if (dev->flags & IFF_PROMISC)
|
|
+ dev_set_promiscuity(port_dev, -1);
|
|
+ if (dev->flags & IFF_ALLMULTI)
|
|
+ dev_set_allmulti(port_dev, -1);
|
|
+
|
|
team_upper_dev_unlink(team, port);
|
|
netdev_rx_handler_unregister(port_dev);
|
|
team_port_disable_netpoll(port);
|
|
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00.h b/drivers/net/wireless/ralink/rt2x00/rt2x00.h
|
|
index 1f38c338ca7a..2a25996d058d 100644
|
|
--- a/drivers/net/wireless/ralink/rt2x00/rt2x00.h
|
|
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00.h
|
|
@@ -672,7 +672,6 @@ enum rt2x00_state_flags {
|
|
CONFIG_CHANNEL_HT40,
|
|
CONFIG_POWERSAVING,
|
|
CONFIG_HT_DISABLED,
|
|
- CONFIG_QOS_DISABLED,
|
|
CONFIG_MONITORING,
|
|
|
|
/*
|
|
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00mac.c b/drivers/net/wireless/ralink/rt2x00/rt2x00mac.c
|
|
index 6fe0c6abe0d6..84728c281f46 100644
|
|
--- a/drivers/net/wireless/ralink/rt2x00/rt2x00mac.c
|
|
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00mac.c
|
|
@@ -670,18 +670,8 @@ void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw,
|
|
rt2x00dev->intf_associated--;
|
|
|
|
rt2x00leds_led_assoc(rt2x00dev, !!rt2x00dev->intf_associated);
|
|
-
|
|
- clear_bit(CONFIG_QOS_DISABLED, &rt2x00dev->flags);
|
|
}
|
|
|
|
- /*
|
|
- * Check for access point which do not support 802.11e . We have to
|
|
- * generate data frames sequence number in S/W for such AP, because
|
|
- * of H/W bug.
|
|
- */
|
|
- if (changes & BSS_CHANGED_QOS && !bss_conf->qos)
|
|
- set_bit(CONFIG_QOS_DISABLED, &rt2x00dev->flags);
|
|
-
|
|
/*
|
|
* When the erp information has changed, we should perform
|
|
* additional configuration steps. For all other changes we are done.
|
|
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00queue.c b/drivers/net/wireless/ralink/rt2x00/rt2x00queue.c
|
|
index e1660b92b20c..1b0f2da8a10d 100644
|
|
--- a/drivers/net/wireless/ralink/rt2x00/rt2x00queue.c
|
|
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00queue.c
|
|
@@ -200,15 +200,18 @@ static void rt2x00queue_create_tx_descriptor_seq(struct rt2x00_dev *rt2x00dev,
|
|
if (!rt2x00_has_cap_flag(rt2x00dev, REQUIRE_SW_SEQNO)) {
|
|
/*
|
|
* rt2800 has a H/W (or F/W) bug, device incorrectly increase
|
|
- * seqno on retransmited data (non-QOS) frames. To workaround
|
|
- * the problem let's generate seqno in software if QOS is
|
|
- * disabled.
|
|
+ * seqno on retransmitted data (non-QOS) and management frames.
|
|
+ * To workaround the problem let's generate seqno in software.
|
|
+ * Except for beacons which are transmitted periodically by H/W
|
|
+ * hence hardware has to assign seqno for them.
|
|
*/
|
|
- if (test_bit(CONFIG_QOS_DISABLED, &rt2x00dev->flags))
|
|
- __clear_bit(ENTRY_TXD_GENERATE_SEQ, &txdesc->flags);
|
|
- else
|
|
+ if (ieee80211_is_beacon(hdr->frame_control)) {
|
|
+ __set_bit(ENTRY_TXD_GENERATE_SEQ, &txdesc->flags);
|
|
/* H/W will generate sequence number */
|
|
return;
|
|
+ }
|
|
+
|
|
+ __clear_bit(ENTRY_TXD_GENERATE_SEQ, &txdesc->flags);
|
|
}
|
|
|
|
/*
|
|
diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c
|
|
index 89b1f1af2fd4..31d31aad3de1 100644
|
|
--- a/drivers/scsi/libfc/fc_rport.c
|
|
+++ b/drivers/scsi/libfc/fc_rport.c
|
|
@@ -2164,7 +2164,6 @@ static void fc_rport_recv_logo_req(struct fc_lport *lport, struct fc_frame *fp)
|
|
FC_RPORT_DBG(rdata, "Received LOGO request while in state %s\n",
|
|
fc_rport_state(rdata));
|
|
|
|
- rdata->flags &= ~FC_RP_STARTED;
|
|
fc_rport_enter_delete(rdata, RPORT_EV_STOP);
|
|
mutex_unlock(&rdata->rp_mutex);
|
|
kref_put(&rdata->kref, fc_rport_destroy);
|
|
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
|
|
index 37d366696d21..c89f0e129f58 100644
|
|
--- a/drivers/scsi/scsi_lib.c
|
|
+++ b/drivers/scsi/scsi_lib.c
|
|
@@ -2050,8 +2050,12 @@ out:
|
|
blk_mq_delay_run_hw_queue(hctx, SCSI_QUEUE_DELAY);
|
|
break;
|
|
default:
|
|
+ if (unlikely(!scsi_device_online(sdev)))
|
|
+ scsi_req(req)->result = DID_NO_CONNECT << 16;
|
|
+ else
|
|
+ scsi_req(req)->result = DID_ERROR << 16;
|
|
/*
|
|
- * Make sure to release all allocated ressources when
|
|
+ * Make sure to release all allocated resources when
|
|
* we hit an error, as we will never see this command
|
|
* again.
|
|
*/
|
|
diff --git a/drivers/staging/comedi/drivers/ni_usb6501.c b/drivers/staging/comedi/drivers/ni_usb6501.c
|
|
index 9a0a96329a55..009c5277387b 100644
|
|
--- a/drivers/staging/comedi/drivers/ni_usb6501.c
|
|
+++ b/drivers/staging/comedi/drivers/ni_usb6501.c
|
|
@@ -472,10 +472,8 @@ static int ni6501_alloc_usb_buffers(struct comedi_device *dev)
|
|
|
|
size = usb_endpoint_maxp(devpriv->ep_tx);
|
|
devpriv->usb_tx_buf = kzalloc(size, GFP_KERNEL);
|
|
- if (!devpriv->usb_tx_buf) {
|
|
- kfree(devpriv->usb_rx_buf);
|
|
+ if (!devpriv->usb_tx_buf)
|
|
return -ENOMEM;
|
|
- }
|
|
|
|
return 0;
|
|
}
|
|
@@ -527,6 +525,9 @@ static int ni6501_auto_attach(struct comedi_device *dev,
|
|
if (!devpriv)
|
|
return -ENOMEM;
|
|
|
|
+ mutex_init(&devpriv->mut);
|
|
+ usb_set_intfdata(intf, devpriv);
|
|
+
|
|
ret = ni6501_find_endpoints(dev);
|
|
if (ret)
|
|
return ret;
|
|
@@ -535,9 +536,6 @@ static int ni6501_auto_attach(struct comedi_device *dev,
|
|
if (ret)
|
|
return ret;
|
|
|
|
- mutex_init(&devpriv->mut);
|
|
- usb_set_intfdata(intf, devpriv);
|
|
-
|
|
ret = comedi_alloc_subdevices(dev, 2);
|
|
if (ret)
|
|
return ret;
|
|
diff --git a/drivers/staging/comedi/drivers/vmk80xx.c b/drivers/staging/comedi/drivers/vmk80xx.c
|
|
index a004aed0147a..1800eb3ae017 100644
|
|
--- a/drivers/staging/comedi/drivers/vmk80xx.c
|
|
+++ b/drivers/staging/comedi/drivers/vmk80xx.c
|
|
@@ -691,10 +691,8 @@ static int vmk80xx_alloc_usb_buffers(struct comedi_device *dev)
|
|
|
|
size = usb_endpoint_maxp(devpriv->ep_tx);
|
|
devpriv->usb_tx_buf = kzalloc(size, GFP_KERNEL);
|
|
- if (!devpriv->usb_tx_buf) {
|
|
- kfree(devpriv->usb_rx_buf);
|
|
+ if (!devpriv->usb_tx_buf)
|
|
return -ENOMEM;
|
|
- }
|
|
|
|
return 0;
|
|
}
|
|
@@ -809,6 +807,8 @@ static int vmk80xx_auto_attach(struct comedi_device *dev,
|
|
|
|
devpriv->model = board->model;
|
|
|
|
+ sema_init(&devpriv->limit_sem, 8);
|
|
+
|
|
ret = vmk80xx_find_usb_endpoints(dev);
|
|
if (ret)
|
|
return ret;
|
|
@@ -817,8 +817,6 @@ static int vmk80xx_auto_attach(struct comedi_device *dev,
|
|
if (ret)
|
|
return ret;
|
|
|
|
- sema_init(&devpriv->limit_sem, 8);
|
|
-
|
|
usb_set_intfdata(intf, devpriv);
|
|
|
|
if (devpriv->model == VMK8055_MODEL)
|
|
diff --git a/drivers/staging/iio/adc/ad7192.c b/drivers/staging/iio/adc/ad7192.c
|
|
index 31a195d1bf05..f58c80327ba5 100644
|
|
--- a/drivers/staging/iio/adc/ad7192.c
|
|
+++ b/drivers/staging/iio/adc/ad7192.c
|
|
@@ -109,10 +109,10 @@
|
|
#define AD7192_CH_AIN3 BIT(6) /* AIN3 - AINCOM */
|
|
#define AD7192_CH_AIN4 BIT(7) /* AIN4 - AINCOM */
|
|
|
|
-#define AD7193_CH_AIN1P_AIN2M 0x000 /* AIN1(+) - AIN2(-) */
|
|
-#define AD7193_CH_AIN3P_AIN4M 0x001 /* AIN3(+) - AIN4(-) */
|
|
-#define AD7193_CH_AIN5P_AIN6M 0x002 /* AIN5(+) - AIN6(-) */
|
|
-#define AD7193_CH_AIN7P_AIN8M 0x004 /* AIN7(+) - AIN8(-) */
|
|
+#define AD7193_CH_AIN1P_AIN2M 0x001 /* AIN1(+) - AIN2(-) */
|
|
+#define AD7193_CH_AIN3P_AIN4M 0x002 /* AIN3(+) - AIN4(-) */
|
|
+#define AD7193_CH_AIN5P_AIN6M 0x004 /* AIN5(+) - AIN6(-) */
|
|
+#define AD7193_CH_AIN7P_AIN8M 0x008 /* AIN7(+) - AIN8(-) */
|
|
#define AD7193_CH_TEMP 0x100 /* Temp senseor */
|
|
#define AD7193_CH_AIN2P_AIN2M 0x200 /* AIN2(+) - AIN2(-) */
|
|
#define AD7193_CH_AIN1 0x401 /* AIN1 - AINCOM */
|
|
diff --git a/drivers/staging/iio/meter/ade7854.c b/drivers/staging/iio/meter/ade7854.c
|
|
index 70612da64a8b..7ae774ef9da3 100644
|
|
--- a/drivers/staging/iio/meter/ade7854.c
|
|
+++ b/drivers/staging/iio/meter/ade7854.c
|
|
@@ -269,7 +269,7 @@ static IIO_DEV_ATTR_VPEAK(0644,
|
|
static IIO_DEV_ATTR_IPEAK(0644,
|
|
ade7854_read_32bit,
|
|
ade7854_write_32bit,
|
|
- ADE7854_VPEAK);
|
|
+ ADE7854_IPEAK);
|
|
static IIO_DEV_ATTR_APHCAL(0644,
|
|
ade7854_read_16bit,
|
|
ade7854_write_16bit,
|
|
diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c
|
|
index d7c22ae5c368..0e93ac888a5f 100644
|
|
--- a/drivers/vhost/vhost.c
|
|
+++ b/drivers/vhost/vhost.c
|
|
@@ -918,8 +918,12 @@ static int vhost_new_umem_range(struct vhost_umem *umem,
|
|
u64 start, u64 size, u64 end,
|
|
u64 userspace_addr, int perm)
|
|
{
|
|
- struct vhost_umem_node *tmp, *node = kmalloc(sizeof(*node), GFP_ATOMIC);
|
|
+ struct vhost_umem_node *tmp, *node;
|
|
|
|
+ if (!size)
|
|
+ return -EFAULT;
|
|
+
|
|
+ node = kmalloc(sizeof(*node), GFP_ATOMIC);
|
|
if (!node)
|
|
return -ENOMEM;
|
|
|
|
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
|
|
index f29cdb1cdeb7..7b7ab10a9db1 100644
|
|
--- a/fs/cifs/cifsglob.h
|
|
+++ b/fs/cifs/cifsglob.h
|
|
@@ -1189,6 +1189,7 @@ cifsFileInfo_get_locked(struct cifsFileInfo *cifs_file)
|
|
}
|
|
|
|
struct cifsFileInfo *cifsFileInfo_get(struct cifsFileInfo *cifs_file);
|
|
+void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_hdlr);
|
|
void cifsFileInfo_put(struct cifsFileInfo *cifs_file);
|
|
|
|
#define CIFS_CACHE_READ_FLG 1
|
|
@@ -1693,6 +1694,7 @@ GLOBAL_EXTERN spinlock_t gidsidlock;
|
|
#endif /* CONFIG_CIFS_ACL */
|
|
|
|
void cifs_oplock_break(struct work_struct *work);
|
|
+void cifs_queue_oplock_break(struct cifsFileInfo *cfile);
|
|
|
|
extern const struct slow_work_ops cifs_oplock_break_ops;
|
|
extern struct workqueue_struct *cifsiod_wq;
|
|
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
|
|
index cd69c1e9750f..48ea9dfd5f02 100644
|
|
--- a/fs/cifs/file.c
|
|
+++ b/fs/cifs/file.c
|
|
@@ -358,12 +358,30 @@ cifsFileInfo_get(struct cifsFileInfo *cifs_file)
|
|
return cifs_file;
|
|
}
|
|
|
|
-/*
|
|
- * Release a reference on the file private data. This may involve closing
|
|
- * the filehandle out on the server. Must be called without holding
|
|
- * tcon->open_file_lock and cifs_file->file_info_lock.
|
|
+/**
|
|
+ * cifsFileInfo_put - release a reference of file priv data
|
|
+ *
|
|
+ * Always potentially wait for oplock handler. See _cifsFileInfo_put().
|
|
*/
|
|
void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
|
|
+{
|
|
+ _cifsFileInfo_put(cifs_file, true);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * _cifsFileInfo_put - release a reference of file priv data
|
|
+ *
|
|
+ * This may involve closing the filehandle @cifs_file out on the
|
|
+ * server. Must be called without holding tcon->open_file_lock and
|
|
+ * cifs_file->file_info_lock.
|
|
+ *
|
|
+ * If @wait_for_oplock_handler is true and we are releasing the last
|
|
+ * reference, wait for any running oplock break handler of the file
|
|
+ * and cancel any pending one. If calling this function from the
|
|
+ * oplock break handler, you need to pass false.
|
|
+ *
|
|
+ */
|
|
+void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_handler)
|
|
{
|
|
struct inode *inode = d_inode(cifs_file->dentry);
|
|
struct cifs_tcon *tcon = tlink_tcon(cifs_file->tlink);
|
|
@@ -411,7 +429,8 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
|
|
|
|
spin_unlock(&tcon->open_file_lock);
|
|
|
|
- oplock_break_cancelled = cancel_work_sync(&cifs_file->oplock_break);
|
|
+ oplock_break_cancelled = wait_oplock_handler ?
|
|
+ cancel_work_sync(&cifs_file->oplock_break) : false;
|
|
|
|
if (!tcon->need_reconnect && !cifs_file->invalidHandle) {
|
|
struct TCP_Server_Info *server = tcon->ses->server;
|
|
@@ -4136,6 +4155,7 @@ void cifs_oplock_break(struct work_struct *work)
|
|
cinode);
|
|
cifs_dbg(FYI, "Oplock release rc = %d\n", rc);
|
|
}
|
|
+ _cifsFileInfo_put(cfile, false /* do not wait for ourself */);
|
|
cifs_done_oplock_break(cinode);
|
|
}
|
|
|
|
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
|
|
index bcab30d4a6c7..76f1649ab444 100644
|
|
--- a/fs/cifs/misc.c
|
|
+++ b/fs/cifs/misc.c
|
|
@@ -486,8 +486,7 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)
|
|
CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2,
|
|
&pCifsInode->flags);
|
|
|
|
- queue_work(cifsoplockd_wq,
|
|
- &netfile->oplock_break);
|
|
+ cifs_queue_oplock_break(netfile);
|
|
netfile->oplock_break_cancelled = false;
|
|
|
|
spin_unlock(&tcon->open_file_lock);
|
|
@@ -584,6 +583,28 @@ void cifs_put_writer(struct cifsInodeInfo *cinode)
|
|
spin_unlock(&cinode->writers_lock);
|
|
}
|
|
|
|
+/**
|
|
+ * cifs_queue_oplock_break - queue the oplock break handler for cfile
|
|
+ *
|
|
+ * This function is called from the demultiplex thread when it
|
|
+ * receives an oplock break for @cfile.
|
|
+ *
|
|
+ * Assumes the tcon->open_file_lock is held.
|
|
+ * Assumes cfile->file_info_lock is NOT held.
|
|
+ */
|
|
+void cifs_queue_oplock_break(struct cifsFileInfo *cfile)
|
|
+{
|
|
+ /*
|
|
+ * Bump the handle refcount now while we hold the
|
|
+ * open_file_lock to enforce the validity of it for the oplock
|
|
+ * break handler. The matching put is done at the end of the
|
|
+ * handler.
|
|
+ */
|
|
+ cifsFileInfo_get(cfile);
|
|
+
|
|
+ queue_work(cifsoplockd_wq, &cfile->oplock_break);
|
|
+}
|
|
+
|
|
void cifs_done_oplock_break(struct cifsInodeInfo *cinode)
|
|
{
|
|
clear_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, &cinode->flags);
|
|
diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c
|
|
index a97a0e0b1a74..31f01f09d25a 100644
|
|
--- a/fs/cifs/smb2misc.c
|
|
+++ b/fs/cifs/smb2misc.c
|
|
@@ -517,7 +517,7 @@ smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp,
|
|
clear_bit(CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2,
|
|
&cinode->flags);
|
|
|
|
- queue_work(cifsoplockd_wq, &cfile->oplock_break);
|
|
+ cifs_queue_oplock_break(cfile);
|
|
kfree(lw);
|
|
return true;
|
|
}
|
|
@@ -661,8 +661,8 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
|
|
CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2,
|
|
&cinode->flags);
|
|
spin_unlock(&cfile->file_info_lock);
|
|
- queue_work(cifsoplockd_wq,
|
|
- &cfile->oplock_break);
|
|
+
|
|
+ cifs_queue_oplock_break(cfile);
|
|
|
|
spin_unlock(&tcon->open_file_lock);
|
|
spin_unlock(&cifs_tcp_ses_lock);
|
|
diff --git a/fs/direct-io.c b/fs/direct-io.c
|
|
index 2c90d541f527..30bf22c989de 100644
|
|
--- a/fs/direct-io.c
|
|
+++ b/fs/direct-io.c
|
|
@@ -219,6 +219,27 @@ static inline struct page *dio_get_page(struct dio *dio,
|
|
return dio->pages[sdio->head];
|
|
}
|
|
|
|
+/*
|
|
+ * Warn about a page cache invalidation failure during a direct io write.
|
|
+ */
|
|
+void dio_warn_stale_pagecache(struct file *filp)
|
|
+{
|
|
+ static DEFINE_RATELIMIT_STATE(_rs, 86400 * HZ, DEFAULT_RATELIMIT_BURST);
|
|
+ char pathname[128];
|
|
+ struct inode *inode = file_inode(filp);
|
|
+ char *path;
|
|
+
|
|
+ errseq_set(&inode->i_mapping->wb_err, -EIO);
|
|
+ if (__ratelimit(&_rs)) {
|
|
+ path = file_path(filp, pathname, sizeof(pathname));
|
|
+ if (IS_ERR(path))
|
|
+ path = "(unknown)";
|
|
+ pr_crit("Page cache invalidation failure on direct I/O. Possible data corruption due to collision with buffered I/O!\n");
|
|
+ pr_crit("File: %s PID: %d Comm: %.20s\n", path, current->pid,
|
|
+ current->comm);
|
|
+ }
|
|
+}
|
|
+
|
|
/**
|
|
* dio_complete() - called when all DIO BIO I/O has been completed
|
|
* @offset: the byte offset in the file of the completed operation
|
|
@@ -290,7 +311,8 @@ static ssize_t dio_complete(struct dio *dio, ssize_t ret, unsigned int flags)
|
|
err = invalidate_inode_pages2_range(dio->inode->i_mapping,
|
|
offset >> PAGE_SHIFT,
|
|
(offset + ret - 1) >> PAGE_SHIFT);
|
|
- WARN_ON_ONCE(err);
|
|
+ if (err)
|
|
+ dio_warn_stale_pagecache(dio->iocb->ki_filp);
|
|
}
|
|
|
|
if (!(dio->flags & DIO_SKIP_DIO_COUNT))
|
|
diff --git a/fs/iomap.c b/fs/iomap.c
|
|
index 8f7673a69273..467d98bf7054 100644
|
|
--- a/fs/iomap.c
|
|
+++ b/fs/iomap.c
|
|
@@ -753,7 +753,8 @@ static ssize_t iomap_dio_complete(struct iomap_dio *dio)
|
|
err = invalidate_inode_pages2_range(inode->i_mapping,
|
|
offset >> PAGE_SHIFT,
|
|
(offset + dio->size - 1) >> PAGE_SHIFT);
|
|
- WARN_ON_ONCE(err);
|
|
+ if (err)
|
|
+ dio_warn_stale_pagecache(iocb->ki_filp);
|
|
}
|
|
|
|
inode_dio_end(file_inode(iocb->ki_filp));
|
|
@@ -1010,9 +1011,16 @@ iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter,
|
|
if (ret)
|
|
goto out_free_dio;
|
|
|
|
+ /*
|
|
+ * Try to invalidate cache pages for the range we're direct
|
|
+ * writing. If this invalidation fails, tough, the write will
|
|
+ * still work, but racing two incompatible write paths is a
|
|
+ * pretty crazy thing to do, so we don't support it 100%.
|
|
+ */
|
|
ret = invalidate_inode_pages2_range(mapping,
|
|
start >> PAGE_SHIFT, end >> PAGE_SHIFT);
|
|
- WARN_ON_ONCE(ret);
|
|
+ if (ret)
|
|
+ dio_warn_stale_pagecache(iocb->ki_filp);
|
|
ret = 0;
|
|
|
|
if (iov_iter_rw(iter) == WRITE && !dio->wait_for_completion &&
|
|
diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
|
|
index 5e63c459dc61..309d24118f9a 100644
|
|
--- a/fs/proc/task_mmu.c
|
|
+++ b/fs/proc/task_mmu.c
|
|
@@ -1160,6 +1160,24 @@ static ssize_t clear_refs_write(struct file *file, const char __user *buf,
|
|
count = -EINTR;
|
|
goto out_mm;
|
|
}
|
|
+ /*
|
|
+ * Avoid to modify vma->vm_flags
|
|
+ * without locked ops while the
|
|
+ * coredump reads the vm_flags.
|
|
+ */
|
|
+ if (!mmget_still_valid(mm)) {
|
|
+ /*
|
|
+ * Silently return "count"
|
|
+ * like if get_task_mm()
|
|
+ * failed. FIXME: should this
|
|
+ * function have returned
|
|
+ * -ESRCH if get_task_mm()
|
|
+ * failed like if
|
|
+ * get_proc_task() fails?
|
|
+ */
|
|
+ up_write(&mm->mmap_sem);
|
|
+ goto out_mm;
|
|
+ }
|
|
for (vma = mm->mmap; vma; vma = vma->vm_next) {
|
|
vma->vm_flags &= ~VM_SOFTDIRTY;
|
|
vma_set_page_prot(vma);
|
|
diff --git a/fs/userfaultfd.c b/fs/userfaultfd.c
|
|
index 5f10052d2671..7a908d683258 100644
|
|
--- a/fs/userfaultfd.c
|
|
+++ b/fs/userfaultfd.c
|
|
@@ -627,6 +627,8 @@ static void userfaultfd_event_wait_completion(struct userfaultfd_ctx *ctx,
|
|
|
|
/* the various vma->vm_userfaultfd_ctx still points to it */
|
|
down_write(&mm->mmap_sem);
|
|
+ /* no task can run (and in turn coredump) yet */
|
|
+ VM_WARN_ON(!mmget_still_valid(mm));
|
|
for (vma = mm->mmap; vma; vma = vma->vm_next)
|
|
if (vma->vm_userfaultfd_ctx.ctx == release_new_ctx) {
|
|
vma->vm_userfaultfd_ctx = NULL_VM_UFFD_CTX;
|
|
@@ -867,6 +869,8 @@ static int userfaultfd_release(struct inode *inode, struct file *file)
|
|
* taking the mmap_sem for writing.
|
|
*/
|
|
down_write(&mm->mmap_sem);
|
|
+ if (!mmget_still_valid(mm))
|
|
+ goto skip_mm;
|
|
prev = NULL;
|
|
for (vma = mm->mmap; vma; vma = vma->vm_next) {
|
|
cond_resched();
|
|
@@ -889,6 +893,7 @@ static int userfaultfd_release(struct inode *inode, struct file *file)
|
|
vma->vm_flags = new_flags;
|
|
vma->vm_userfaultfd_ctx = NULL_VM_UFFD_CTX;
|
|
}
|
|
+skip_mm:
|
|
up_write(&mm->mmap_sem);
|
|
mmput(mm);
|
|
wakeup:
|
|
@@ -1327,6 +1332,8 @@ static int userfaultfd_register(struct userfaultfd_ctx *ctx,
|
|
goto out;
|
|
|
|
down_write(&mm->mmap_sem);
|
|
+ if (!mmget_still_valid(mm))
|
|
+ goto out_unlock;
|
|
vma = find_vma_prev(mm, start, &prev);
|
|
if (!vma)
|
|
goto out_unlock;
|
|
@@ -1514,6 +1521,8 @@ static int userfaultfd_unregister(struct userfaultfd_ctx *ctx,
|
|
goto out;
|
|
|
|
down_write(&mm->mmap_sem);
|
|
+ if (!mmget_still_valid(mm))
|
|
+ goto out_unlock;
|
|
vma = find_vma_prev(mm, start, &prev);
|
|
if (!vma)
|
|
goto out_unlock;
|
|
diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
|
|
index ea66f04f46f7..e4265db08e4b 100644
|
|
--- a/fs/xfs/libxfs/xfs_attr.c
|
|
+++ b/fs/xfs/libxfs/xfs_attr.c
|
|
@@ -212,6 +212,7 @@ xfs_attr_set(
|
|
int flags)
|
|
{
|
|
struct xfs_mount *mp = dp->i_mount;
|
|
+ struct xfs_buf *leaf_bp = NULL;
|
|
struct xfs_da_args args;
|
|
struct xfs_defer_ops dfops;
|
|
struct xfs_trans_res tres;
|
|
@@ -327,9 +328,16 @@ xfs_attr_set(
|
|
* GROT: another possible req'mt for a double-split btree op.
|
|
*/
|
|
xfs_defer_init(args.dfops, args.firstblock);
|
|
- error = xfs_attr_shortform_to_leaf(&args);
|
|
+ error = xfs_attr_shortform_to_leaf(&args, &leaf_bp);
|
|
if (error)
|
|
goto out_defer_cancel;
|
|
+ /*
|
|
+ * Prevent the leaf buffer from being unlocked so that a
|
|
+ * concurrent AIL push cannot grab the half-baked leaf
|
|
+ * buffer and run into problems with the write verifier.
|
|
+ */
|
|
+ xfs_trans_bhold(args.trans, leaf_bp);
|
|
+ xfs_defer_bjoin(args.dfops, leaf_bp);
|
|
xfs_defer_ijoin(args.dfops, dp);
|
|
error = xfs_defer_finish(&args.trans, args.dfops);
|
|
if (error)
|
|
@@ -337,13 +345,14 @@ xfs_attr_set(
|
|
|
|
/*
|
|
* Commit the leaf transformation. We'll need another (linked)
|
|
- * transaction to add the new attribute to the leaf.
|
|
+ * transaction to add the new attribute to the leaf, which
|
|
+ * means that we have to hold & join the leaf buffer here too.
|
|
*/
|
|
-
|
|
error = xfs_trans_roll_inode(&args.trans, dp);
|
|
if (error)
|
|
goto out;
|
|
-
|
|
+ xfs_trans_bjoin(args.trans, leaf_bp);
|
|
+ leaf_bp = NULL;
|
|
}
|
|
|
|
if (xfs_bmap_one_block(dp, XFS_ATTR_FORK))
|
|
@@ -374,8 +383,9 @@ xfs_attr_set(
|
|
|
|
out_defer_cancel:
|
|
xfs_defer_cancel(&dfops);
|
|
- args.trans = NULL;
|
|
out:
|
|
+ if (leaf_bp)
|
|
+ xfs_trans_brelse(args.trans, leaf_bp);
|
|
if (args.trans)
|
|
xfs_trans_cancel(args.trans);
|
|
xfs_iunlock(dp, XFS_ILOCK_EXCL);
|
|
diff --git a/fs/xfs/libxfs/xfs_attr_leaf.c b/fs/xfs/libxfs/xfs_attr_leaf.c
|
|
index 40e53a4fc0a6..73a541755d5b 100644
|
|
--- a/fs/xfs/libxfs/xfs_attr_leaf.c
|
|
+++ b/fs/xfs/libxfs/xfs_attr_leaf.c
|
|
@@ -739,10 +739,13 @@ xfs_attr_shortform_getvalue(xfs_da_args_t *args)
|
|
}
|
|
|
|
/*
|
|
- * Convert from using the shortform to the leaf.
|
|
+ * Convert from using the shortform to the leaf. On success, return the
|
|
+ * buffer so that we can keep it locked until we're totally done with it.
|
|
*/
|
|
int
|
|
-xfs_attr_shortform_to_leaf(xfs_da_args_t *args)
|
|
+xfs_attr_shortform_to_leaf(
|
|
+ struct xfs_da_args *args,
|
|
+ struct xfs_buf **leaf_bp)
|
|
{
|
|
xfs_inode_t *dp;
|
|
xfs_attr_shortform_t *sf;
|
|
@@ -821,7 +824,7 @@ xfs_attr_shortform_to_leaf(xfs_da_args_t *args)
|
|
sfe = XFS_ATTR_SF_NEXTENTRY(sfe);
|
|
}
|
|
error = 0;
|
|
-
|
|
+ *leaf_bp = bp;
|
|
out:
|
|
kmem_free(tmpbuffer);
|
|
return error;
|
|
diff --git a/fs/xfs/libxfs/xfs_attr_leaf.h b/fs/xfs/libxfs/xfs_attr_leaf.h
|
|
index f7dda0c237b0..894124efb421 100644
|
|
--- a/fs/xfs/libxfs/xfs_attr_leaf.h
|
|
+++ b/fs/xfs/libxfs/xfs_attr_leaf.h
|
|
@@ -48,7 +48,8 @@ void xfs_attr_shortform_create(struct xfs_da_args *args);
|
|
void xfs_attr_shortform_add(struct xfs_da_args *args, int forkoff);
|
|
int xfs_attr_shortform_lookup(struct xfs_da_args *args);
|
|
int xfs_attr_shortform_getvalue(struct xfs_da_args *args);
|
|
-int xfs_attr_shortform_to_leaf(struct xfs_da_args *args);
|
|
+int xfs_attr_shortform_to_leaf(struct xfs_da_args *args,
|
|
+ struct xfs_buf **leaf_bp);
|
|
int xfs_attr_shortform_remove(struct xfs_da_args *args);
|
|
int xfs_attr_shortform_allfit(struct xfs_buf *bp, struct xfs_inode *dp);
|
|
int xfs_attr_shortform_bytesfit(struct xfs_inode *dp, int bytes);
|
|
diff --git a/fs/xfs/libxfs/xfs_defer.c b/fs/xfs/libxfs/xfs_defer.c
|
|
index 072ebfe1d6ae..087fea02c389 100644
|
|
--- a/fs/xfs/libxfs/xfs_defer.c
|
|
+++ b/fs/xfs/libxfs/xfs_defer.c
|
|
@@ -249,6 +249,10 @@ xfs_defer_trans_roll(
|
|
for (i = 0; i < XFS_DEFER_OPS_NR_INODES && dop->dop_inodes[i]; i++)
|
|
xfs_trans_log_inode(*tp, dop->dop_inodes[i], XFS_ILOG_CORE);
|
|
|
|
+ /* Hold the (previously bjoin'd) buffer locked across the roll. */
|
|
+ for (i = 0; i < XFS_DEFER_OPS_NR_BUFS && dop->dop_bufs[i]; i++)
|
|
+ xfs_trans_dirty_buf(*tp, dop->dop_bufs[i]);
|
|
+
|
|
trace_xfs_defer_trans_roll((*tp)->t_mountp, dop);
|
|
|
|
/* Roll the transaction. */
|
|
@@ -264,6 +268,12 @@ xfs_defer_trans_roll(
|
|
for (i = 0; i < XFS_DEFER_OPS_NR_INODES && dop->dop_inodes[i]; i++)
|
|
xfs_trans_ijoin(*tp, dop->dop_inodes[i], 0);
|
|
|
|
+ /* Rejoin the buffers and dirty them so the log moves forward. */
|
|
+ for (i = 0; i < XFS_DEFER_OPS_NR_BUFS && dop->dop_bufs[i]; i++) {
|
|
+ xfs_trans_bjoin(*tp, dop->dop_bufs[i]);
|
|
+ xfs_trans_bhold(*tp, dop->dop_bufs[i]);
|
|
+ }
|
|
+
|
|
return error;
|
|
}
|
|
|
|
@@ -295,6 +305,31 @@ xfs_defer_ijoin(
|
|
}
|
|
}
|
|
|
|
+ ASSERT(0);
|
|
+ return -EFSCORRUPTED;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Add this buffer to the deferred op. Each joined buffer is relogged
|
|
+ * each time we roll the transaction.
|
|
+ */
|
|
+int
|
|
+xfs_defer_bjoin(
|
|
+ struct xfs_defer_ops *dop,
|
|
+ struct xfs_buf *bp)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < XFS_DEFER_OPS_NR_BUFS; i++) {
|
|
+ if (dop->dop_bufs[i] == bp)
|
|
+ return 0;
|
|
+ else if (dop->dop_bufs[i] == NULL) {
|
|
+ dop->dop_bufs[i] = bp;
|
|
+ return 0;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ ASSERT(0);
|
|
return -EFSCORRUPTED;
|
|
}
|
|
|
|
@@ -493,9 +528,7 @@ xfs_defer_init(
|
|
struct xfs_defer_ops *dop,
|
|
xfs_fsblock_t *fbp)
|
|
{
|
|
- dop->dop_committed = false;
|
|
- dop->dop_low = false;
|
|
- memset(&dop->dop_inodes, 0, sizeof(dop->dop_inodes));
|
|
+ memset(dop, 0, sizeof(struct xfs_defer_ops));
|
|
*fbp = NULLFSBLOCK;
|
|
INIT_LIST_HEAD(&dop->dop_intake);
|
|
INIT_LIST_HEAD(&dop->dop_pending);
|
|
diff --git a/fs/xfs/libxfs/xfs_defer.h b/fs/xfs/libxfs/xfs_defer.h
|
|
index d4f046dd44bd..045beacdd37d 100644
|
|
--- a/fs/xfs/libxfs/xfs_defer.h
|
|
+++ b/fs/xfs/libxfs/xfs_defer.h
|
|
@@ -59,6 +59,7 @@ enum xfs_defer_ops_type {
|
|
};
|
|
|
|
#define XFS_DEFER_OPS_NR_INODES 2 /* join up to two inodes */
|
|
+#define XFS_DEFER_OPS_NR_BUFS 2 /* join up to two buffers */
|
|
|
|
struct xfs_defer_ops {
|
|
bool dop_committed; /* did any trans commit? */
|
|
@@ -66,8 +67,9 @@ struct xfs_defer_ops {
|
|
struct list_head dop_intake; /* unlogged pending work */
|
|
struct list_head dop_pending; /* logged pending work */
|
|
|
|
- /* relog these inodes with each roll */
|
|
+ /* relog these with each roll */
|
|
struct xfs_inode *dop_inodes[XFS_DEFER_OPS_NR_INODES];
|
|
+ struct xfs_buf *dop_bufs[XFS_DEFER_OPS_NR_BUFS];
|
|
};
|
|
|
|
void xfs_defer_add(struct xfs_defer_ops *dop, enum xfs_defer_ops_type type,
|
|
@@ -77,6 +79,7 @@ void xfs_defer_cancel(struct xfs_defer_ops *dop);
|
|
void xfs_defer_init(struct xfs_defer_ops *dop, xfs_fsblock_t *fbp);
|
|
bool xfs_defer_has_unfinished_work(struct xfs_defer_ops *dop);
|
|
int xfs_defer_ijoin(struct xfs_defer_ops *dop, struct xfs_inode *ip);
|
|
+int xfs_defer_bjoin(struct xfs_defer_ops *dop, struct xfs_buf *bp);
|
|
|
|
/* Description of a deferred type. */
|
|
struct xfs_defer_op_type {
|
|
diff --git a/include/linux/fs.h b/include/linux/fs.h
|
|
index f6a577edec67..dafac283b0ff 100644
|
|
--- a/include/linux/fs.h
|
|
+++ b/include/linux/fs.h
|
|
@@ -2965,6 +2965,7 @@ enum {
|
|
};
|
|
|
|
void dio_end_io(struct bio *bio);
|
|
+void dio_warn_stale_pagecache(struct file *filp);
|
|
|
|
ssize_t __blockdev_direct_IO(struct kiocb *iocb, struct inode *inode,
|
|
struct block_device *bdev, struct iov_iter *iter,
|
|
diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h
|
|
index bd2684700b74..520702b82134 100644
|
|
--- a/include/linux/kprobes.h
|
|
+++ b/include/linux/kprobes.h
|
|
@@ -198,6 +198,7 @@ struct kretprobe_instance {
|
|
struct kretprobe *rp;
|
|
kprobe_opcode_t *ret_addr;
|
|
struct task_struct *task;
|
|
+ void *fp;
|
|
char data[0];
|
|
};
|
|
|
|
diff --git a/include/linux/sched/mm.h b/include/linux/sched/mm.h
|
|
index 3d49b91b674d..ef4ae0a545fe 100644
|
|
--- a/include/linux/sched/mm.h
|
|
+++ b/include/linux/sched/mm.h
|
|
@@ -57,6 +57,27 @@ static inline void mmdrop_async(struct mm_struct *mm)
|
|
}
|
|
}
|
|
|
|
+/*
|
|
+ * This has to be called after a get_task_mm()/mmget_not_zero()
|
|
+ * followed by taking the mmap_sem for writing before modifying the
|
|
+ * vmas or anything the coredump pretends not to change from under it.
|
|
+ *
|
|
+ * NOTE: find_extend_vma() called from GUP context is the only place
|
|
+ * that can modify the "mm" (notably the vm_start/end) under mmap_sem
|
|
+ * for reading and outside the context of the process, so it is also
|
|
+ * the only case that holds the mmap_sem for reading that must call
|
|
+ * this function. Generally if the mmap_sem is hold for reading
|
|
+ * there's no need of this check after get_task_mm()/mmget_not_zero().
|
|
+ *
|
|
+ * This function can be obsoleted and the check can be removed, after
|
|
+ * the coredump code will hold the mmap_sem for writing before
|
|
+ * invoking the ->core_dump methods.
|
|
+ */
|
|
+static inline bool mmget_still_valid(struct mm_struct *mm)
|
|
+{
|
|
+ return likely(!mm->core_state);
|
|
+}
|
|
+
|
|
/**
|
|
* mmget() - Pin the address space associated with a &struct mm_struct.
|
|
* @mm: The address space to pin.
|
|
diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h
|
|
index 335cf7851f12..008f64823c41 100644
|
|
--- a/include/net/inet_frag.h
|
|
+++ b/include/net/inet_frag.h
|
|
@@ -77,8 +77,8 @@ struct inet_frag_queue {
|
|
struct timer_list timer;
|
|
spinlock_t lock;
|
|
refcount_t refcnt;
|
|
- struct sk_buff *fragments; /* Used in IPv6. */
|
|
- struct rb_root rb_fragments; /* Used in IPv4. */
|
|
+ struct sk_buff *fragments; /* used in 6lopwpan IPv6. */
|
|
+ struct rb_root rb_fragments; /* Used in IPv4/IPv6. */
|
|
struct sk_buff *fragments_tail;
|
|
struct sk_buff *last_run_head;
|
|
ktime_t stamp;
|
|
@@ -153,4 +153,16 @@ static inline void add_frag_mem_limit(struct netns_frags *nf, long val)
|
|
|
|
extern const u8 ip_frag_ecn_table[16];
|
|
|
|
+/* Return values of inet_frag_queue_insert() */
|
|
+#define IPFRAG_OK 0
|
|
+#define IPFRAG_DUP 1
|
|
+#define IPFRAG_OVERLAP 2
|
|
+int inet_frag_queue_insert(struct inet_frag_queue *q, struct sk_buff *skb,
|
|
+ int offset, int end);
|
|
+void *inet_frag_reasm_prepare(struct inet_frag_queue *q, struct sk_buff *skb,
|
|
+ struct sk_buff *parent);
|
|
+void inet_frag_reasm_finish(struct inet_frag_queue *q, struct sk_buff *head,
|
|
+ void *reasm_data);
|
|
+struct sk_buff *inet_frag_pull_head(struct inet_frag_queue *q);
|
|
+
|
|
#endif
|
|
diff --git a/include/net/ipv6.h b/include/net/ipv6.h
|
|
index fa87a62e9bd3..6294d20a5f0e 100644
|
|
--- a/include/net/ipv6.h
|
|
+++ b/include/net/ipv6.h
|
|
@@ -512,35 +512,6 @@ static inline bool ipv6_prefix_equal(const struct in6_addr *addr1,
|
|
}
|
|
#endif
|
|
|
|
-struct inet_frag_queue;
|
|
-
|
|
-enum ip6_defrag_users {
|
|
- IP6_DEFRAG_LOCAL_DELIVER,
|
|
- IP6_DEFRAG_CONNTRACK_IN,
|
|
- __IP6_DEFRAG_CONNTRACK_IN = IP6_DEFRAG_CONNTRACK_IN + USHRT_MAX,
|
|
- IP6_DEFRAG_CONNTRACK_OUT,
|
|
- __IP6_DEFRAG_CONNTRACK_OUT = IP6_DEFRAG_CONNTRACK_OUT + USHRT_MAX,
|
|
- IP6_DEFRAG_CONNTRACK_BRIDGE_IN,
|
|
- __IP6_DEFRAG_CONNTRACK_BRIDGE_IN = IP6_DEFRAG_CONNTRACK_BRIDGE_IN + USHRT_MAX,
|
|
-};
|
|
-
|
|
-void ip6_frag_init(struct inet_frag_queue *q, const void *a);
|
|
-extern const struct rhashtable_params ip6_rhash_params;
|
|
-
|
|
-/*
|
|
- * Equivalent of ipv4 struct ip
|
|
- */
|
|
-struct frag_queue {
|
|
- struct inet_frag_queue q;
|
|
-
|
|
- int iif;
|
|
- unsigned int csum;
|
|
- __u16 nhoffset;
|
|
- u8 ecn;
|
|
-};
|
|
-
|
|
-void ip6_expire_frag_queue(struct net *net, struct frag_queue *fq);
|
|
-
|
|
static inline bool ipv6_addr_any(const struct in6_addr *a)
|
|
{
|
|
#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && BITS_PER_LONG == 64
|
|
diff --git a/include/net/ipv6_frag.h b/include/net/ipv6_frag.h
|
|
new file mode 100644
|
|
index 000000000000..28aa9b30aece
|
|
--- /dev/null
|
|
+++ b/include/net/ipv6_frag.h
|
|
@@ -0,0 +1,111 @@
|
|
+/* SPDX-License-Identifier: GPL-2.0 */
|
|
+#ifndef _IPV6_FRAG_H
|
|
+#define _IPV6_FRAG_H
|
|
+#include <linux/kernel.h>
|
|
+#include <net/addrconf.h>
|
|
+#include <net/ipv6.h>
|
|
+#include <net/inet_frag.h>
|
|
+
|
|
+enum ip6_defrag_users {
|
|
+ IP6_DEFRAG_LOCAL_DELIVER,
|
|
+ IP6_DEFRAG_CONNTRACK_IN,
|
|
+ __IP6_DEFRAG_CONNTRACK_IN = IP6_DEFRAG_CONNTRACK_IN + USHRT_MAX,
|
|
+ IP6_DEFRAG_CONNTRACK_OUT,
|
|
+ __IP6_DEFRAG_CONNTRACK_OUT = IP6_DEFRAG_CONNTRACK_OUT + USHRT_MAX,
|
|
+ IP6_DEFRAG_CONNTRACK_BRIDGE_IN,
|
|
+ __IP6_DEFRAG_CONNTRACK_BRIDGE_IN = IP6_DEFRAG_CONNTRACK_BRIDGE_IN + USHRT_MAX,
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Equivalent of ipv4 struct ip
|
|
+ */
|
|
+struct frag_queue {
|
|
+ struct inet_frag_queue q;
|
|
+
|
|
+ int iif;
|
|
+ __u16 nhoffset;
|
|
+ u8 ecn;
|
|
+};
|
|
+
|
|
+#if IS_ENABLED(CONFIG_IPV6)
|
|
+static inline void ip6frag_init(struct inet_frag_queue *q, const void *a)
|
|
+{
|
|
+ struct frag_queue *fq = container_of(q, struct frag_queue, q);
|
|
+ const struct frag_v6_compare_key *key = a;
|
|
+
|
|
+ q->key.v6 = *key;
|
|
+ fq->ecn = 0;
|
|
+}
|
|
+
|
|
+static inline u32 ip6frag_key_hashfn(const void *data, u32 len, u32 seed)
|
|
+{
|
|
+ return jhash2(data,
|
|
+ sizeof(struct frag_v6_compare_key) / sizeof(u32), seed);
|
|
+}
|
|
+
|
|
+static inline u32 ip6frag_obj_hashfn(const void *data, u32 len, u32 seed)
|
|
+{
|
|
+ const struct inet_frag_queue *fq = data;
|
|
+
|
|
+ return jhash2((const u32 *)&fq->key.v6,
|
|
+ sizeof(struct frag_v6_compare_key) / sizeof(u32), seed);
|
|
+}
|
|
+
|
|
+static inline int
|
|
+ip6frag_obj_cmpfn(struct rhashtable_compare_arg *arg, const void *ptr)
|
|
+{
|
|
+ const struct frag_v6_compare_key *key = arg->key;
|
|
+ const struct inet_frag_queue *fq = ptr;
|
|
+
|
|
+ return !!memcmp(&fq->key, key, sizeof(*key));
|
|
+}
|
|
+
|
|
+static inline void
|
|
+ip6frag_expire_frag_queue(struct net *net, struct frag_queue *fq)
|
|
+{
|
|
+ struct net_device *dev = NULL;
|
|
+ struct sk_buff *head;
|
|
+
|
|
+ rcu_read_lock();
|
|
+ spin_lock(&fq->q.lock);
|
|
+
|
|
+ if (fq->q.flags & INET_FRAG_COMPLETE)
|
|
+ goto out;
|
|
+
|
|
+ inet_frag_kill(&fq->q);
|
|
+
|
|
+ dev = dev_get_by_index_rcu(net, fq->iif);
|
|
+ if (!dev)
|
|
+ goto out;
|
|
+
|
|
+ __IP6_INC_STATS(net, __in6_dev_get(dev), IPSTATS_MIB_REASMFAILS);
|
|
+ __IP6_INC_STATS(net, __in6_dev_get(dev), IPSTATS_MIB_REASMTIMEOUT);
|
|
+
|
|
+ /* Don't send error if the first segment did not arrive. */
|
|
+ if (!(fq->q.flags & INET_FRAG_FIRST_IN))
|
|
+ goto out;
|
|
+
|
|
+ /* sk_buff::dev and sk_buff::rbnode are unionized. So we
|
|
+ * pull the head out of the tree in order to be able to
|
|
+ * deal with head->dev.
|
|
+ */
|
|
+ head = inet_frag_pull_head(&fq->q);
|
|
+ if (!head)
|
|
+ goto out;
|
|
+
|
|
+ head->dev = dev;
|
|
+ skb_get(head);
|
|
+ spin_unlock(&fq->q.lock);
|
|
+
|
|
+ icmpv6_send(head, ICMPV6_TIME_EXCEED, ICMPV6_EXC_FRAGTIME, 0);
|
|
+ kfree_skb(head);
|
|
+ goto out_rcu_unlock;
|
|
+
|
|
+out:
|
|
+ spin_unlock(&fq->q.lock);
|
|
+out_rcu_unlock:
|
|
+ rcu_read_unlock();
|
|
+ inet_frag_put(&fq->q);
|
|
+}
|
|
+#endif
|
|
+#endif
|
|
diff --git a/kernel/kprobes.c b/kernel/kprobes.c
|
|
index 5cbad4fb9107..ec11bb986a8b 100644
|
|
--- a/kernel/kprobes.c
|
|
+++ b/kernel/kprobes.c
|
|
@@ -703,7 +703,6 @@ static void unoptimize_kprobe(struct kprobe *p, bool force)
|
|
static int reuse_unused_kprobe(struct kprobe *ap)
|
|
{
|
|
struct optimized_kprobe *op;
|
|
- int ret;
|
|
|
|
BUG_ON(!kprobe_unused(ap));
|
|
/*
|
|
@@ -717,9 +716,8 @@ static int reuse_unused_kprobe(struct kprobe *ap)
|
|
/* Enable the probe again */
|
|
ap->flags &= ~KPROBE_FLAG_DISABLED;
|
|
/* Optimize it again (remove from op->list) */
|
|
- ret = kprobe_optready(ap);
|
|
- if (ret)
|
|
- return ret;
|
|
+ if (!kprobe_optready(ap))
|
|
+ return -EINVAL;
|
|
|
|
optimize_kprobe(ap);
|
|
return 0;
|
|
diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
|
|
index e57be7031cb3..bf694c709b96 100644
|
|
--- a/kernel/locking/lockdep.c
|
|
+++ b/kernel/locking/lockdep.c
|
|
@@ -3650,9 +3650,6 @@ __lock_set_class(struct lockdep_map *lock, const char *name,
|
|
unsigned int depth;
|
|
int i;
|
|
|
|
- if (unlikely(!debug_locks))
|
|
- return 0;
|
|
-
|
|
depth = curr->lockdep_depth;
|
|
/*
|
|
* This function is about (re)setting the class of a held lock,
|
|
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
|
|
index 9829ede00498..a5d163903835 100644
|
|
--- a/kernel/sched/fair.c
|
|
+++ b/kernel/sched/fair.c
|
|
@@ -4672,12 +4672,15 @@ static enum hrtimer_restart sched_cfs_slack_timer(struct hrtimer *timer)
|
|
return HRTIMER_NORESTART;
|
|
}
|
|
|
|
+extern const u64 max_cfs_quota_period;
|
|
+
|
|
static enum hrtimer_restart sched_cfs_period_timer(struct hrtimer *timer)
|
|
{
|
|
struct cfs_bandwidth *cfs_b =
|
|
container_of(timer, struct cfs_bandwidth, period_timer);
|
|
int overrun;
|
|
int idle = 0;
|
|
+ int count = 0;
|
|
|
|
raw_spin_lock(&cfs_b->lock);
|
|
for (;;) {
|
|
@@ -4685,6 +4688,28 @@ static enum hrtimer_restart sched_cfs_period_timer(struct hrtimer *timer)
|
|
if (!overrun)
|
|
break;
|
|
|
|
+ if (++count > 3) {
|
|
+ u64 new, old = ktime_to_ns(cfs_b->period);
|
|
+
|
|
+ new = (old * 147) / 128; /* ~115% */
|
|
+ new = min(new, max_cfs_quota_period);
|
|
+
|
|
+ cfs_b->period = ns_to_ktime(new);
|
|
+
|
|
+ /* since max is 1s, this is limited to 1e9^2, which fits in u64 */
|
|
+ cfs_b->quota *= new;
|
|
+ cfs_b->quota = div64_u64(cfs_b->quota, old);
|
|
+
|
|
+ pr_warn_ratelimited(
|
|
+ "cfs_period_timer[cpu%d]: period too short, scaling up (new cfs_period_us %lld, cfs_quota_us = %lld)\n",
|
|
+ smp_processor_id(),
|
|
+ div_u64(new, NSEC_PER_USEC),
|
|
+ div_u64(cfs_b->quota, NSEC_PER_USEC));
|
|
+
|
|
+ /* reset count so we don't come right back in here */
|
|
+ count = 0;
|
|
+ }
|
|
+
|
|
idle = do_sched_cfs_period_timer(cfs_b, overrun);
|
|
}
|
|
if (idle)
|
|
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
|
|
index 34a3b8a262a9..f13601a616ad 100644
|
|
--- a/kernel/sysctl.c
|
|
+++ b/kernel/sysctl.c
|
|
@@ -124,6 +124,7 @@ static int zero;
|
|
static int __maybe_unused one = 1;
|
|
static int __maybe_unused two = 2;
|
|
static int __maybe_unused four = 4;
|
|
+static unsigned long zero_ul;
|
|
static unsigned long one_ul = 1;
|
|
static unsigned long long_max = LONG_MAX;
|
|
static int one_hundred = 100;
|
|
@@ -1682,7 +1683,7 @@ static struct ctl_table fs_table[] = {
|
|
.maxlen = sizeof(files_stat.max_files),
|
|
.mode = 0644,
|
|
.proc_handler = proc_doulongvec_minmax,
|
|
- .extra1 = &zero,
|
|
+ .extra1 = &zero_ul,
|
|
.extra2 = &long_max,
|
|
},
|
|
{
|
|
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
|
|
index 9937d7cf2a64..3e92852c8b23 100644
|
|
--- a/kernel/trace/ftrace.c
|
|
+++ b/kernel/trace/ftrace.c
|
|
@@ -33,6 +33,7 @@
|
|
#include <linux/list.h>
|
|
#include <linux/hash.h>
|
|
#include <linux/rcupdate.h>
|
|
+#include <linux/kprobes.h>
|
|
|
|
#include <trace/events/sched.h>
|
|
|
|
@@ -6035,7 +6036,7 @@ void ftrace_reset_array_ops(struct trace_array *tr)
|
|
tr->ops->func = ftrace_stub;
|
|
}
|
|
|
|
-static inline void
|
|
+static nokprobe_inline void
|
|
__ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip,
|
|
struct ftrace_ops *ignored, struct pt_regs *regs)
|
|
{
|
|
@@ -6098,11 +6099,13 @@ static void ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip,
|
|
{
|
|
__ftrace_ops_list_func(ip, parent_ip, NULL, regs);
|
|
}
|
|
+NOKPROBE_SYMBOL(ftrace_ops_list_func);
|
|
#else
|
|
static void ftrace_ops_no_ops(unsigned long ip, unsigned long parent_ip)
|
|
{
|
|
__ftrace_ops_list_func(ip, parent_ip, NULL, NULL);
|
|
}
|
|
+NOKPROBE_SYMBOL(ftrace_ops_no_ops);
|
|
#endif
|
|
|
|
/*
|
|
@@ -6132,6 +6135,7 @@ static void ftrace_ops_assist_func(unsigned long ip, unsigned long parent_ip,
|
|
preempt_enable_notrace();
|
|
trace_clear_recursion(bit);
|
|
}
|
|
+NOKPROBE_SYMBOL(ftrace_ops_assist_func);
|
|
|
|
/**
|
|
* ftrace_ops_get_func - get the function a trampoline should call
|
|
diff --git a/mm/mmap.c b/mm/mmap.c
|
|
index 00dab291e61d..59fd53b41c9c 100644
|
|
--- a/mm/mmap.c
|
|
+++ b/mm/mmap.c
|
|
@@ -45,6 +45,7 @@
|
|
#include <linux/moduleparam.h>
|
|
#include <linux/pkeys.h>
|
|
#include <linux/oom.h>
|
|
+#include <linux/sched/mm.h>
|
|
|
|
#include <linux/uaccess.h>
|
|
#include <asm/cacheflush.h>
|
|
@@ -2448,7 +2449,8 @@ find_extend_vma(struct mm_struct *mm, unsigned long addr)
|
|
vma = find_vma_prev(mm, addr, &prev);
|
|
if (vma && (vma->vm_start <= addr))
|
|
return vma;
|
|
- if (!prev || expand_stack(prev, addr))
|
|
+ /* don't alter vm_end if the coredump is running */
|
|
+ if (!prev || !mmget_still_valid(mm) || expand_stack(prev, addr))
|
|
return NULL;
|
|
if (prev->vm_flags & VM_LOCKED)
|
|
populate_vma_page_range(prev, addr, prev->vm_end, NULL);
|
|
@@ -2474,6 +2476,9 @@ find_extend_vma(struct mm_struct *mm, unsigned long addr)
|
|
return vma;
|
|
if (!(vma->vm_flags & VM_GROWSDOWN))
|
|
return NULL;
|
|
+ /* don't alter vm_start if the coredump is running */
|
|
+ if (!mmget_still_valid(mm))
|
|
+ return NULL;
|
|
start = vma->vm_start;
|
|
if (expand_stack(vma, addr))
|
|
return NULL;
|
|
diff --git a/mm/percpu.c b/mm/percpu.c
|
|
index 3074148b7e0d..0c06e2f549a7 100644
|
|
--- a/mm/percpu.c
|
|
+++ b/mm/percpu.c
|
|
@@ -2507,8 +2507,8 @@ int __init pcpu_embed_first_chunk(size_t reserved_size, size_t dyn_size,
|
|
ai->groups[group].base_offset = areas[group] - base;
|
|
}
|
|
|
|
- pr_info("Embedded %zu pages/cpu @%p s%zu r%zu d%zu u%zu\n",
|
|
- PFN_DOWN(size_sum), base, ai->static_size, ai->reserved_size,
|
|
+ pr_info("Embedded %zu pages/cpu s%zu r%zu d%zu u%zu\n",
|
|
+ PFN_DOWN(size_sum), ai->static_size, ai->reserved_size,
|
|
ai->dyn_size, ai->unit_size);
|
|
|
|
rc = pcpu_setup_first_chunk(ai, base);
|
|
@@ -2629,8 +2629,8 @@ int __init pcpu_page_first_chunk(size_t reserved_size,
|
|
}
|
|
|
|
/* we're ready, commit */
|
|
- pr_info("%d %s pages/cpu @%p s%zu r%zu d%zu\n",
|
|
- unit_pages, psize_str, vm.addr, ai->static_size,
|
|
+ pr_info("%d %s pages/cpu s%zu r%zu d%zu\n",
|
|
+ unit_pages, psize_str, ai->static_size,
|
|
ai->reserved_size, ai->dyn_size);
|
|
|
|
rc = pcpu_setup_first_chunk(ai, vm.addr);
|
|
diff --git a/mm/vmstat.c b/mm/vmstat.c
|
|
index 6389e876c7a7..28c45c26f901 100644
|
|
--- a/mm/vmstat.c
|
|
+++ b/mm/vmstat.c
|
|
@@ -1201,13 +1201,8 @@ const char * const vmstat_text[] = {
|
|
#endif
|
|
#endif /* CONFIG_MEMORY_BALLOON */
|
|
#ifdef CONFIG_DEBUG_TLBFLUSH
|
|
-#ifdef CONFIG_SMP
|
|
"nr_tlb_remote_flush",
|
|
"nr_tlb_remote_flush_received",
|
|
-#else
|
|
- "", /* nr_tlb_remote_flush */
|
|
- "", /* nr_tlb_remote_flush_received */
|
|
-#endif /* CONFIG_SMP */
|
|
"nr_tlb_local_flush_all",
|
|
"nr_tlb_local_flush_one",
|
|
#endif /* CONFIG_DEBUG_TLBFLUSH */
|
|
diff --git a/net/atm/lec.c b/net/atm/lec.c
|
|
index 9f2365694ad4..85ce89c8a35c 100644
|
|
--- a/net/atm/lec.c
|
|
+++ b/net/atm/lec.c
|
|
@@ -710,7 +710,10 @@ static int lec_vcc_attach(struct atm_vcc *vcc, void __user *arg)
|
|
|
|
static int lec_mcast_attach(struct atm_vcc *vcc, int arg)
|
|
{
|
|
- if (arg < 0 || arg >= MAX_LEC_ITF || !dev_lec[arg])
|
|
+ if (arg < 0 || arg >= MAX_LEC_ITF)
|
|
+ return -EINVAL;
|
|
+ arg = array_index_nospec(arg, MAX_LEC_ITF);
|
|
+ if (!dev_lec[arg])
|
|
return -EINVAL;
|
|
vcc->proto_data = dev_lec[arg];
|
|
return lec_mcast_make(netdev_priv(dev_lec[arg]), vcc);
|
|
@@ -728,6 +731,7 @@ static int lecd_attach(struct atm_vcc *vcc, int arg)
|
|
i = arg;
|
|
if (arg >= MAX_LEC_ITF)
|
|
return -EINVAL;
|
|
+ i = array_index_nospec(arg, MAX_LEC_ITF);
|
|
if (!dev_lec[i]) {
|
|
int size;
|
|
|
|
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c
|
|
index 7637f58c1226..10fa84056cb5 100644
|
|
--- a/net/bridge/br_input.c
|
|
+++ b/net/bridge/br_input.c
|
|
@@ -236,13 +236,10 @@ static void __br_handle_local_finish(struct sk_buff *skb)
|
|
/* note: already called with rcu_read_lock */
|
|
static int br_handle_local_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
|
|
{
|
|
- struct net_bridge_port *p = br_port_get_rcu(skb->dev);
|
|
-
|
|
__br_handle_local_finish(skb);
|
|
|
|
- BR_INPUT_SKB_CB(skb)->brdev = p->br->dev;
|
|
- br_pass_frame_up(skb);
|
|
- return 0;
|
|
+ /* return 1 to signal the okfn() was called so it's ok to use the skb */
|
|
+ return 1;
|
|
}
|
|
|
|
/*
|
|
@@ -318,10 +315,18 @@ rx_handler_result_t br_handle_frame(struct sk_buff **pskb)
|
|
goto forward;
|
|
}
|
|
|
|
- /* Deliver packet to local host only */
|
|
- NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN, dev_net(skb->dev),
|
|
- NULL, skb, skb->dev, NULL, br_handle_local_finish);
|
|
- return RX_HANDLER_CONSUMED;
|
|
+ /* The else clause should be hit when nf_hook():
|
|
+ * - returns < 0 (drop/error)
|
|
+ * - returns = 0 (stolen/nf_queue)
|
|
+ * Thus return 1 from the okfn() to signal the skb is ok to pass
|
|
+ */
|
|
+ if (NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN,
|
|
+ dev_net(skb->dev), NULL, skb, skb->dev, NULL,
|
|
+ br_handle_local_finish) == 1) {
|
|
+ return RX_HANDLER_PASS;
|
|
+ } else {
|
|
+ return RX_HANDLER_CONSUMED;
|
|
+ }
|
|
}
|
|
|
|
forward:
|
|
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
|
|
index 8dc5c8d69bcd..e83048cb53ce 100644
|
|
--- a/net/bridge/br_multicast.c
|
|
+++ b/net/bridge/br_multicast.c
|
|
@@ -2119,7 +2119,8 @@ static void br_multicast_start_querier(struct net_bridge *br,
|
|
|
|
__br_multicast_open(br, query);
|
|
|
|
- list_for_each_entry(port, &br->port_list, list) {
|
|
+ rcu_read_lock();
|
|
+ list_for_each_entry_rcu(port, &br->port_list, list) {
|
|
if (port->state == BR_STATE_DISABLED ||
|
|
port->state == BR_STATE_BLOCKING)
|
|
continue;
|
|
@@ -2131,6 +2132,7 @@ static void br_multicast_start_querier(struct net_bridge *br,
|
|
br_multicast_enable(&port->ip6_own_query);
|
|
#endif
|
|
}
|
|
+ rcu_read_unlock();
|
|
}
|
|
|
|
int br_multicast_toggle(struct net_bridge *br, unsigned long val)
|
|
diff --git a/net/ieee802154/6lowpan/reassembly.c b/net/ieee802154/6lowpan/reassembly.c
|
|
index 2cc224106b69..ec7a5da56129 100644
|
|
--- a/net/ieee802154/6lowpan/reassembly.c
|
|
+++ b/net/ieee802154/6lowpan/reassembly.c
|
|
@@ -25,7 +25,7 @@
|
|
|
|
#include <net/ieee802154_netdev.h>
|
|
#include <net/6lowpan.h>
|
|
-#include <net/ipv6.h>
|
|
+#include <net/ipv6_frag.h>
|
|
#include <net/inet_frag.h>
|
|
|
|
#include "6lowpan_i.h"
|
|
diff --git a/net/ipv4/fou.c b/net/ipv4/fou.c
|
|
index c9ec1603666b..665f11d7388e 100644
|
|
--- a/net/ipv4/fou.c
|
|
+++ b/net/ipv4/fou.c
|
|
@@ -120,6 +120,7 @@ static int gue_udp_recv(struct sock *sk, struct sk_buff *skb)
|
|
struct guehdr *guehdr;
|
|
void *data;
|
|
u16 doffset = 0;
|
|
+ u8 proto_ctype;
|
|
|
|
if (!fou)
|
|
return 1;
|
|
@@ -211,13 +212,14 @@ static int gue_udp_recv(struct sock *sk, struct sk_buff *skb)
|
|
if (unlikely(guehdr->control))
|
|
return gue_control_message(skb, guehdr);
|
|
|
|
+ proto_ctype = guehdr->proto_ctype;
|
|
__skb_pull(skb, sizeof(struct udphdr) + hdrlen);
|
|
skb_reset_transport_header(skb);
|
|
|
|
if (iptunnel_pull_offloads(skb))
|
|
goto drop;
|
|
|
|
- return -guehdr->proto_ctype;
|
|
+ return -proto_ctype;
|
|
|
|
drop:
|
|
kfree_skb(skb);
|
|
diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c
|
|
index 6ffee9d2b0e5..481cded81b2d 100644
|
|
--- a/net/ipv4/inet_fragment.c
|
|
+++ b/net/ipv4/inet_fragment.c
|
|
@@ -24,6 +24,62 @@
|
|
#include <net/sock.h>
|
|
#include <net/inet_frag.h>
|
|
#include <net/inet_ecn.h>
|
|
+#include <net/ip.h>
|
|
+#include <net/ipv6.h>
|
|
+
|
|
+/* Use skb->cb to track consecutive/adjacent fragments coming at
|
|
+ * the end of the queue. Nodes in the rb-tree queue will
|
|
+ * contain "runs" of one or more adjacent fragments.
|
|
+ *
|
|
+ * Invariants:
|
|
+ * - next_frag is NULL at the tail of a "run";
|
|
+ * - the head of a "run" has the sum of all fragment lengths in frag_run_len.
|
|
+ */
|
|
+struct ipfrag_skb_cb {
|
|
+ union {
|
|
+ struct inet_skb_parm h4;
|
|
+ struct inet6_skb_parm h6;
|
|
+ };
|
|
+ struct sk_buff *next_frag;
|
|
+ int frag_run_len;
|
|
+};
|
|
+
|
|
+#define FRAG_CB(skb) ((struct ipfrag_skb_cb *)((skb)->cb))
|
|
+
|
|
+static void fragcb_clear(struct sk_buff *skb)
|
|
+{
|
|
+ RB_CLEAR_NODE(&skb->rbnode);
|
|
+ FRAG_CB(skb)->next_frag = NULL;
|
|
+ FRAG_CB(skb)->frag_run_len = skb->len;
|
|
+}
|
|
+
|
|
+/* Append skb to the last "run". */
|
|
+static void fragrun_append_to_last(struct inet_frag_queue *q,
|
|
+ struct sk_buff *skb)
|
|
+{
|
|
+ fragcb_clear(skb);
|
|
+
|
|
+ FRAG_CB(q->last_run_head)->frag_run_len += skb->len;
|
|
+ FRAG_CB(q->fragments_tail)->next_frag = skb;
|
|
+ q->fragments_tail = skb;
|
|
+}
|
|
+
|
|
+/* Create a new "run" with the skb. */
|
|
+static void fragrun_create(struct inet_frag_queue *q, struct sk_buff *skb)
|
|
+{
|
|
+ BUILD_BUG_ON(sizeof(struct ipfrag_skb_cb) > sizeof(skb->cb));
|
|
+ fragcb_clear(skb);
|
|
+
|
|
+ if (q->last_run_head)
|
|
+ rb_link_node(&skb->rbnode, &q->last_run_head->rbnode,
|
|
+ &q->last_run_head->rbnode.rb_right);
|
|
+ else
|
|
+ rb_link_node(&skb->rbnode, NULL, &q->rb_fragments.rb_node);
|
|
+ rb_insert_color(&skb->rbnode, &q->rb_fragments);
|
|
+
|
|
+ q->fragments_tail = skb;
|
|
+ q->last_run_head = skb;
|
|
+}
|
|
|
|
/* Given the OR values of all fragments, apply RFC 3168 5.3 requirements
|
|
* Value : 0xff if frame should be dropped.
|
|
@@ -122,6 +178,28 @@ static void inet_frag_destroy_rcu(struct rcu_head *head)
|
|
kmem_cache_free(f->frags_cachep, q);
|
|
}
|
|
|
|
+unsigned int inet_frag_rbtree_purge(struct rb_root *root)
|
|
+{
|
|
+ struct rb_node *p = rb_first(root);
|
|
+ unsigned int sum = 0;
|
|
+
|
|
+ while (p) {
|
|
+ struct sk_buff *skb = rb_entry(p, struct sk_buff, rbnode);
|
|
+
|
|
+ p = rb_next(p);
|
|
+ rb_erase(&skb->rbnode, root);
|
|
+ while (skb) {
|
|
+ struct sk_buff *next = FRAG_CB(skb)->next_frag;
|
|
+
|
|
+ sum += skb->truesize;
|
|
+ kfree_skb(skb);
|
|
+ skb = next;
|
|
+ }
|
|
+ }
|
|
+ return sum;
|
|
+}
|
|
+EXPORT_SYMBOL(inet_frag_rbtree_purge);
|
|
+
|
|
void inet_frag_destroy(struct inet_frag_queue *q)
|
|
{
|
|
struct sk_buff *fp;
|
|
@@ -224,3 +302,218 @@ struct inet_frag_queue *inet_frag_find(struct netns_frags *nf, void *key)
|
|
return fq;
|
|
}
|
|
EXPORT_SYMBOL(inet_frag_find);
|
|
+
|
|
+int inet_frag_queue_insert(struct inet_frag_queue *q, struct sk_buff *skb,
|
|
+ int offset, int end)
|
|
+{
|
|
+ struct sk_buff *last = q->fragments_tail;
|
|
+
|
|
+ /* RFC5722, Section 4, amended by Errata ID : 3089
|
|
+ * When reassembling an IPv6 datagram, if
|
|
+ * one or more its constituent fragments is determined to be an
|
|
+ * overlapping fragment, the entire datagram (and any constituent
|
|
+ * fragments) MUST be silently discarded.
|
|
+ *
|
|
+ * Duplicates, however, should be ignored (i.e. skb dropped, but the
|
|
+ * queue/fragments kept for later reassembly).
|
|
+ */
|
|
+ if (!last)
|
|
+ fragrun_create(q, skb); /* First fragment. */
|
|
+ else if (last->ip_defrag_offset + last->len < end) {
|
|
+ /* This is the common case: skb goes to the end. */
|
|
+ /* Detect and discard overlaps. */
|
|
+ if (offset < last->ip_defrag_offset + last->len)
|
|
+ return IPFRAG_OVERLAP;
|
|
+ if (offset == last->ip_defrag_offset + last->len)
|
|
+ fragrun_append_to_last(q, skb);
|
|
+ else
|
|
+ fragrun_create(q, skb);
|
|
+ } else {
|
|
+ /* Binary search. Note that skb can become the first fragment,
|
|
+ * but not the last (covered above).
|
|
+ */
|
|
+ struct rb_node **rbn, *parent;
|
|
+
|
|
+ rbn = &q->rb_fragments.rb_node;
|
|
+ do {
|
|
+ struct sk_buff *curr;
|
|
+ int curr_run_end;
|
|
+
|
|
+ parent = *rbn;
|
|
+ curr = rb_to_skb(parent);
|
|
+ curr_run_end = curr->ip_defrag_offset +
|
|
+ FRAG_CB(curr)->frag_run_len;
|
|
+ if (end <= curr->ip_defrag_offset)
|
|
+ rbn = &parent->rb_left;
|
|
+ else if (offset >= curr_run_end)
|
|
+ rbn = &parent->rb_right;
|
|
+ else if (offset >= curr->ip_defrag_offset &&
|
|
+ end <= curr_run_end)
|
|
+ return IPFRAG_DUP;
|
|
+ else
|
|
+ return IPFRAG_OVERLAP;
|
|
+ } while (*rbn);
|
|
+ /* Here we have parent properly set, and rbn pointing to
|
|
+ * one of its NULL left/right children. Insert skb.
|
|
+ */
|
|
+ fragcb_clear(skb);
|
|
+ rb_link_node(&skb->rbnode, parent, rbn);
|
|
+ rb_insert_color(&skb->rbnode, &q->rb_fragments);
|
|
+ }
|
|
+
|
|
+ skb->ip_defrag_offset = offset;
|
|
+
|
|
+ return IPFRAG_OK;
|
|
+}
|
|
+EXPORT_SYMBOL(inet_frag_queue_insert);
|
|
+
|
|
+void *inet_frag_reasm_prepare(struct inet_frag_queue *q, struct sk_buff *skb,
|
|
+ struct sk_buff *parent)
|
|
+{
|
|
+ struct sk_buff *fp, *head = skb_rb_first(&q->rb_fragments);
|
|
+ struct sk_buff **nextp;
|
|
+ int delta;
|
|
+
|
|
+ if (head != skb) {
|
|
+ fp = skb_clone(skb, GFP_ATOMIC);
|
|
+ if (!fp)
|
|
+ return NULL;
|
|
+ FRAG_CB(fp)->next_frag = FRAG_CB(skb)->next_frag;
|
|
+ if (RB_EMPTY_NODE(&skb->rbnode))
|
|
+ FRAG_CB(parent)->next_frag = fp;
|
|
+ else
|
|
+ rb_replace_node(&skb->rbnode, &fp->rbnode,
|
|
+ &q->rb_fragments);
|
|
+ if (q->fragments_tail == skb)
|
|
+ q->fragments_tail = fp;
|
|
+ skb_morph(skb, head);
|
|
+ FRAG_CB(skb)->next_frag = FRAG_CB(head)->next_frag;
|
|
+ rb_replace_node(&head->rbnode, &skb->rbnode,
|
|
+ &q->rb_fragments);
|
|
+ consume_skb(head);
|
|
+ head = skb;
|
|
+ }
|
|
+ WARN_ON(head->ip_defrag_offset != 0);
|
|
+
|
|
+ delta = -head->truesize;
|
|
+
|
|
+ /* Head of list must not be cloned. */
|
|
+ if (skb_unclone(head, GFP_ATOMIC))
|
|
+ return NULL;
|
|
+
|
|
+ delta += head->truesize;
|
|
+ if (delta)
|
|
+ add_frag_mem_limit(q->net, delta);
|
|
+
|
|
+ /* If the first fragment is fragmented itself, we split
|
|
+ * it to two chunks: the first with data and paged part
|
|
+ * and the second, holding only fragments.
|
|
+ */
|
|
+ if (skb_has_frag_list(head)) {
|
|
+ struct sk_buff *clone;
|
|
+ int i, plen = 0;
|
|
+
|
|
+ clone = alloc_skb(0, GFP_ATOMIC);
|
|
+ if (!clone)
|
|
+ return NULL;
|
|
+ skb_shinfo(clone)->frag_list = skb_shinfo(head)->frag_list;
|
|
+ skb_frag_list_init(head);
|
|
+ for (i = 0; i < skb_shinfo(head)->nr_frags; i++)
|
|
+ plen += skb_frag_size(&skb_shinfo(head)->frags[i]);
|
|
+ clone->data_len = head->data_len - plen;
|
|
+ clone->len = clone->data_len;
|
|
+ head->truesize += clone->truesize;
|
|
+ clone->csum = 0;
|
|
+ clone->ip_summed = head->ip_summed;
|
|
+ add_frag_mem_limit(q->net, clone->truesize);
|
|
+ skb_shinfo(head)->frag_list = clone;
|
|
+ nextp = &clone->next;
|
|
+ } else {
|
|
+ nextp = &skb_shinfo(head)->frag_list;
|
|
+ }
|
|
+
|
|
+ return nextp;
|
|
+}
|
|
+EXPORT_SYMBOL(inet_frag_reasm_prepare);
|
|
+
|
|
+void inet_frag_reasm_finish(struct inet_frag_queue *q, struct sk_buff *head,
|
|
+ void *reasm_data)
|
|
+{
|
|
+ struct sk_buff **nextp = (struct sk_buff **)reasm_data;
|
|
+ struct rb_node *rbn;
|
|
+ struct sk_buff *fp;
|
|
+
|
|
+ skb_push(head, head->data - skb_network_header(head));
|
|
+
|
|
+ /* Traverse the tree in order, to build frag_list. */
|
|
+ fp = FRAG_CB(head)->next_frag;
|
|
+ rbn = rb_next(&head->rbnode);
|
|
+ rb_erase(&head->rbnode, &q->rb_fragments);
|
|
+ while (rbn || fp) {
|
|
+ /* fp points to the next sk_buff in the current run;
|
|
+ * rbn points to the next run.
|
|
+ */
|
|
+ /* Go through the current run. */
|
|
+ while (fp) {
|
|
+ *nextp = fp;
|
|
+ nextp = &fp->next;
|
|
+ fp->prev = NULL;
|
|
+ memset(&fp->rbnode, 0, sizeof(fp->rbnode));
|
|
+ fp->sk = NULL;
|
|
+ head->data_len += fp->len;
|
|
+ head->len += fp->len;
|
|
+ if (head->ip_summed != fp->ip_summed)
|
|
+ head->ip_summed = CHECKSUM_NONE;
|
|
+ else if (head->ip_summed == CHECKSUM_COMPLETE)
|
|
+ head->csum = csum_add(head->csum, fp->csum);
|
|
+ head->truesize += fp->truesize;
|
|
+ fp = FRAG_CB(fp)->next_frag;
|
|
+ }
|
|
+ /* Move to the next run. */
|
|
+ if (rbn) {
|
|
+ struct rb_node *rbnext = rb_next(rbn);
|
|
+
|
|
+ fp = rb_to_skb(rbn);
|
|
+ rb_erase(rbn, &q->rb_fragments);
|
|
+ rbn = rbnext;
|
|
+ }
|
|
+ }
|
|
+ sub_frag_mem_limit(q->net, head->truesize);
|
|
+
|
|
+ *nextp = NULL;
|
|
+ head->next = NULL;
|
|
+ head->prev = NULL;
|
|
+ head->tstamp = q->stamp;
|
|
+}
|
|
+EXPORT_SYMBOL(inet_frag_reasm_finish);
|
|
+
|
|
+struct sk_buff *inet_frag_pull_head(struct inet_frag_queue *q)
|
|
+{
|
|
+ struct sk_buff *head;
|
|
+
|
|
+ if (q->fragments) {
|
|
+ head = q->fragments;
|
|
+ q->fragments = head->next;
|
|
+ } else {
|
|
+ struct sk_buff *skb;
|
|
+
|
|
+ head = skb_rb_first(&q->rb_fragments);
|
|
+ if (!head)
|
|
+ return NULL;
|
|
+ skb = FRAG_CB(head)->next_frag;
|
|
+ if (skb)
|
|
+ rb_replace_node(&head->rbnode, &skb->rbnode,
|
|
+ &q->rb_fragments);
|
|
+ else
|
|
+ rb_erase(&head->rbnode, &q->rb_fragments);
|
|
+ memset(&head->rbnode, 0, sizeof(head->rbnode));
|
|
+ barrier();
|
|
+ }
|
|
+ if (head == q->fragments_tail)
|
|
+ q->fragments_tail = NULL;
|
|
+
|
|
+ sub_frag_mem_limit(q->net, head->truesize);
|
|
+
|
|
+ return head;
|
|
+}
|
|
+EXPORT_SYMBOL(inet_frag_pull_head);
|
|
diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c
|
|
index d95b32af4a0e..5a1d39e32196 100644
|
|
--- a/net/ipv4/ip_fragment.c
|
|
+++ b/net/ipv4/ip_fragment.c
|
|
@@ -57,57 +57,6 @@
|
|
*/
|
|
static const char ip_frag_cache_name[] = "ip4-frags";
|
|
|
|
-/* Use skb->cb to track consecutive/adjacent fragments coming at
|
|
- * the end of the queue. Nodes in the rb-tree queue will
|
|
- * contain "runs" of one or more adjacent fragments.
|
|
- *
|
|
- * Invariants:
|
|
- * - next_frag is NULL at the tail of a "run";
|
|
- * - the head of a "run" has the sum of all fragment lengths in frag_run_len.
|
|
- */
|
|
-struct ipfrag_skb_cb {
|
|
- struct inet_skb_parm h;
|
|
- struct sk_buff *next_frag;
|
|
- int frag_run_len;
|
|
-};
|
|
-
|
|
-#define FRAG_CB(skb) ((struct ipfrag_skb_cb *)((skb)->cb))
|
|
-
|
|
-static void ip4_frag_init_run(struct sk_buff *skb)
|
|
-{
|
|
- BUILD_BUG_ON(sizeof(struct ipfrag_skb_cb) > sizeof(skb->cb));
|
|
-
|
|
- FRAG_CB(skb)->next_frag = NULL;
|
|
- FRAG_CB(skb)->frag_run_len = skb->len;
|
|
-}
|
|
-
|
|
-/* Append skb to the last "run". */
|
|
-static void ip4_frag_append_to_last_run(struct inet_frag_queue *q,
|
|
- struct sk_buff *skb)
|
|
-{
|
|
- RB_CLEAR_NODE(&skb->rbnode);
|
|
- FRAG_CB(skb)->next_frag = NULL;
|
|
-
|
|
- FRAG_CB(q->last_run_head)->frag_run_len += skb->len;
|
|
- FRAG_CB(q->fragments_tail)->next_frag = skb;
|
|
- q->fragments_tail = skb;
|
|
-}
|
|
-
|
|
-/* Create a new "run" with the skb. */
|
|
-static void ip4_frag_create_run(struct inet_frag_queue *q, struct sk_buff *skb)
|
|
-{
|
|
- if (q->last_run_head)
|
|
- rb_link_node(&skb->rbnode, &q->last_run_head->rbnode,
|
|
- &q->last_run_head->rbnode.rb_right);
|
|
- else
|
|
- rb_link_node(&skb->rbnode, NULL, &q->rb_fragments.rb_node);
|
|
- rb_insert_color(&skb->rbnode, &q->rb_fragments);
|
|
-
|
|
- ip4_frag_init_run(skb);
|
|
- q->fragments_tail = skb;
|
|
- q->last_run_head = skb;
|
|
-}
|
|
-
|
|
/* Describe an entry in the "incomplete datagrams" queue. */
|
|
struct ipq {
|
|
struct inet_frag_queue q;
|
|
@@ -212,27 +161,9 @@ static void ip_expire(struct timer_list *t)
|
|
* pull the head out of the tree in order to be able to
|
|
* deal with head->dev.
|
|
*/
|
|
- if (qp->q.fragments) {
|
|
- head = qp->q.fragments;
|
|
- qp->q.fragments = head->next;
|
|
- } else {
|
|
- head = skb_rb_first(&qp->q.rb_fragments);
|
|
- if (!head)
|
|
- goto out;
|
|
- if (FRAG_CB(head)->next_frag)
|
|
- rb_replace_node(&head->rbnode,
|
|
- &FRAG_CB(head)->next_frag->rbnode,
|
|
- &qp->q.rb_fragments);
|
|
- else
|
|
- rb_erase(&head->rbnode, &qp->q.rb_fragments);
|
|
- memset(&head->rbnode, 0, sizeof(head->rbnode));
|
|
- barrier();
|
|
- }
|
|
- if (head == qp->q.fragments_tail)
|
|
- qp->q.fragments_tail = NULL;
|
|
-
|
|
- sub_frag_mem_limit(qp->q.net, head->truesize);
|
|
-
|
|
+ head = inet_frag_pull_head(&qp->q);
|
|
+ if (!head)
|
|
+ goto out;
|
|
head->dev = dev_get_by_index_rcu(net, qp->iif);
|
|
if (!head->dev)
|
|
goto out;
|
|
@@ -345,12 +276,10 @@ static int ip_frag_reinit(struct ipq *qp)
|
|
static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
|
|
{
|
|
struct net *net = container_of(qp->q.net, struct net, ipv4.frags);
|
|
- struct rb_node **rbn, *parent;
|
|
- struct sk_buff *skb1, *prev_tail;
|
|
- int ihl, end, skb1_run_end;
|
|
+ int ihl, end, flags, offset;
|
|
+ struct sk_buff *prev_tail;
|
|
struct net_device *dev;
|
|
unsigned int fragsize;
|
|
- int flags, offset;
|
|
int err = -ENOENT;
|
|
u8 ecn;
|
|
|
|
@@ -382,7 +311,7 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
|
|
*/
|
|
if (end < qp->q.len ||
|
|
((qp->q.flags & INET_FRAG_LAST_IN) && end != qp->q.len))
|
|
- goto err;
|
|
+ goto discard_qp;
|
|
qp->q.flags |= INET_FRAG_LAST_IN;
|
|
qp->q.len = end;
|
|
} else {
|
|
@@ -394,82 +323,33 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
|
|
if (end > qp->q.len) {
|
|
/* Some bits beyond end -> corruption. */
|
|
if (qp->q.flags & INET_FRAG_LAST_IN)
|
|
- goto err;
|
|
+ goto discard_qp;
|
|
qp->q.len = end;
|
|
}
|
|
}
|
|
if (end == offset)
|
|
- goto err;
|
|
+ goto discard_qp;
|
|
|
|
err = -ENOMEM;
|
|
if (!pskb_pull(skb, skb_network_offset(skb) + ihl))
|
|
- goto err;
|
|
+ goto discard_qp;
|
|
|
|
err = pskb_trim_rcsum(skb, end - offset);
|
|
if (err)
|
|
- goto err;
|
|
+ goto discard_qp;
|
|
|
|
/* Note : skb->rbnode and skb->dev share the same location. */
|
|
dev = skb->dev;
|
|
/* Makes sure compiler wont do silly aliasing games */
|
|
barrier();
|
|
|
|
- /* RFC5722, Section 4, amended by Errata ID : 3089
|
|
- * When reassembling an IPv6 datagram, if
|
|
- * one or more its constituent fragments is determined to be an
|
|
- * overlapping fragment, the entire datagram (and any constituent
|
|
- * fragments) MUST be silently discarded.
|
|
- *
|
|
- * We do the same here for IPv4 (and increment an snmp counter) but
|
|
- * we do not want to drop the whole queue in response to a duplicate
|
|
- * fragment.
|
|
- */
|
|
-
|
|
- err = -EINVAL;
|
|
- /* Find out where to put this fragment. */
|
|
prev_tail = qp->q.fragments_tail;
|
|
- if (!prev_tail)
|
|
- ip4_frag_create_run(&qp->q, skb); /* First fragment. */
|
|
- else if (prev_tail->ip_defrag_offset + prev_tail->len < end) {
|
|
- /* This is the common case: skb goes to the end. */
|
|
- /* Detect and discard overlaps. */
|
|
- if (offset < prev_tail->ip_defrag_offset + prev_tail->len)
|
|
- goto discard_qp;
|
|
- if (offset == prev_tail->ip_defrag_offset + prev_tail->len)
|
|
- ip4_frag_append_to_last_run(&qp->q, skb);
|
|
- else
|
|
- ip4_frag_create_run(&qp->q, skb);
|
|
- } else {
|
|
- /* Binary search. Note that skb can become the first fragment,
|
|
- * but not the last (covered above).
|
|
- */
|
|
- rbn = &qp->q.rb_fragments.rb_node;
|
|
- do {
|
|
- parent = *rbn;
|
|
- skb1 = rb_to_skb(parent);
|
|
- skb1_run_end = skb1->ip_defrag_offset +
|
|
- FRAG_CB(skb1)->frag_run_len;
|
|
- if (end <= skb1->ip_defrag_offset)
|
|
- rbn = &parent->rb_left;
|
|
- else if (offset >= skb1_run_end)
|
|
- rbn = &parent->rb_right;
|
|
- else if (offset >= skb1->ip_defrag_offset &&
|
|
- end <= skb1_run_end)
|
|
- goto err; /* No new data, potential duplicate */
|
|
- else
|
|
- goto discard_qp; /* Found an overlap */
|
|
- } while (*rbn);
|
|
- /* Here we have parent properly set, and rbn pointing to
|
|
- * one of its NULL left/right children. Insert skb.
|
|
- */
|
|
- ip4_frag_init_run(skb);
|
|
- rb_link_node(&skb->rbnode, parent, rbn);
|
|
- rb_insert_color(&skb->rbnode, &qp->q.rb_fragments);
|
|
- }
|
|
+ err = inet_frag_queue_insert(&qp->q, skb, offset, end);
|
|
+ if (err)
|
|
+ goto insert_error;
|
|
|
|
if (dev)
|
|
qp->iif = dev->ifindex;
|
|
- skb->ip_defrag_offset = offset;
|
|
|
|
qp->q.stamp = skb->tstamp;
|
|
qp->q.meat += skb->len;
|
|
@@ -494,15 +374,24 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
|
|
skb->_skb_refdst = 0UL;
|
|
err = ip_frag_reasm(qp, skb, prev_tail, dev);
|
|
skb->_skb_refdst = orefdst;
|
|
+ if (err)
|
|
+ inet_frag_kill(&qp->q);
|
|
return err;
|
|
}
|
|
|
|
skb_dst_drop(skb);
|
|
return -EINPROGRESS;
|
|
|
|
+insert_error:
|
|
+ if (err == IPFRAG_DUP) {
|
|
+ kfree_skb(skb);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ err = -EINVAL;
|
|
+ __IP_INC_STATS(net, IPSTATS_MIB_REASM_OVERLAPS);
|
|
discard_qp:
|
|
inet_frag_kill(&qp->q);
|
|
- __IP_INC_STATS(net, IPSTATS_MIB_REASM_OVERLAPS);
|
|
+ __IP_INC_STATS(net, IPSTATS_MIB_REASMFAILS);
|
|
err:
|
|
kfree_skb(skb);
|
|
return err;
|
|
@@ -514,13 +403,8 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *skb,
|
|
{
|
|
struct net *net = container_of(qp->q.net, struct net, ipv4.frags);
|
|
struct iphdr *iph;
|
|
- struct sk_buff *fp, *head = skb_rb_first(&qp->q.rb_fragments);
|
|
- struct sk_buff **nextp; /* To build frag_list. */
|
|
- struct rb_node *rbn;
|
|
- int len;
|
|
- int ihlen;
|
|
- int delta;
|
|
- int err;
|
|
+ void *reasm_data;
|
|
+ int len, err;
|
|
u8 ecn;
|
|
|
|
ipq_kill(qp);
|
|
@@ -530,117 +414,23 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *skb,
|
|
err = -EINVAL;
|
|
goto out_fail;
|
|
}
|
|
- /* Make the one we just received the head. */
|
|
- if (head != skb) {
|
|
- fp = skb_clone(skb, GFP_ATOMIC);
|
|
- if (!fp)
|
|
- goto out_nomem;
|
|
- FRAG_CB(fp)->next_frag = FRAG_CB(skb)->next_frag;
|
|
- if (RB_EMPTY_NODE(&skb->rbnode))
|
|
- FRAG_CB(prev_tail)->next_frag = fp;
|
|
- else
|
|
- rb_replace_node(&skb->rbnode, &fp->rbnode,
|
|
- &qp->q.rb_fragments);
|
|
- if (qp->q.fragments_tail == skb)
|
|
- qp->q.fragments_tail = fp;
|
|
- skb_morph(skb, head);
|
|
- FRAG_CB(skb)->next_frag = FRAG_CB(head)->next_frag;
|
|
- rb_replace_node(&head->rbnode, &skb->rbnode,
|
|
- &qp->q.rb_fragments);
|
|
- consume_skb(head);
|
|
- head = skb;
|
|
- }
|
|
|
|
- WARN_ON(head->ip_defrag_offset != 0);
|
|
-
|
|
- /* Allocate a new buffer for the datagram. */
|
|
- ihlen = ip_hdrlen(head);
|
|
- len = ihlen + qp->q.len;
|
|
+ /* Make the one we just received the head. */
|
|
+ reasm_data = inet_frag_reasm_prepare(&qp->q, skb, prev_tail);
|
|
+ if (!reasm_data)
|
|
+ goto out_nomem;
|
|
|
|
+ len = ip_hdrlen(skb) + qp->q.len;
|
|
err = -E2BIG;
|
|
if (len > 65535)
|
|
goto out_oversize;
|
|
|
|
- delta = - head->truesize;
|
|
-
|
|
- /* Head of list must not be cloned. */
|
|
- if (skb_unclone(head, GFP_ATOMIC))
|
|
- goto out_nomem;
|
|
-
|
|
- delta += head->truesize;
|
|
- if (delta)
|
|
- add_frag_mem_limit(qp->q.net, delta);
|
|
-
|
|
- /* If the first fragment is fragmented itself, we split
|
|
- * it to two chunks: the first with data and paged part
|
|
- * and the second, holding only fragments. */
|
|
- if (skb_has_frag_list(head)) {
|
|
- struct sk_buff *clone;
|
|
- int i, plen = 0;
|
|
-
|
|
- clone = alloc_skb(0, GFP_ATOMIC);
|
|
- if (!clone)
|
|
- goto out_nomem;
|
|
- skb_shinfo(clone)->frag_list = skb_shinfo(head)->frag_list;
|
|
- skb_frag_list_init(head);
|
|
- for (i = 0; i < skb_shinfo(head)->nr_frags; i++)
|
|
- plen += skb_frag_size(&skb_shinfo(head)->frags[i]);
|
|
- clone->len = clone->data_len = head->data_len - plen;
|
|
- head->truesize += clone->truesize;
|
|
- clone->csum = 0;
|
|
- clone->ip_summed = head->ip_summed;
|
|
- add_frag_mem_limit(qp->q.net, clone->truesize);
|
|
- skb_shinfo(head)->frag_list = clone;
|
|
- nextp = &clone->next;
|
|
- } else {
|
|
- nextp = &skb_shinfo(head)->frag_list;
|
|
- }
|
|
+ inet_frag_reasm_finish(&qp->q, skb, reasm_data);
|
|
|
|
- skb_push(head, head->data - skb_network_header(head));
|
|
+ skb->dev = dev;
|
|
+ IPCB(skb)->frag_max_size = max(qp->max_df_size, qp->q.max_size);
|
|
|
|
- /* Traverse the tree in order, to build frag_list. */
|
|
- fp = FRAG_CB(head)->next_frag;
|
|
- rbn = rb_next(&head->rbnode);
|
|
- rb_erase(&head->rbnode, &qp->q.rb_fragments);
|
|
- while (rbn || fp) {
|
|
- /* fp points to the next sk_buff in the current run;
|
|
- * rbn points to the next run.
|
|
- */
|
|
- /* Go through the current run. */
|
|
- while (fp) {
|
|
- *nextp = fp;
|
|
- nextp = &fp->next;
|
|
- fp->prev = NULL;
|
|
- memset(&fp->rbnode, 0, sizeof(fp->rbnode));
|
|
- fp->sk = NULL;
|
|
- head->data_len += fp->len;
|
|
- head->len += fp->len;
|
|
- if (head->ip_summed != fp->ip_summed)
|
|
- head->ip_summed = CHECKSUM_NONE;
|
|
- else if (head->ip_summed == CHECKSUM_COMPLETE)
|
|
- head->csum = csum_add(head->csum, fp->csum);
|
|
- head->truesize += fp->truesize;
|
|
- fp = FRAG_CB(fp)->next_frag;
|
|
- }
|
|
- /* Move to the next run. */
|
|
- if (rbn) {
|
|
- struct rb_node *rbnext = rb_next(rbn);
|
|
-
|
|
- fp = rb_to_skb(rbn);
|
|
- rb_erase(rbn, &qp->q.rb_fragments);
|
|
- rbn = rbnext;
|
|
- }
|
|
- }
|
|
- sub_frag_mem_limit(qp->q.net, head->truesize);
|
|
-
|
|
- *nextp = NULL;
|
|
- head->next = NULL;
|
|
- head->prev = NULL;
|
|
- head->dev = dev;
|
|
- head->tstamp = qp->q.stamp;
|
|
- IPCB(head)->frag_max_size = max(qp->max_df_size, qp->q.max_size);
|
|
-
|
|
- iph = ip_hdr(head);
|
|
+ iph = ip_hdr(skb);
|
|
iph->tot_len = htons(len);
|
|
iph->tos |= ecn;
|
|
|
|
@@ -653,7 +443,7 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *skb,
|
|
* from one very small df-fragment and one large non-df frag.
|
|
*/
|
|
if (qp->max_df_size == qp->q.max_size) {
|
|
- IPCB(head)->flags |= IPSKB_FRAG_PMTU;
|
|
+ IPCB(skb)->flags |= IPSKB_FRAG_PMTU;
|
|
iph->frag_off = htons(IP_DF);
|
|
} else {
|
|
iph->frag_off = 0;
|
|
@@ -751,28 +541,6 @@ struct sk_buff *ip_check_defrag(struct net *net, struct sk_buff *skb, u32 user)
|
|
}
|
|
EXPORT_SYMBOL(ip_check_defrag);
|
|
|
|
-unsigned int inet_frag_rbtree_purge(struct rb_root *root)
|
|
-{
|
|
- struct rb_node *p = rb_first(root);
|
|
- unsigned int sum = 0;
|
|
-
|
|
- while (p) {
|
|
- struct sk_buff *skb = rb_entry(p, struct sk_buff, rbnode);
|
|
-
|
|
- p = rb_next(p);
|
|
- rb_erase(&skb->rbnode, root);
|
|
- while (skb) {
|
|
- struct sk_buff *next = FRAG_CB(skb)->next_frag;
|
|
-
|
|
- sum += skb->truesize;
|
|
- kfree_skb(skb);
|
|
- skb = next;
|
|
- }
|
|
- }
|
|
- return sum;
|
|
-}
|
|
-EXPORT_SYMBOL(inet_frag_rbtree_purge);
|
|
-
|
|
#ifdef CONFIG_SYSCTL
|
|
static int dist_min;
|
|
|
|
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
|
|
index a1bf87711bfa..c64f062d6323 100644
|
|
--- a/net/ipv4/route.c
|
|
+++ b/net/ipv4/route.c
|
|
@@ -1194,9 +1194,23 @@ static struct dst_entry *ipv4_dst_check(struct dst_entry *dst, u32 cookie)
|
|
|
|
static void ipv4_link_failure(struct sk_buff *skb)
|
|
{
|
|
+ struct ip_options opt;
|
|
struct rtable *rt;
|
|
+ int res;
|
|
|
|
- icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0);
|
|
+ /* Recompile ip options since IPCB may not be valid anymore.
|
|
+ */
|
|
+ memset(&opt, 0, sizeof(opt));
|
|
+ opt.optlen = ip_hdr(skb)->ihl*4 - sizeof(struct iphdr);
|
|
+
|
|
+ rcu_read_lock();
|
|
+ res = __ip_options_compile(dev_net(skb->dev), &opt, skb, NULL);
|
|
+ rcu_read_unlock();
|
|
+
|
|
+ if (res)
|
|
+ return;
|
|
+
|
|
+ __icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0, &opt);
|
|
|
|
rt = skb_rtable(skb);
|
|
if (rt)
|
|
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
|
|
index c8227e07d574..657d33e2ff6a 100644
|
|
--- a/net/ipv4/tcp_input.c
|
|
+++ b/net/ipv4/tcp_input.c
|
|
@@ -389,11 +389,12 @@ static int __tcp_grow_window(const struct sock *sk, const struct sk_buff *skb)
|
|
static void tcp_grow_window(struct sock *sk, const struct sk_buff *skb)
|
|
{
|
|
struct tcp_sock *tp = tcp_sk(sk);
|
|
+ int room;
|
|
+
|
|
+ room = min_t(int, tp->window_clamp, tcp_space(sk)) - tp->rcv_ssthresh;
|
|
|
|
/* Check #1 */
|
|
- if (tp->rcv_ssthresh < tp->window_clamp &&
|
|
- (int)tp->rcv_ssthresh < tcp_space(sk) &&
|
|
- !tcp_under_memory_pressure(sk)) {
|
|
+ if (room > 0 && !tcp_under_memory_pressure(sk)) {
|
|
int incr;
|
|
|
|
/* Check #2. Increase window, if skb with such overhead
|
|
@@ -406,8 +407,7 @@ static void tcp_grow_window(struct sock *sk, const struct sk_buff *skb)
|
|
|
|
if (incr) {
|
|
incr = max_t(int, incr, 2 * skb->len);
|
|
- tp->rcv_ssthresh = min(tp->rcv_ssthresh + incr,
|
|
- tp->window_clamp);
|
|
+ tp->rcv_ssthresh += min(room, incr);
|
|
inet_csk(sk)->icsk_ack.quick |= 1;
|
|
}
|
|
}
|
|
diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c
|
|
index 237fb04c6716..cb1b4772dac0 100644
|
|
--- a/net/ipv6/netfilter/nf_conntrack_reasm.c
|
|
+++ b/net/ipv6/netfilter/nf_conntrack_reasm.c
|
|
@@ -33,9 +33,8 @@
|
|
|
|
#include <net/sock.h>
|
|
#include <net/snmp.h>
|
|
-#include <net/inet_frag.h>
|
|
+#include <net/ipv6_frag.h>
|
|
|
|
-#include <net/ipv6.h>
|
|
#include <net/protocol.h>
|
|
#include <net/transp_v6.h>
|
|
#include <net/rawv6.h>
|
|
@@ -52,14 +51,6 @@
|
|
|
|
static const char nf_frags_cache_name[] = "nf-frags";
|
|
|
|
-struct nf_ct_frag6_skb_cb
|
|
-{
|
|
- struct inet6_skb_parm h;
|
|
- int offset;
|
|
-};
|
|
-
|
|
-#define NFCT_FRAG6_CB(skb) ((struct nf_ct_frag6_skb_cb *)((skb)->cb))
|
|
-
|
|
static struct inet_frags nf_frags;
|
|
|
|
#ifdef CONFIG_SYSCTL
|
|
@@ -145,6 +136,9 @@ static void __net_exit nf_ct_frags6_sysctl_unregister(struct net *net)
|
|
}
|
|
#endif
|
|
|
|
+static int nf_ct_frag6_reasm(struct frag_queue *fq, struct sk_buff *skb,
|
|
+ struct sk_buff *prev_tail, struct net_device *dev);
|
|
+
|
|
static inline u8 ip6_frag_ecn(const struct ipv6hdr *ipv6h)
|
|
{
|
|
return 1 << (ipv6_get_dsfield(ipv6h) & INET_ECN_MASK);
|
|
@@ -159,7 +153,7 @@ static void nf_ct_frag6_expire(struct timer_list *t)
|
|
fq = container_of(frag, struct frag_queue, q);
|
|
net = container_of(fq->q.net, struct net, nf_frag.frags);
|
|
|
|
- ip6_expire_frag_queue(net, fq);
|
|
+ ip6frag_expire_frag_queue(net, fq);
|
|
}
|
|
|
|
/* Creation primitives. */
|
|
@@ -186,9 +180,10 @@ static struct frag_queue *fq_find(struct net *net, __be32 id, u32 user,
|
|
static int nf_ct_frag6_queue(struct frag_queue *fq, struct sk_buff *skb,
|
|
const struct frag_hdr *fhdr, int nhoff)
|
|
{
|
|
- struct sk_buff *prev, *next;
|
|
unsigned int payload_len;
|
|
- int offset, end;
|
|
+ struct net_device *dev;
|
|
+ struct sk_buff *prev;
|
|
+ int offset, end, err;
|
|
u8 ecn;
|
|
|
|
if (fq->q.flags & INET_FRAG_COMPLETE) {
|
|
@@ -263,55 +258,19 @@ static int nf_ct_frag6_queue(struct frag_queue *fq, struct sk_buff *skb,
|
|
goto err;
|
|
}
|
|
|
|
- /* Find out which fragments are in front and at the back of us
|
|
- * in the chain of fragments so far. We must know where to put
|
|
- * this fragment, right?
|
|
- */
|
|
+ /* Note : skb->rbnode and skb->dev share the same location. */
|
|
+ dev = skb->dev;
|
|
+ /* Makes sure compiler wont do silly aliasing games */
|
|
+ barrier();
|
|
+
|
|
prev = fq->q.fragments_tail;
|
|
- if (!prev || NFCT_FRAG6_CB(prev)->offset < offset) {
|
|
- next = NULL;
|
|
- goto found;
|
|
- }
|
|
- prev = NULL;
|
|
- for (next = fq->q.fragments; next != NULL; next = next->next) {
|
|
- if (NFCT_FRAG6_CB(next)->offset >= offset)
|
|
- break; /* bingo! */
|
|
- prev = next;
|
|
- }
|
|
+ err = inet_frag_queue_insert(&fq->q, skb, offset, end);
|
|
+ if (err)
|
|
+ goto insert_error;
|
|
|
|
-found:
|
|
- /* RFC5722, Section 4:
|
|
- * When reassembling an IPv6 datagram, if
|
|
- * one or more its constituent fragments is determined to be an
|
|
- * overlapping fragment, the entire datagram (and any constituent
|
|
- * fragments, including those not yet received) MUST be silently
|
|
- * discarded.
|
|
- */
|
|
+ if (dev)
|
|
+ fq->iif = dev->ifindex;
|
|
|
|
- /* Check for overlap with preceding fragment. */
|
|
- if (prev &&
|
|
- (NFCT_FRAG6_CB(prev)->offset + prev->len) > offset)
|
|
- goto discard_fq;
|
|
-
|
|
- /* Look for overlap with succeeding segment. */
|
|
- if (next && NFCT_FRAG6_CB(next)->offset < end)
|
|
- goto discard_fq;
|
|
-
|
|
- NFCT_FRAG6_CB(skb)->offset = offset;
|
|
-
|
|
- /* Insert this fragment in the chain of fragments. */
|
|
- skb->next = next;
|
|
- if (!next)
|
|
- fq->q.fragments_tail = skb;
|
|
- if (prev)
|
|
- prev->next = skb;
|
|
- else
|
|
- fq->q.fragments = skb;
|
|
-
|
|
- if (skb->dev) {
|
|
- fq->iif = skb->dev->ifindex;
|
|
- skb->dev = NULL;
|
|
- }
|
|
fq->q.stamp = skb->tstamp;
|
|
fq->q.meat += skb->len;
|
|
fq->ecn |= ecn;
|
|
@@ -327,11 +286,25 @@ found:
|
|
fq->q.flags |= INET_FRAG_FIRST_IN;
|
|
}
|
|
|
|
- return 0;
|
|
+ if (fq->q.flags == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) &&
|
|
+ fq->q.meat == fq->q.len) {
|
|
+ unsigned long orefdst = skb->_skb_refdst;
|
|
|
|
-discard_fq:
|
|
+ skb->_skb_refdst = 0UL;
|
|
+ err = nf_ct_frag6_reasm(fq, skb, prev, dev);
|
|
+ skb->_skb_refdst = orefdst;
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ skb_dst_drop(skb);
|
|
+ return -EINPROGRESS;
|
|
+
|
|
+insert_error:
|
|
+ if (err == IPFRAG_DUP)
|
|
+ goto err;
|
|
inet_frag_kill(&fq->q);
|
|
err:
|
|
+ skb_dst_drop(skb);
|
|
return -EINVAL;
|
|
}
|
|
|
|
@@ -341,147 +314,67 @@ err:
|
|
* It is called with locked fq, and caller must check that
|
|
* queue is eligible for reassembly i.e. it is not COMPLETE,
|
|
* the last and the first frames arrived and all the bits are here.
|
|
- *
|
|
- * returns true if *prev skb has been transformed into the reassembled
|
|
- * skb, false otherwise.
|
|
*/
|
|
-static bool
|
|
-nf_ct_frag6_reasm(struct frag_queue *fq, struct sk_buff *prev, struct net_device *dev)
|
|
+static int nf_ct_frag6_reasm(struct frag_queue *fq, struct sk_buff *skb,
|
|
+ struct sk_buff *prev_tail, struct net_device *dev)
|
|
{
|
|
- struct sk_buff *fp, *head = fq->q.fragments;
|
|
- int payload_len, delta;
|
|
+ void *reasm_data;
|
|
+ int payload_len;
|
|
u8 ecn;
|
|
|
|
inet_frag_kill(&fq->q);
|
|
|
|
- WARN_ON(head == NULL);
|
|
- WARN_ON(NFCT_FRAG6_CB(head)->offset != 0);
|
|
-
|
|
ecn = ip_frag_ecn_table[fq->ecn];
|
|
if (unlikely(ecn == 0xff))
|
|
- return false;
|
|
+ goto err;
|
|
|
|
- /* Unfragmented part is taken from the first segment. */
|
|
- payload_len = ((head->data - skb_network_header(head)) -
|
|
+ reasm_data = inet_frag_reasm_prepare(&fq->q, skb, prev_tail);
|
|
+ if (!reasm_data)
|
|
+ goto err;
|
|
+
|
|
+ payload_len = ((skb->data - skb_network_header(skb)) -
|
|
sizeof(struct ipv6hdr) + fq->q.len -
|
|
sizeof(struct frag_hdr));
|
|
if (payload_len > IPV6_MAXPLEN) {
|
|
net_dbg_ratelimited("nf_ct_frag6_reasm: payload len = %d\n",
|
|
payload_len);
|
|
- return false;
|
|
- }
|
|
-
|
|
- delta = - head->truesize;
|
|
-
|
|
- /* Head of list must not be cloned. */
|
|
- if (skb_unclone(head, GFP_ATOMIC))
|
|
- return false;
|
|
-
|
|
- delta += head->truesize;
|
|
- if (delta)
|
|
- add_frag_mem_limit(fq->q.net, delta);
|
|
-
|
|
- /* If the first fragment is fragmented itself, we split
|
|
- * it to two chunks: the first with data and paged part
|
|
- * and the second, holding only fragments. */
|
|
- if (skb_has_frag_list(head)) {
|
|
- struct sk_buff *clone;
|
|
- int i, plen = 0;
|
|
-
|
|
- clone = alloc_skb(0, GFP_ATOMIC);
|
|
- if (clone == NULL)
|
|
- return false;
|
|
-
|
|
- clone->next = head->next;
|
|
- head->next = clone;
|
|
- skb_shinfo(clone)->frag_list = skb_shinfo(head)->frag_list;
|
|
- skb_frag_list_init(head);
|
|
- for (i = 0; i < skb_shinfo(head)->nr_frags; i++)
|
|
- plen += skb_frag_size(&skb_shinfo(head)->frags[i]);
|
|
- clone->len = clone->data_len = head->data_len - plen;
|
|
- head->data_len -= clone->len;
|
|
- head->len -= clone->len;
|
|
- clone->csum = 0;
|
|
- clone->ip_summed = head->ip_summed;
|
|
-
|
|
- add_frag_mem_limit(fq->q.net, clone->truesize);
|
|
- }
|
|
-
|
|
- /* morph head into last received skb: prev.
|
|
- *
|
|
- * This allows callers of ipv6 conntrack defrag to continue
|
|
- * to use the last skb(frag) passed into the reasm engine.
|
|
- * The last skb frag 'silently' turns into the full reassembled skb.
|
|
- *
|
|
- * Since prev is also part of q->fragments we have to clone it first.
|
|
- */
|
|
- if (head != prev) {
|
|
- struct sk_buff *iter;
|
|
-
|
|
- fp = skb_clone(prev, GFP_ATOMIC);
|
|
- if (!fp)
|
|
- return false;
|
|
-
|
|
- fp->next = prev->next;
|
|
-
|
|
- iter = head;
|
|
- while (iter) {
|
|
- if (iter->next == prev) {
|
|
- iter->next = fp;
|
|
- break;
|
|
- }
|
|
- iter = iter->next;
|
|
- }
|
|
-
|
|
- skb_morph(prev, head);
|
|
- prev->next = head->next;
|
|
- consume_skb(head);
|
|
- head = prev;
|
|
+ goto err;
|
|
}
|
|
|
|
/* We have to remove fragment header from datagram and to relocate
|
|
* header in order to calculate ICV correctly. */
|
|
- skb_network_header(head)[fq->nhoffset] = skb_transport_header(head)[0];
|
|
- memmove(head->head + sizeof(struct frag_hdr), head->head,
|
|
- (head->data - head->head) - sizeof(struct frag_hdr));
|
|
- head->mac_header += sizeof(struct frag_hdr);
|
|
- head->network_header += sizeof(struct frag_hdr);
|
|
-
|
|
- skb_shinfo(head)->frag_list = head->next;
|
|
- skb_reset_transport_header(head);
|
|
- skb_push(head, head->data - skb_network_header(head));
|
|
-
|
|
- for (fp = head->next; fp; fp = fp->next) {
|
|
- head->data_len += fp->len;
|
|
- head->len += fp->len;
|
|
- if (head->ip_summed != fp->ip_summed)
|
|
- head->ip_summed = CHECKSUM_NONE;
|
|
- else if (head->ip_summed == CHECKSUM_COMPLETE)
|
|
- head->csum = csum_add(head->csum, fp->csum);
|
|
- head->truesize += fp->truesize;
|
|
- fp->sk = NULL;
|
|
- }
|
|
- sub_frag_mem_limit(fq->q.net, head->truesize);
|
|
+ skb_network_header(skb)[fq->nhoffset] = skb_transport_header(skb)[0];
|
|
+ memmove(skb->head + sizeof(struct frag_hdr), skb->head,
|
|
+ (skb->data - skb->head) - sizeof(struct frag_hdr));
|
|
+ skb->mac_header += sizeof(struct frag_hdr);
|
|
+ skb->network_header += sizeof(struct frag_hdr);
|
|
+
|
|
+ skb_reset_transport_header(skb);
|
|
|
|
- head->ignore_df = 1;
|
|
- head->next = NULL;
|
|
- head->dev = dev;
|
|
- head->tstamp = fq->q.stamp;
|
|
- ipv6_hdr(head)->payload_len = htons(payload_len);
|
|
- ipv6_change_dsfield(ipv6_hdr(head), 0xff, ecn);
|
|
- IP6CB(head)->frag_max_size = sizeof(struct ipv6hdr) + fq->q.max_size;
|
|
+ inet_frag_reasm_finish(&fq->q, skb, reasm_data);
|
|
+
|
|
+ skb->ignore_df = 1;
|
|
+ skb->dev = dev;
|
|
+ ipv6_hdr(skb)->payload_len = htons(payload_len);
|
|
+ ipv6_change_dsfield(ipv6_hdr(skb), 0xff, ecn);
|
|
+ IP6CB(skb)->frag_max_size = sizeof(struct ipv6hdr) + fq->q.max_size;
|
|
|
|
/* Yes, and fold redundant checksum back. 8) */
|
|
- if (head->ip_summed == CHECKSUM_COMPLETE)
|
|
- head->csum = csum_partial(skb_network_header(head),
|
|
- skb_network_header_len(head),
|
|
- head->csum);
|
|
+ if (skb->ip_summed == CHECKSUM_COMPLETE)
|
|
+ skb->csum = csum_partial(skb_network_header(skb),
|
|
+ skb_network_header_len(skb),
|
|
+ skb->csum);
|
|
|
|
fq->q.fragments = NULL;
|
|
fq->q.rb_fragments = RB_ROOT;
|
|
fq->q.fragments_tail = NULL;
|
|
+ fq->q.last_run_head = NULL;
|
|
+
|
|
+ return 0;
|
|
|
|
- return true;
|
|
+err:
|
|
+ inet_frag_kill(&fq->q);
|
|
+ return -EINVAL;
|
|
}
|
|
|
|
/*
|
|
@@ -550,7 +443,6 @@ find_prev_fhdr(struct sk_buff *skb, u8 *prevhdrp, int *prevhoff, int *fhoff)
|
|
int nf_ct_frag6_gather(struct net *net, struct sk_buff *skb, u32 user)
|
|
{
|
|
u16 savethdr = skb->transport_header;
|
|
- struct net_device *dev = skb->dev;
|
|
int fhoff, nhoff, ret;
|
|
struct frag_hdr *fhdr;
|
|
struct frag_queue *fq;
|
|
@@ -573,10 +465,6 @@ int nf_ct_frag6_gather(struct net *net, struct sk_buff *skb, u32 user)
|
|
hdr = ipv6_hdr(skb);
|
|
fhdr = (struct frag_hdr *)skb_transport_header(skb);
|
|
|
|
- if (skb->len - skb_network_offset(skb) < IPV6_MIN_MTU &&
|
|
- fhdr->frag_off & htons(IP6_MF))
|
|
- return -EINVAL;
|
|
-
|
|
skb_orphan(skb);
|
|
fq = fq_find(net, fhdr->identification, user, hdr,
|
|
skb->dev ? skb->dev->ifindex : 0);
|
|
@@ -588,24 +476,17 @@ int nf_ct_frag6_gather(struct net *net, struct sk_buff *skb, u32 user)
|
|
spin_lock_bh(&fq->q.lock);
|
|
|
|
ret = nf_ct_frag6_queue(fq, skb, fhdr, nhoff);
|
|
- if (ret < 0) {
|
|
- if (ret == -EPROTO) {
|
|
- skb->transport_header = savethdr;
|
|
- ret = 0;
|
|
- }
|
|
- goto out_unlock;
|
|
+ if (ret == -EPROTO) {
|
|
+ skb->transport_header = savethdr;
|
|
+ ret = 0;
|
|
}
|
|
|
|
/* after queue has assumed skb ownership, only 0 or -EINPROGRESS
|
|
* must be returned.
|
|
*/
|
|
- ret = -EINPROGRESS;
|
|
- if (fq->q.flags == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) &&
|
|
- fq->q.meat == fq->q.len &&
|
|
- nf_ct_frag6_reasm(fq, skb, dev))
|
|
- ret = 0;
|
|
+ if (ret)
|
|
+ ret = -EINPROGRESS;
|
|
|
|
-out_unlock:
|
|
spin_unlock_bh(&fq->q.lock);
|
|
inet_frag_put(&fq->q);
|
|
return ret;
|
|
@@ -641,16 +522,24 @@ static struct pernet_operations nf_ct_net_ops = {
|
|
.exit = nf_ct_net_exit,
|
|
};
|
|
|
|
+static const struct rhashtable_params nfct_rhash_params = {
|
|
+ .head_offset = offsetof(struct inet_frag_queue, node),
|
|
+ .hashfn = ip6frag_key_hashfn,
|
|
+ .obj_hashfn = ip6frag_obj_hashfn,
|
|
+ .obj_cmpfn = ip6frag_obj_cmpfn,
|
|
+ .automatic_shrinking = true,
|
|
+};
|
|
+
|
|
int nf_ct_frag6_init(void)
|
|
{
|
|
int ret = 0;
|
|
|
|
- nf_frags.constructor = ip6_frag_init;
|
|
+ nf_frags.constructor = ip6frag_init;
|
|
nf_frags.destructor = NULL;
|
|
nf_frags.qsize = sizeof(struct frag_queue);
|
|
nf_frags.frag_expire = nf_ct_frag6_expire;
|
|
nf_frags.frags_cache_name = nf_frags_cache_name;
|
|
- nf_frags.rhash_params = ip6_rhash_params;
|
|
+ nf_frags.rhash_params = nfct_rhash_params;
|
|
ret = inet_frags_init(&nf_frags);
|
|
if (ret)
|
|
goto out;
|
|
diff --git a/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c b/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c
|
|
index b326da59257f..123bfb13a5d1 100644
|
|
--- a/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c
|
|
+++ b/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c
|
|
@@ -14,8 +14,7 @@
|
|
#include <linux/skbuff.h>
|
|
#include <linux/icmp.h>
|
|
#include <linux/sysctl.h>
|
|
-#include <net/ipv6.h>
|
|
-#include <net/inet_frag.h>
|
|
+#include <net/ipv6_frag.h>
|
|
|
|
#include <linux/netfilter_ipv6.h>
|
|
#include <linux/netfilter_bridge.h>
|
|
diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c
|
|
index 2a8c680b67cd..fe797b29ca89 100644
|
|
--- a/net/ipv6/reassembly.c
|
|
+++ b/net/ipv6/reassembly.c
|
|
@@ -57,18 +57,11 @@
|
|
#include <net/rawv6.h>
|
|
#include <net/ndisc.h>
|
|
#include <net/addrconf.h>
|
|
-#include <net/inet_frag.h>
|
|
+#include <net/ipv6_frag.h>
|
|
#include <net/inet_ecn.h>
|
|
|
|
static const char ip6_frag_cache_name[] = "ip6-frags";
|
|
|
|
-struct ip6frag_skb_cb {
|
|
- struct inet6_skb_parm h;
|
|
- int offset;
|
|
-};
|
|
-
|
|
-#define FRAG6_CB(skb) ((struct ip6frag_skb_cb *)((skb)->cb))
|
|
-
|
|
static u8 ip6_frag_ecn(const struct ipv6hdr *ipv6h)
|
|
{
|
|
return 1 << (ipv6_get_dsfield(ipv6h) & INET_ECN_MASK);
|
|
@@ -76,63 +69,8 @@ static u8 ip6_frag_ecn(const struct ipv6hdr *ipv6h)
|
|
|
|
static struct inet_frags ip6_frags;
|
|
|
|
-static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *prev,
|
|
- struct net_device *dev);
|
|
-
|
|
-void ip6_frag_init(struct inet_frag_queue *q, const void *a)
|
|
-{
|
|
- struct frag_queue *fq = container_of(q, struct frag_queue, q);
|
|
- const struct frag_v6_compare_key *key = a;
|
|
-
|
|
- q->key.v6 = *key;
|
|
- fq->ecn = 0;
|
|
-}
|
|
-EXPORT_SYMBOL(ip6_frag_init);
|
|
-
|
|
-void ip6_expire_frag_queue(struct net *net, struct frag_queue *fq)
|
|
-{
|
|
- struct net_device *dev = NULL;
|
|
- struct sk_buff *head;
|
|
-
|
|
- rcu_read_lock();
|
|
- spin_lock(&fq->q.lock);
|
|
-
|
|
- if (fq->q.flags & INET_FRAG_COMPLETE)
|
|
- goto out;
|
|
-
|
|
- inet_frag_kill(&fq->q);
|
|
-
|
|
- dev = dev_get_by_index_rcu(net, fq->iif);
|
|
- if (!dev)
|
|
- goto out;
|
|
-
|
|
- __IP6_INC_STATS(net, __in6_dev_get(dev), IPSTATS_MIB_REASMFAILS);
|
|
- __IP6_INC_STATS(net, __in6_dev_get(dev), IPSTATS_MIB_REASMTIMEOUT);
|
|
-
|
|
- /* Don't send error if the first segment did not arrive. */
|
|
- head = fq->q.fragments;
|
|
- if (!(fq->q.flags & INET_FRAG_FIRST_IN) || !head)
|
|
- goto out;
|
|
-
|
|
- /* But use as source device on which LAST ARRIVED
|
|
- * segment was received. And do not use fq->dev
|
|
- * pointer directly, device might already disappeared.
|
|
- */
|
|
- head->dev = dev;
|
|
- skb_get(head);
|
|
- spin_unlock(&fq->q.lock);
|
|
-
|
|
- icmpv6_send(head, ICMPV6_TIME_EXCEED, ICMPV6_EXC_FRAGTIME, 0);
|
|
- kfree_skb(head);
|
|
- goto out_rcu_unlock;
|
|
-
|
|
-out:
|
|
- spin_unlock(&fq->q.lock);
|
|
-out_rcu_unlock:
|
|
- rcu_read_unlock();
|
|
- inet_frag_put(&fq->q);
|
|
-}
|
|
-EXPORT_SYMBOL(ip6_expire_frag_queue);
|
|
+static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *skb,
|
|
+ struct sk_buff *prev_tail, struct net_device *dev);
|
|
|
|
static void ip6_frag_expire(struct timer_list *t)
|
|
{
|
|
@@ -143,7 +81,7 @@ static void ip6_frag_expire(struct timer_list *t)
|
|
fq = container_of(frag, struct frag_queue, q);
|
|
net = container_of(fq->q.net, struct net, ipv6.frags);
|
|
|
|
- ip6_expire_frag_queue(net, fq);
|
|
+ ip6frag_expire_frag_queue(net, fq);
|
|
}
|
|
|
|
static struct frag_queue *
|
|
@@ -170,27 +108,29 @@ fq_find(struct net *net, __be32 id, const struct ipv6hdr *hdr, int iif)
|
|
}
|
|
|
|
static int ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb,
|
|
- struct frag_hdr *fhdr, int nhoff)
|
|
+ struct frag_hdr *fhdr, int nhoff,
|
|
+ u32 *prob_offset)
|
|
{
|
|
- struct sk_buff *prev, *next;
|
|
- struct net_device *dev;
|
|
- int offset, end, fragsize;
|
|
struct net *net = dev_net(skb_dst(skb)->dev);
|
|
+ int offset, end, fragsize;
|
|
+ struct sk_buff *prev_tail;
|
|
+ struct net_device *dev;
|
|
+ int err = -ENOENT;
|
|
u8 ecn;
|
|
|
|
if (fq->q.flags & INET_FRAG_COMPLETE)
|
|
goto err;
|
|
|
|
+ err = -EINVAL;
|
|
offset = ntohs(fhdr->frag_off) & ~0x7;
|
|
end = offset + (ntohs(ipv6_hdr(skb)->payload_len) -
|
|
((u8 *)(fhdr + 1) - (u8 *)(ipv6_hdr(skb) + 1)));
|
|
|
|
if ((unsigned int)end > IPV6_MAXPLEN) {
|
|
- __IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
|
|
- IPSTATS_MIB_INHDRERRORS);
|
|
- icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
|
|
- ((u8 *)&fhdr->frag_off -
|
|
- skb_network_header(skb)));
|
|
+ *prob_offset = (u8 *)&fhdr->frag_off - skb_network_header(skb);
|
|
+ /* note that if prob_offset is set, the skb is freed elsewhere,
|
|
+ * we do not free it here.
|
|
+ */
|
|
return -1;
|
|
}
|
|
|
|
@@ -210,7 +150,7 @@ static int ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb,
|
|
*/
|
|
if (end < fq->q.len ||
|
|
((fq->q.flags & INET_FRAG_LAST_IN) && end != fq->q.len))
|
|
- goto err;
|
|
+ goto discard_fq;
|
|
fq->q.flags |= INET_FRAG_LAST_IN;
|
|
fq->q.len = end;
|
|
} else {
|
|
@@ -221,79 +161,42 @@ static int ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb,
|
|
/* RFC2460 says always send parameter problem in
|
|
* this case. -DaveM
|
|
*/
|
|
- __IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
|
|
- IPSTATS_MIB_INHDRERRORS);
|
|
- icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
|
|
- offsetof(struct ipv6hdr, payload_len));
|
|
+ *prob_offset = offsetof(struct ipv6hdr, payload_len);
|
|
return -1;
|
|
}
|
|
if (end > fq->q.len) {
|
|
/* Some bits beyond end -> corruption. */
|
|
if (fq->q.flags & INET_FRAG_LAST_IN)
|
|
- goto err;
|
|
+ goto discard_fq;
|
|
fq->q.len = end;
|
|
}
|
|
}
|
|
|
|
if (end == offset)
|
|
- goto err;
|
|
+ goto discard_fq;
|
|
|
|
+ err = -ENOMEM;
|
|
/* Point into the IP datagram 'data' part. */
|
|
if (!pskb_pull(skb, (u8 *) (fhdr + 1) - skb->data))
|
|
- goto err;
|
|
-
|
|
- if (pskb_trim_rcsum(skb, end - offset))
|
|
- goto err;
|
|
-
|
|
- /* Find out which fragments are in front and at the back of us
|
|
- * in the chain of fragments so far. We must know where to put
|
|
- * this fragment, right?
|
|
- */
|
|
- prev = fq->q.fragments_tail;
|
|
- if (!prev || FRAG6_CB(prev)->offset < offset) {
|
|
- next = NULL;
|
|
- goto found;
|
|
- }
|
|
- prev = NULL;
|
|
- for (next = fq->q.fragments; next != NULL; next = next->next) {
|
|
- if (FRAG6_CB(next)->offset >= offset)
|
|
- break; /* bingo! */
|
|
- prev = next;
|
|
- }
|
|
-
|
|
-found:
|
|
- /* RFC5722, Section 4, amended by Errata ID : 3089
|
|
- * When reassembling an IPv6 datagram, if
|
|
- * one or more its constituent fragments is determined to be an
|
|
- * overlapping fragment, the entire datagram (and any constituent
|
|
- * fragments) MUST be silently discarded.
|
|
- */
|
|
-
|
|
- /* Check for overlap with preceding fragment. */
|
|
- if (prev &&
|
|
- (FRAG6_CB(prev)->offset + prev->len) > offset)
|
|
goto discard_fq;
|
|
|
|
- /* Look for overlap with succeeding segment. */
|
|
- if (next && FRAG6_CB(next)->offset < end)
|
|
+ err = pskb_trim_rcsum(skb, end - offset);
|
|
+ if (err)
|
|
goto discard_fq;
|
|
|
|
- FRAG6_CB(skb)->offset = offset;
|
|
+ /* Note : skb->rbnode and skb->dev share the same location. */
|
|
+ dev = skb->dev;
|
|
+ /* Makes sure compiler wont do silly aliasing games */
|
|
+ barrier();
|
|
|
|
- /* Insert this fragment in the chain of fragments. */
|
|
- skb->next = next;
|
|
- if (!next)
|
|
- fq->q.fragments_tail = skb;
|
|
- if (prev)
|
|
- prev->next = skb;
|
|
- else
|
|
- fq->q.fragments = skb;
|
|
+ prev_tail = fq->q.fragments_tail;
|
|
+ err = inet_frag_queue_insert(&fq->q, skb, offset, end);
|
|
+ if (err)
|
|
+ goto insert_error;
|
|
|
|
- dev = skb->dev;
|
|
- if (dev) {
|
|
+ if (dev)
|
|
fq->iif = dev->ifindex;
|
|
- skb->dev = NULL;
|
|
- }
|
|
+
|
|
fq->q.stamp = skb->tstamp;
|
|
fq->q.meat += skb->len;
|
|
fq->ecn |= ecn;
|
|
@@ -313,44 +216,48 @@ found:
|
|
|
|
if (fq->q.flags == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) &&
|
|
fq->q.meat == fq->q.len) {
|
|
- int res;
|
|
unsigned long orefdst = skb->_skb_refdst;
|
|
|
|
skb->_skb_refdst = 0UL;
|
|
- res = ip6_frag_reasm(fq, prev, dev);
|
|
+ err = ip6_frag_reasm(fq, skb, prev_tail, dev);
|
|
skb->_skb_refdst = orefdst;
|
|
- return res;
|
|
+ return err;
|
|
}
|
|
|
|
skb_dst_drop(skb);
|
|
- return -1;
|
|
+ return -EINPROGRESS;
|
|
|
|
+insert_error:
|
|
+ if (err == IPFRAG_DUP) {
|
|
+ kfree_skb(skb);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ err = -EINVAL;
|
|
+ __IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
|
|
+ IPSTATS_MIB_REASM_OVERLAPS);
|
|
discard_fq:
|
|
inet_frag_kill(&fq->q);
|
|
-err:
|
|
__IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
|
|
IPSTATS_MIB_REASMFAILS);
|
|
+err:
|
|
kfree_skb(skb);
|
|
- return -1;
|
|
+ return err;
|
|
}
|
|
|
|
/*
|
|
* Check if this packet is complete.
|
|
- * Returns NULL on failure by any reason, and pointer
|
|
- * to current nexthdr field in reassembled frame.
|
|
*
|
|
* It is called with locked fq, and caller must check that
|
|
* queue is eligible for reassembly i.e. it is not COMPLETE,
|
|
* the last and the first frames arrived and all the bits are here.
|
|
*/
|
|
-static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *prev,
|
|
- struct net_device *dev)
|
|
+static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *skb,
|
|
+ struct sk_buff *prev_tail, struct net_device *dev)
|
|
{
|
|
struct net *net = container_of(fq->q.net, struct net, ipv6.frags);
|
|
- struct sk_buff *fp, *head = fq->q.fragments;
|
|
- int payload_len, delta;
|
|
unsigned int nhoff;
|
|
- int sum_truesize;
|
|
+ void *reasm_data;
|
|
+ int payload_len;
|
|
u8 ecn;
|
|
|
|
inet_frag_kill(&fq->q);
|
|
@@ -359,120 +266,40 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *prev,
|
|
if (unlikely(ecn == 0xff))
|
|
goto out_fail;
|
|
|
|
- /* Make the one we just received the head. */
|
|
- if (prev) {
|
|
- head = prev->next;
|
|
- fp = skb_clone(head, GFP_ATOMIC);
|
|
-
|
|
- if (!fp)
|
|
- goto out_oom;
|
|
-
|
|
- fp->next = head->next;
|
|
- if (!fp->next)
|
|
- fq->q.fragments_tail = fp;
|
|
- prev->next = fp;
|
|
-
|
|
- skb_morph(head, fq->q.fragments);
|
|
- head->next = fq->q.fragments->next;
|
|
-
|
|
- consume_skb(fq->q.fragments);
|
|
- fq->q.fragments = head;
|
|
- }
|
|
-
|
|
- WARN_ON(head == NULL);
|
|
- WARN_ON(FRAG6_CB(head)->offset != 0);
|
|
+ reasm_data = inet_frag_reasm_prepare(&fq->q, skb, prev_tail);
|
|
+ if (!reasm_data)
|
|
+ goto out_oom;
|
|
|
|
- /* Unfragmented part is taken from the first segment. */
|
|
- payload_len = ((head->data - skb_network_header(head)) -
|
|
+ payload_len = ((skb->data - skb_network_header(skb)) -
|
|
sizeof(struct ipv6hdr) + fq->q.len -
|
|
sizeof(struct frag_hdr));
|
|
if (payload_len > IPV6_MAXPLEN)
|
|
goto out_oversize;
|
|
|
|
- delta = - head->truesize;
|
|
-
|
|
- /* Head of list must not be cloned. */
|
|
- if (skb_unclone(head, GFP_ATOMIC))
|
|
- goto out_oom;
|
|
-
|
|
- delta += head->truesize;
|
|
- if (delta)
|
|
- add_frag_mem_limit(fq->q.net, delta);
|
|
-
|
|
- /* If the first fragment is fragmented itself, we split
|
|
- * it to two chunks: the first with data and paged part
|
|
- * and the second, holding only fragments. */
|
|
- if (skb_has_frag_list(head)) {
|
|
- struct sk_buff *clone;
|
|
- int i, plen = 0;
|
|
-
|
|
- clone = alloc_skb(0, GFP_ATOMIC);
|
|
- if (!clone)
|
|
- goto out_oom;
|
|
- clone->next = head->next;
|
|
- head->next = clone;
|
|
- skb_shinfo(clone)->frag_list = skb_shinfo(head)->frag_list;
|
|
- skb_frag_list_init(head);
|
|
- for (i = 0; i < skb_shinfo(head)->nr_frags; i++)
|
|
- plen += skb_frag_size(&skb_shinfo(head)->frags[i]);
|
|
- clone->len = clone->data_len = head->data_len - plen;
|
|
- head->data_len -= clone->len;
|
|
- head->len -= clone->len;
|
|
- clone->csum = 0;
|
|
- clone->ip_summed = head->ip_summed;
|
|
- add_frag_mem_limit(fq->q.net, clone->truesize);
|
|
- }
|
|
-
|
|
/* We have to remove fragment header from datagram and to relocate
|
|
* header in order to calculate ICV correctly. */
|
|
nhoff = fq->nhoffset;
|
|
- skb_network_header(head)[nhoff] = skb_transport_header(head)[0];
|
|
- memmove(head->head + sizeof(struct frag_hdr), head->head,
|
|
- (head->data - head->head) - sizeof(struct frag_hdr));
|
|
- if (skb_mac_header_was_set(head))
|
|
- head->mac_header += sizeof(struct frag_hdr);
|
|
- head->network_header += sizeof(struct frag_hdr);
|
|
-
|
|
- skb_reset_transport_header(head);
|
|
- skb_push(head, head->data - skb_network_header(head));
|
|
-
|
|
- sum_truesize = head->truesize;
|
|
- for (fp = head->next; fp;) {
|
|
- bool headstolen;
|
|
- int delta;
|
|
- struct sk_buff *next = fp->next;
|
|
-
|
|
- sum_truesize += fp->truesize;
|
|
- if (head->ip_summed != fp->ip_summed)
|
|
- head->ip_summed = CHECKSUM_NONE;
|
|
- else if (head->ip_summed == CHECKSUM_COMPLETE)
|
|
- head->csum = csum_add(head->csum, fp->csum);
|
|
-
|
|
- if (skb_try_coalesce(head, fp, &headstolen, &delta)) {
|
|
- kfree_skb_partial(fp, headstolen);
|
|
- } else {
|
|
- if (!skb_shinfo(head)->frag_list)
|
|
- skb_shinfo(head)->frag_list = fp;
|
|
- head->data_len += fp->len;
|
|
- head->len += fp->len;
|
|
- head->truesize += fp->truesize;
|
|
- }
|
|
- fp = next;
|
|
- }
|
|
- sub_frag_mem_limit(fq->q.net, sum_truesize);
|
|
+ skb_network_header(skb)[nhoff] = skb_transport_header(skb)[0];
|
|
+ memmove(skb->head + sizeof(struct frag_hdr), skb->head,
|
|
+ (skb->data - skb->head) - sizeof(struct frag_hdr));
|
|
+ if (skb_mac_header_was_set(skb))
|
|
+ skb->mac_header += sizeof(struct frag_hdr);
|
|
+ skb->network_header += sizeof(struct frag_hdr);
|
|
+
|
|
+ skb_reset_transport_header(skb);
|
|
+
|
|
+ inet_frag_reasm_finish(&fq->q, skb, reasm_data);
|
|
|
|
- head->next = NULL;
|
|
- head->dev = dev;
|
|
- head->tstamp = fq->q.stamp;
|
|
- ipv6_hdr(head)->payload_len = htons(payload_len);
|
|
- ipv6_change_dsfield(ipv6_hdr(head), 0xff, ecn);
|
|
- IP6CB(head)->nhoff = nhoff;
|
|
- IP6CB(head)->flags |= IP6SKB_FRAGMENTED;
|
|
- IP6CB(head)->frag_max_size = fq->q.max_size;
|
|
+ skb->dev = dev;
|
|
+ ipv6_hdr(skb)->payload_len = htons(payload_len);
|
|
+ ipv6_change_dsfield(ipv6_hdr(skb), 0xff, ecn);
|
|
+ IP6CB(skb)->nhoff = nhoff;
|
|
+ IP6CB(skb)->flags |= IP6SKB_FRAGMENTED;
|
|
+ IP6CB(skb)->frag_max_size = fq->q.max_size;
|
|
|
|
/* Yes, and fold redundant checksum back. 8) */
|
|
- skb_postpush_rcsum(head, skb_network_header(head),
|
|
- skb_network_header_len(head));
|
|
+ skb_postpush_rcsum(skb, skb_network_header(skb),
|
|
+ skb_network_header_len(skb));
|
|
|
|
rcu_read_lock();
|
|
__IP6_INC_STATS(net, __in6_dev_get(dev), IPSTATS_MIB_REASMOKS);
|
|
@@ -480,6 +307,7 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *prev,
|
|
fq->q.fragments = NULL;
|
|
fq->q.rb_fragments = RB_ROOT;
|
|
fq->q.fragments_tail = NULL;
|
|
+ fq->q.last_run_head = NULL;
|
|
return 1;
|
|
|
|
out_oversize:
|
|
@@ -491,6 +319,7 @@ out_fail:
|
|
rcu_read_lock();
|
|
__IP6_INC_STATS(net, __in6_dev_get(dev), IPSTATS_MIB_REASMFAILS);
|
|
rcu_read_unlock();
|
|
+ inet_frag_kill(&fq->q);
|
|
return -1;
|
|
}
|
|
|
|
@@ -529,22 +358,26 @@ static int ipv6_frag_rcv(struct sk_buff *skb)
|
|
return 1;
|
|
}
|
|
|
|
- if (skb->len - skb_network_offset(skb) < IPV6_MIN_MTU &&
|
|
- fhdr->frag_off & htons(IP6_MF))
|
|
- goto fail_hdr;
|
|
-
|
|
iif = skb->dev ? skb->dev->ifindex : 0;
|
|
fq = fq_find(net, fhdr->identification, hdr, iif);
|
|
if (fq) {
|
|
+ u32 prob_offset = 0;
|
|
int ret;
|
|
|
|
spin_lock(&fq->q.lock);
|
|
|
|
fq->iif = iif;
|
|
- ret = ip6_frag_queue(fq, skb, fhdr, IP6CB(skb)->nhoff);
|
|
+ ret = ip6_frag_queue(fq, skb, fhdr, IP6CB(skb)->nhoff,
|
|
+ &prob_offset);
|
|
|
|
spin_unlock(&fq->q.lock);
|
|
inet_frag_put(&fq->q);
|
|
+ if (prob_offset) {
|
|
+ __IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
|
|
+ IPSTATS_MIB_INHDRERRORS);
|
|
+ /* icmpv6_param_prob() calls kfree_skb(skb) */
|
|
+ icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, prob_offset);
|
|
+ }
|
|
return ret;
|
|
}
|
|
|
|
@@ -712,42 +545,19 @@ static struct pernet_operations ip6_frags_ops = {
|
|
.exit = ipv6_frags_exit_net,
|
|
};
|
|
|
|
-static u32 ip6_key_hashfn(const void *data, u32 len, u32 seed)
|
|
-{
|
|
- return jhash2(data,
|
|
- sizeof(struct frag_v6_compare_key) / sizeof(u32), seed);
|
|
-}
|
|
-
|
|
-static u32 ip6_obj_hashfn(const void *data, u32 len, u32 seed)
|
|
-{
|
|
- const struct inet_frag_queue *fq = data;
|
|
-
|
|
- return jhash2((const u32 *)&fq->key.v6,
|
|
- sizeof(struct frag_v6_compare_key) / sizeof(u32), seed);
|
|
-}
|
|
-
|
|
-static int ip6_obj_cmpfn(struct rhashtable_compare_arg *arg, const void *ptr)
|
|
-{
|
|
- const struct frag_v6_compare_key *key = arg->key;
|
|
- const struct inet_frag_queue *fq = ptr;
|
|
-
|
|
- return !!memcmp(&fq->key, key, sizeof(*key));
|
|
-}
|
|
-
|
|
-const struct rhashtable_params ip6_rhash_params = {
|
|
+static const struct rhashtable_params ip6_rhash_params = {
|
|
.head_offset = offsetof(struct inet_frag_queue, node),
|
|
- .hashfn = ip6_key_hashfn,
|
|
- .obj_hashfn = ip6_obj_hashfn,
|
|
- .obj_cmpfn = ip6_obj_cmpfn,
|
|
+ .hashfn = ip6frag_key_hashfn,
|
|
+ .obj_hashfn = ip6frag_obj_hashfn,
|
|
+ .obj_cmpfn = ip6frag_obj_cmpfn,
|
|
.automatic_shrinking = true,
|
|
};
|
|
-EXPORT_SYMBOL(ip6_rhash_params);
|
|
|
|
int __init ipv6_frag_init(void)
|
|
{
|
|
int ret;
|
|
|
|
- ip6_frags.constructor = ip6_frag_init;
|
|
+ ip6_frags.constructor = ip6frag_init;
|
|
ip6_frags.destructor = NULL;
|
|
ip6_frags.qsize = sizeof(struct frag_queue);
|
|
ip6_frags.frag_expire = ip6_frag_expire;
|
|
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
|
|
index 4d82fe7d627c..284276b3e0b4 100644
|
|
--- a/net/mac80211/driver-ops.h
|
|
+++ b/net/mac80211/driver-ops.h
|
|
@@ -1164,6 +1164,9 @@ static inline void drv_wake_tx_queue(struct ieee80211_local *local,
|
|
{
|
|
struct ieee80211_sub_if_data *sdata = vif_to_sdata(txq->txq.vif);
|
|
|
|
+ if (local->in_reconfig)
|
|
+ return;
|
|
+
|
|
if (!check_sdata_in_driver(sdata))
|
|
return;
|
|
|
|
diff --git a/net/openvswitch/conntrack.c b/net/openvswitch/conntrack.c
|
|
index 285f8797c26a..0171b27a2b81 100644
|
|
--- a/net/openvswitch/conntrack.c
|
|
+++ b/net/openvswitch/conntrack.c
|
|
@@ -23,6 +23,7 @@
|
|
#include <net/netfilter/nf_conntrack_seqadj.h>
|
|
#include <net/netfilter/nf_conntrack_zones.h>
|
|
#include <net/netfilter/ipv6/nf_defrag_ipv6.h>
|
|
+#include <net/ipv6_frag.h>
|
|
|
|
#ifdef CONFIG_NF_NAT_NEEDED
|
|
#include <linux/netfilter/nf_nat.h>
|
|
diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c
|
|
index 29d6699d5a06..55b4c0dc2b93 100644
|
|
--- a/scripts/mod/file2alias.c
|
|
+++ b/scripts/mod/file2alias.c
|
|
@@ -47,49 +47,9 @@ typedef struct {
|
|
struct devtable {
|
|
const char *device_id; /* name of table, __mod_<name>__*_device_table. */
|
|
unsigned long id_size;
|
|
- void *function;
|
|
+ int (*do_entry)(const char *filename, void *symval, char *alias);
|
|
};
|
|
|
|
-#define ___cat(a,b) a ## b
|
|
-#define __cat(a,b) ___cat(a,b)
|
|
-
|
|
-/* we need some special handling for this host tool running eventually on
|
|
- * Darwin. The Mach-O section handling is a bit different than ELF section
|
|
- * handling. The differnces in detail are:
|
|
- * a) we have segments which have sections
|
|
- * b) we need a API call to get the respective section symbols */
|
|
-#if defined(__MACH__)
|
|
-#include <mach-o/getsect.h>
|
|
-
|
|
-#define INIT_SECTION(name) do { \
|
|
- unsigned long name ## _len; \
|
|
- char *__cat(pstart_,name) = getsectdata("__TEXT", \
|
|
- #name, &__cat(name,_len)); \
|
|
- char *__cat(pstop_,name) = __cat(pstart_,name) + \
|
|
- __cat(name, _len); \
|
|
- __cat(__start_,name) = (void *)__cat(pstart_,name); \
|
|
- __cat(__stop_,name) = (void *)__cat(pstop_,name); \
|
|
- } while (0)
|
|
-#define SECTION(name) __attribute__((section("__TEXT, " #name)))
|
|
-
|
|
-struct devtable **__start___devtable, **__stop___devtable;
|
|
-#else
|
|
-#define INIT_SECTION(name) /* no-op for ELF */
|
|
-#define SECTION(name) __attribute__((section(#name)))
|
|
-
|
|
-/* We construct a table of pointers in an ELF section (pointers generally
|
|
- * go unpadded by gcc). ld creates boundary syms for us. */
|
|
-extern struct devtable *__start___devtable[], *__stop___devtable[];
|
|
-#endif /* __MACH__ */
|
|
-
|
|
-#if !defined(__used)
|
|
-# if __GNUC__ == 3 && __GNUC_MINOR__ < 3
|
|
-# define __used __attribute__((__unused__))
|
|
-# else
|
|
-# define __used __attribute__((__used__))
|
|
-# endif
|
|
-#endif
|
|
-
|
|
/* Define a variable f that holds the value of field f of struct devid
|
|
* based at address m.
|
|
*/
|
|
@@ -102,16 +62,6 @@ extern struct devtable *__start___devtable[], *__stop___devtable[];
|
|
#define DEF_FIELD_ADDR(m, devid, f) \
|
|
typeof(((struct devid *)0)->f) *f = ((m) + OFF_##devid##_##f)
|
|
|
|
-/* Add a table entry. We test function type matches while we're here. */
|
|
-#define ADD_TO_DEVTABLE(device_id, type, function) \
|
|
- static struct devtable __cat(devtable,__LINE__) = { \
|
|
- device_id + 0*sizeof((function)((const char *)NULL, \
|
|
- (void *)NULL, \
|
|
- (char *)NULL)), \
|
|
- SIZE_##type, (function) }; \
|
|
- static struct devtable *SECTION(__devtable) __used \
|
|
- __cat(devtable_ptr,__LINE__) = &__cat(devtable,__LINE__)
|
|
-
|
|
#define ADD(str, sep, cond, field) \
|
|
do { \
|
|
strcat(str, sep); \
|
|
@@ -431,7 +381,6 @@ static int do_hid_entry(const char *filename,
|
|
|
|
return 1;
|
|
}
|
|
-ADD_TO_DEVTABLE("hid", hid_device_id, do_hid_entry);
|
|
|
|
/* Looks like: ieee1394:venNmoNspNverN */
|
|
static int do_ieee1394_entry(const char *filename,
|
|
@@ -456,7 +405,6 @@ static int do_ieee1394_entry(const char *filename,
|
|
add_wildcard(alias);
|
|
return 1;
|
|
}
|
|
-ADD_TO_DEVTABLE("ieee1394", ieee1394_device_id, do_ieee1394_entry);
|
|
|
|
/* Looks like: pci:vNdNsvNsdNbcNscNiN. */
|
|
static int do_pci_entry(const char *filename,
|
|
@@ -500,7 +448,6 @@ static int do_pci_entry(const char *filename,
|
|
add_wildcard(alias);
|
|
return 1;
|
|
}
|
|
-ADD_TO_DEVTABLE("pci", pci_device_id, do_pci_entry);
|
|
|
|
/* looks like: "ccw:tNmNdtNdmN" */
|
|
static int do_ccw_entry(const char *filename,
|
|
@@ -524,7 +471,6 @@ static int do_ccw_entry(const char *filename,
|
|
add_wildcard(alias);
|
|
return 1;
|
|
}
|
|
-ADD_TO_DEVTABLE("ccw", ccw_device_id, do_ccw_entry);
|
|
|
|
/* looks like: "ap:tN" */
|
|
static int do_ap_entry(const char *filename,
|
|
@@ -535,7 +481,6 @@ static int do_ap_entry(const char *filename,
|
|
sprintf(alias, "ap:t%02X*", dev_type);
|
|
return 1;
|
|
}
|
|
-ADD_TO_DEVTABLE("ap", ap_device_id, do_ap_entry);
|
|
|
|
/* looks like: "css:tN" */
|
|
static int do_css_entry(const char *filename,
|
|
@@ -546,7 +491,6 @@ static int do_css_entry(const char *filename,
|
|
sprintf(alias, "css:t%01X", type);
|
|
return 1;
|
|
}
|
|
-ADD_TO_DEVTABLE("css", css_device_id, do_css_entry);
|
|
|
|
/* Looks like: "serio:tyNprNidNexN" */
|
|
static int do_serio_entry(const char *filename,
|
|
@@ -566,7 +510,6 @@ static int do_serio_entry(const char *filename,
|
|
add_wildcard(alias);
|
|
return 1;
|
|
}
|
|
-ADD_TO_DEVTABLE("serio", serio_device_id, do_serio_entry);
|
|
|
|
/* looks like: "acpi:ACPI0003" or "acpi:PNP0C0B" or "acpi:LNXVIDEO" or
|
|
* "acpi:bbsspp" (bb=base-class, ss=sub-class, pp=prog-if)
|
|
@@ -604,7 +547,6 @@ static int do_acpi_entry(const char *filename,
|
|
}
|
|
return 1;
|
|
}
|
|
-ADD_TO_DEVTABLE("acpi", acpi_device_id, do_acpi_entry);
|
|
|
|
/* looks like: "pnp:dD" */
|
|
static void do_pnp_device_entry(void *symval, unsigned long size,
|
|
@@ -725,7 +667,6 @@ static int do_pcmcia_entry(const char *filename,
|
|
add_wildcard(alias);
|
|
return 1;
|
|
}
|
|
-ADD_TO_DEVTABLE("pcmcia", pcmcia_device_id, do_pcmcia_entry);
|
|
|
|
static int do_vio_entry(const char *filename, void *symval,
|
|
char *alias)
|
|
@@ -745,7 +686,6 @@ static int do_vio_entry(const char *filename, void *symval,
|
|
add_wildcard(alias);
|
|
return 1;
|
|
}
|
|
-ADD_TO_DEVTABLE("vio", vio_device_id, do_vio_entry);
|
|
|
|
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
|
|
|
|
@@ -818,7 +758,6 @@ static int do_input_entry(const char *filename, void *symval,
|
|
do_input(alias, *swbit, 0, INPUT_DEVICE_ID_SW_MAX);
|
|
return 1;
|
|
}
|
|
-ADD_TO_DEVTABLE("input", input_device_id, do_input_entry);
|
|
|
|
static int do_eisa_entry(const char *filename, void *symval,
|
|
char *alias)
|
|
@@ -830,7 +769,6 @@ static int do_eisa_entry(const char *filename, void *symval,
|
|
strcat(alias, "*");
|
|
return 1;
|
|
}
|
|
-ADD_TO_DEVTABLE("eisa", eisa_device_id, do_eisa_entry);
|
|
|
|
/* Looks like: parisc:tNhvNrevNsvN */
|
|
static int do_parisc_entry(const char *filename, void *symval,
|
|
@@ -850,7 +788,6 @@ static int do_parisc_entry(const char *filename, void *symval,
|
|
add_wildcard(alias);
|
|
return 1;
|
|
}
|
|
-ADD_TO_DEVTABLE("parisc", parisc_device_id, do_parisc_entry);
|
|
|
|
/* Looks like: sdio:cNvNdN. */
|
|
static int do_sdio_entry(const char *filename,
|
|
@@ -867,7 +804,6 @@ static int do_sdio_entry(const char *filename,
|
|
add_wildcard(alias);
|
|
return 1;
|
|
}
|
|
-ADD_TO_DEVTABLE("sdio", sdio_device_id, do_sdio_entry);
|
|
|
|
/* Looks like: ssb:vNidNrevN. */
|
|
static int do_ssb_entry(const char *filename,
|
|
@@ -884,7 +820,6 @@ static int do_ssb_entry(const char *filename,
|
|
add_wildcard(alias);
|
|
return 1;
|
|
}
|
|
-ADD_TO_DEVTABLE("ssb", ssb_device_id, do_ssb_entry);
|
|
|
|
/* Looks like: bcma:mNidNrevNclN. */
|
|
static int do_bcma_entry(const char *filename,
|
|
@@ -903,7 +838,6 @@ static int do_bcma_entry(const char *filename,
|
|
add_wildcard(alias);
|
|
return 1;
|
|
}
|
|
-ADD_TO_DEVTABLE("bcma", bcma_device_id, do_bcma_entry);
|
|
|
|
/* Looks like: virtio:dNvN */
|
|
static int do_virtio_entry(const char *filename, void *symval,
|
|
@@ -919,7 +853,6 @@ static int do_virtio_entry(const char *filename, void *symval,
|
|
add_wildcard(alias);
|
|
return 1;
|
|
}
|
|
-ADD_TO_DEVTABLE("virtio", virtio_device_id, do_virtio_entry);
|
|
|
|
/*
|
|
* Looks like: vmbus:guid
|
|
@@ -942,7 +875,6 @@ static int do_vmbus_entry(const char *filename, void *symval,
|
|
|
|
return 1;
|
|
}
|
|
-ADD_TO_DEVTABLE("vmbus", hv_vmbus_device_id, do_vmbus_entry);
|
|
|
|
/* Looks like: i2c:S */
|
|
static int do_i2c_entry(const char *filename, void *symval,
|
|
@@ -953,7 +885,6 @@ static int do_i2c_entry(const char *filename, void *symval,
|
|
|
|
return 1;
|
|
}
|
|
-ADD_TO_DEVTABLE("i2c", i2c_device_id, do_i2c_entry);
|
|
|
|
/* Looks like: spi:S */
|
|
static int do_spi_entry(const char *filename, void *symval,
|
|
@@ -964,7 +895,6 @@ static int do_spi_entry(const char *filename, void *symval,
|
|
|
|
return 1;
|
|
}
|
|
-ADD_TO_DEVTABLE("spi", spi_device_id, do_spi_entry);
|
|
|
|
static const struct dmifield {
|
|
const char *prefix;
|
|
@@ -1019,7 +949,6 @@ static int do_dmi_entry(const char *filename, void *symval,
|
|
strcat(alias, ":");
|
|
return 1;
|
|
}
|
|
-ADD_TO_DEVTABLE("dmi", dmi_system_id, do_dmi_entry);
|
|
|
|
static int do_platform_entry(const char *filename,
|
|
void *symval, char *alias)
|
|
@@ -1028,7 +957,6 @@ static int do_platform_entry(const char *filename,
|
|
sprintf(alias, PLATFORM_MODULE_PREFIX "%s", *name);
|
|
return 1;
|
|
}
|
|
-ADD_TO_DEVTABLE("platform", platform_device_id, do_platform_entry);
|
|
|
|
static int do_mdio_entry(const char *filename,
|
|
void *symval, char *alias)
|
|
@@ -1053,7 +981,6 @@ static int do_mdio_entry(const char *filename,
|
|
|
|
return 1;
|
|
}
|
|
-ADD_TO_DEVTABLE("mdio", mdio_device_id, do_mdio_entry);
|
|
|
|
/* Looks like: zorro:iN. */
|
|
static int do_zorro_entry(const char *filename, void *symval,
|
|
@@ -1064,7 +991,6 @@ static int do_zorro_entry(const char *filename, void *symval,
|
|
ADD(alias, "i", id != ZORRO_WILDCARD, id);
|
|
return 1;
|
|
}
|
|
-ADD_TO_DEVTABLE("zorro", zorro_device_id, do_zorro_entry);
|
|
|
|
/* looks like: "pnp:dD" */
|
|
static int do_isapnp_entry(const char *filename,
|
|
@@ -1080,7 +1006,6 @@ static int do_isapnp_entry(const char *filename,
|
|
(function >> 12) & 0x0f, (function >> 8) & 0x0f);
|
|
return 1;
|
|
}
|
|
-ADD_TO_DEVTABLE("isapnp", isapnp_device_id, do_isapnp_entry);
|
|
|
|
/* Looks like: "ipack:fNvNdN". */
|
|
static int do_ipack_entry(const char *filename,
|
|
@@ -1096,7 +1021,6 @@ static int do_ipack_entry(const char *filename,
|
|
add_wildcard(alias);
|
|
return 1;
|
|
}
|
|
-ADD_TO_DEVTABLE("ipack", ipack_device_id, do_ipack_entry);
|
|
|
|
/*
|
|
* Append a match expression for a single masked hex digit.
|
|
@@ -1167,7 +1091,6 @@ static int do_amba_entry(const char *filename,
|
|
|
|
return 1;
|
|
}
|
|
-ADD_TO_DEVTABLE("amba", amba_id, do_amba_entry);
|
|
|
|
/*
|
|
* looks like: "mipscdmm:tN"
|
|
@@ -1183,7 +1106,6 @@ static int do_mips_cdmm_entry(const char *filename,
|
|
sprintf(alias, "mipscdmm:t%02X*", type);
|
|
return 1;
|
|
}
|
|
-ADD_TO_DEVTABLE("mipscdmm", mips_cdmm_device_id, do_mips_cdmm_entry);
|
|
|
|
/* LOOKS like cpu:type:x86,venVVVVfamFFFFmodMMMM:feature:*,FEAT,*
|
|
* All fields are numbers. It would be nicer to use strings for vendor
|
|
@@ -1208,7 +1130,6 @@ static int do_x86cpu_entry(const char *filename, void *symval,
|
|
sprintf(alias + strlen(alias), "%04X*", feature);
|
|
return 1;
|
|
}
|
|
-ADD_TO_DEVTABLE("x86cpu", x86_cpu_id, do_x86cpu_entry);
|
|
|
|
/* LOOKS like cpu:type:*:feature:*FEAT* */
|
|
static int do_cpu_entry(const char *filename, void *symval, char *alias)
|
|
@@ -1218,7 +1139,6 @@ static int do_cpu_entry(const char *filename, void *symval, char *alias)
|
|
sprintf(alias, "cpu:type:*:feature:*%04X*", feature);
|
|
return 1;
|
|
}
|
|
-ADD_TO_DEVTABLE("cpu", cpu_feature, do_cpu_entry);
|
|
|
|
/* Looks like: mei:S:uuid:N:* */
|
|
static int do_mei_entry(const char *filename, void *symval,
|
|
@@ -1237,7 +1157,6 @@ static int do_mei_entry(const char *filename, void *symval,
|
|
|
|
return 1;
|
|
}
|
|
-ADD_TO_DEVTABLE("mei", mei_cl_device_id, do_mei_entry);
|
|
|
|
/* Looks like: rapidio:vNdNavNadN */
|
|
static int do_rio_entry(const char *filename,
|
|
@@ -1257,7 +1176,6 @@ static int do_rio_entry(const char *filename,
|
|
add_wildcard(alias);
|
|
return 1;
|
|
}
|
|
-ADD_TO_DEVTABLE("rapidio", rio_device_id, do_rio_entry);
|
|
|
|
/* Looks like: ulpi:vNpN */
|
|
static int do_ulpi_entry(const char *filename, void *symval,
|
|
@@ -1270,7 +1188,6 @@ static int do_ulpi_entry(const char *filename, void *symval,
|
|
|
|
return 1;
|
|
}
|
|
-ADD_TO_DEVTABLE("ulpi", ulpi_device_id, do_ulpi_entry);
|
|
|
|
/* Looks like: hdaudio:vNrNaN */
|
|
static int do_hda_entry(const char *filename, void *symval, char *alias)
|
|
@@ -1287,7 +1204,6 @@ static int do_hda_entry(const char *filename, void *symval, char *alias)
|
|
add_wildcard(alias);
|
|
return 1;
|
|
}
|
|
-ADD_TO_DEVTABLE("hdaudio", hda_device_id, do_hda_entry);
|
|
|
|
/* Looks like: fsl-mc:vNdN */
|
|
static int do_fsl_mc_entry(const char *filename, void *symval,
|
|
@@ -1299,7 +1215,6 @@ static int do_fsl_mc_entry(const char *filename, void *symval,
|
|
sprintf(alias, "fsl-mc:v%08Xd%s", vendor, *obj_type);
|
|
return 1;
|
|
}
|
|
-ADD_TO_DEVTABLE("fslmc", fsl_mc_device_id, do_fsl_mc_entry);
|
|
|
|
/* Does namelen bytes of name exactly match the symbol? */
|
|
static bool sym_is(const char *name, unsigned namelen, const char *symbol)
|
|
@@ -1313,12 +1228,11 @@ static bool sym_is(const char *name, unsigned namelen, const char *symbol)
|
|
static void do_table(void *symval, unsigned long size,
|
|
unsigned long id_size,
|
|
const char *device_id,
|
|
- void *function,
|
|
+ int (*do_entry)(const char *filename, void *symval, char *alias),
|
|
struct module *mod)
|
|
{
|
|
unsigned int i;
|
|
char alias[500];
|
|
- int (*do_entry)(const char *, void *entry, char *alias) = function;
|
|
|
|
device_id_check(mod->name, device_id, size, id_size, symval);
|
|
/* Leave last one: it's the terminator. */
|
|
@@ -1332,6 +1246,44 @@ static void do_table(void *symval, unsigned long size,
|
|
}
|
|
}
|
|
|
|
+static const struct devtable devtable[] = {
|
|
+ {"hid", SIZE_hid_device_id, do_hid_entry},
|
|
+ {"ieee1394", SIZE_ieee1394_device_id, do_ieee1394_entry},
|
|
+ {"pci", SIZE_pci_device_id, do_pci_entry},
|
|
+ {"ccw", SIZE_ccw_device_id, do_ccw_entry},
|
|
+ {"ap", SIZE_ap_device_id, do_ap_entry},
|
|
+ {"css", SIZE_css_device_id, do_css_entry},
|
|
+ {"serio", SIZE_serio_device_id, do_serio_entry},
|
|
+ {"acpi", SIZE_acpi_device_id, do_acpi_entry},
|
|
+ {"pcmcia", SIZE_pcmcia_device_id, do_pcmcia_entry},
|
|
+ {"vio", SIZE_vio_device_id, do_vio_entry},
|
|
+ {"input", SIZE_input_device_id, do_input_entry},
|
|
+ {"eisa", SIZE_eisa_device_id, do_eisa_entry},
|
|
+ {"parisc", SIZE_parisc_device_id, do_parisc_entry},
|
|
+ {"sdio", SIZE_sdio_device_id, do_sdio_entry},
|
|
+ {"ssb", SIZE_ssb_device_id, do_ssb_entry},
|
|
+ {"bcma", SIZE_bcma_device_id, do_bcma_entry},
|
|
+ {"virtio", SIZE_virtio_device_id, do_virtio_entry},
|
|
+ {"vmbus", SIZE_hv_vmbus_device_id, do_vmbus_entry},
|
|
+ {"i2c", SIZE_i2c_device_id, do_i2c_entry},
|
|
+ {"spi", SIZE_spi_device_id, do_spi_entry},
|
|
+ {"dmi", SIZE_dmi_system_id, do_dmi_entry},
|
|
+ {"platform", SIZE_platform_device_id, do_platform_entry},
|
|
+ {"mdio", SIZE_mdio_device_id, do_mdio_entry},
|
|
+ {"zorro", SIZE_zorro_device_id, do_zorro_entry},
|
|
+ {"isapnp", SIZE_isapnp_device_id, do_isapnp_entry},
|
|
+ {"ipack", SIZE_ipack_device_id, do_ipack_entry},
|
|
+ {"amba", SIZE_amba_id, do_amba_entry},
|
|
+ {"mipscdmm", SIZE_mips_cdmm_device_id, do_mips_cdmm_entry},
|
|
+ {"x86cpu", SIZE_x86_cpu_id, do_x86cpu_entry},
|
|
+ {"cpu", SIZE_cpu_feature, do_cpu_entry},
|
|
+ {"mei", SIZE_mei_cl_device_id, do_mei_entry},
|
|
+ {"rapidio", SIZE_rio_device_id, do_rio_entry},
|
|
+ {"ulpi", SIZE_ulpi_device_id, do_ulpi_entry},
|
|
+ {"hdaudio", SIZE_hda_device_id, do_hda_entry},
|
|
+ {"fslmc", SIZE_fsl_mc_device_id, do_fsl_mc_entry},
|
|
+};
|
|
+
|
|
/* Create MODULE_ALIAS() statements.
|
|
* At this time, we cannot write the actual output C source yet,
|
|
* so we write into the mod->dev_table_buf buffer. */
|
|
@@ -1386,13 +1338,14 @@ void handle_moddevtable(struct module *mod, struct elf_info *info,
|
|
else if (sym_is(name, namelen, "pnp_card"))
|
|
do_pnp_card_entries(symval, sym->st_size, mod);
|
|
else {
|
|
- struct devtable **p;
|
|
- INIT_SECTION(__devtable);
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < ARRAY_SIZE(devtable); i++) {
|
|
+ const struct devtable *p = &devtable[i];
|
|
|
|
- for (p = __start___devtable; p < __stop___devtable; p++) {
|
|
- if (sym_is(name, namelen, (*p)->device_id)) {
|
|
- do_table(symval, sym->st_size, (*p)->id_size,
|
|
- (*p)->device_id, (*p)->function, mod);
|
|
+ if (sym_is(name, namelen, p->device_id)) {
|
|
+ do_table(symval, sym->st_size, p->id_size,
|
|
+ p->device_id, p->do_entry, mod);
|
|
break;
|
|
}
|
|
}
|
|
diff --git a/security/device_cgroup.c b/security/device_cgroup.c
|
|
index 5ef7e5240563..ea014df89428 100644
|
|
--- a/security/device_cgroup.c
|
|
+++ b/security/device_cgroup.c
|
|
@@ -569,7 +569,7 @@ static int propagate_exception(struct dev_cgroup *devcg_root,
|
|
devcg->behavior == DEVCG_DEFAULT_ALLOW) {
|
|
rc = dev_exception_add(devcg, ex);
|
|
if (rc)
|
|
- break;
|
|
+ return rc;
|
|
} else {
|
|
/*
|
|
* in the other possible cases:
|
|
diff --git a/sound/core/info.c b/sound/core/info.c
|
|
index bcf6a48cc70d..5fb00437507b 100644
|
|
--- a/sound/core/info.c
|
|
+++ b/sound/core/info.c
|
|
@@ -722,8 +722,11 @@ snd_info_create_entry(const char *name, struct snd_info_entry *parent)
|
|
INIT_LIST_HEAD(&entry->children);
|
|
INIT_LIST_HEAD(&entry->list);
|
|
entry->parent = parent;
|
|
- if (parent)
|
|
+ if (parent) {
|
|
+ mutex_lock(&parent->access);
|
|
list_add_tail(&entry->list, &parent->children);
|
|
+ mutex_unlock(&parent->access);
|
|
+ }
|
|
return entry;
|
|
}
|
|
|
|
@@ -805,7 +808,12 @@ void snd_info_free_entry(struct snd_info_entry * entry)
|
|
list_for_each_entry_safe(p, n, &entry->children, list)
|
|
snd_info_free_entry(p);
|
|
|
|
- list_del(&entry->list);
|
|
+ p = entry->parent;
|
|
+ if (p) {
|
|
+ mutex_lock(&p->access);
|
|
+ list_del(&entry->list);
|
|
+ mutex_unlock(&p->access);
|
|
+ }
|
|
kfree(entry->name);
|
|
if (entry->private_free)
|
|
entry->private_free(entry);
|
|
diff --git a/sound/core/init.c b/sound/core/init.c
|
|
index 32ebe2f6bc59..dcb9199f5e4f 100644
|
|
--- a/sound/core/init.c
|
|
+++ b/sound/core/init.c
|
|
@@ -406,14 +406,7 @@ int snd_card_disconnect(struct snd_card *card)
|
|
card->shutdown = 1;
|
|
spin_unlock(&card->files_lock);
|
|
|
|
- /* phase 1: disable fops (user space) operations for ALSA API */
|
|
- mutex_lock(&snd_card_mutex);
|
|
- snd_cards[card->number] = NULL;
|
|
- clear_bit(card->number, snd_cards_lock);
|
|
- mutex_unlock(&snd_card_mutex);
|
|
-
|
|
- /* phase 2: replace file->f_op with special dummy operations */
|
|
-
|
|
+ /* replace file->f_op with special dummy operations */
|
|
spin_lock(&card->files_lock);
|
|
list_for_each_entry(mfile, &card->files_list, list) {
|
|
/* it's critical part, use endless loop */
|
|
@@ -429,7 +422,7 @@ int snd_card_disconnect(struct snd_card *card)
|
|
}
|
|
spin_unlock(&card->files_lock);
|
|
|
|
- /* phase 3: notify all connected devices about disconnection */
|
|
+ /* notify all connected devices about disconnection */
|
|
/* at this point, they cannot respond to any calls except release() */
|
|
|
|
#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
|
|
@@ -445,6 +438,13 @@ int snd_card_disconnect(struct snd_card *card)
|
|
device_del(&card->card_dev);
|
|
card->registered = false;
|
|
}
|
|
+
|
|
+ /* disable fops (user space) operations for ALSA API */
|
|
+ mutex_lock(&snd_card_mutex);
|
|
+ snd_cards[card->number] = NULL;
|
|
+ clear_bit(card->number, snd_cards_lock);
|
|
+ mutex_unlock(&snd_card_mutex);
|
|
+
|
|
#ifdef CONFIG_PM
|
|
wake_up(&card->power_sleep);
|
|
#endif
|
|
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
|
|
index 9637d0bbdeb5..b9e720cb6f02 100644
|
|
--- a/sound/pci/hda/patch_realtek.c
|
|
+++ b/sound/pci/hda/patch_realtek.c
|
|
@@ -6743,6 +6743,8 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
|
|
{0x12, 0x90a60140},
|
|
{0x14, 0x90170150},
|
|
{0x21, 0x02211020}),
|
|
+ SND_HDA_PIN_QUIRK(0x10ec0236, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
|
|
+ {0x21, 0x02211020}),
|
|
SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL2_MIC_NO_PRESENCE,
|
|
{0x14, 0x90170110},
|
|
{0x21, 0x02211020}),
|
|
@@ -6853,6 +6855,10 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
|
|
{0x21, 0x0221101f}),
|
|
SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
|
|
ALC256_STANDARD_PINS),
|
|
+ SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
|
|
+ {0x14, 0x90170110},
|
|
+ {0x1b, 0x01011020},
|
|
+ {0x21, 0x0221101f}),
|
|
SND_HDA_PIN_QUIRK(0x10ec0256, 0x1043, "ASUS", ALC256_FIXUP_ASUS_MIC,
|
|
{0x14, 0x90170110},
|
|
{0x1b, 0x90a70130},
|
|
diff --git a/tools/include/linux/bitops.h b/tools/include/linux/bitops.h
|
|
index acc704bd3998..0b0ef3abc966 100644
|
|
--- a/tools/include/linux/bitops.h
|
|
+++ b/tools/include/linux/bitops.h
|
|
@@ -3,8 +3,6 @@
|
|
#define _TOOLS_LINUX_BITOPS_H_
|
|
|
|
#include <asm/types.h>
|
|
-#include <linux/compiler.h>
|
|
-
|
|
#ifndef __WORDSIZE
|
|
#define __WORDSIZE (__SIZEOF_LONG__ * 8)
|
|
#endif
|
|
@@ -12,10 +10,9 @@
|
|
#ifndef BITS_PER_LONG
|
|
# define BITS_PER_LONG __WORDSIZE
|
|
#endif
|
|
+#include <linux/bits.h>
|
|
+#include <linux/compiler.h>
|
|
|
|
-#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG))
|
|
-#define BIT_WORD(nr) ((nr) / BITS_PER_LONG)
|
|
-#define BITS_PER_BYTE 8
|
|
#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))
|
|
#define BITS_TO_U64(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u64))
|
|
#define BITS_TO_U32(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u32))
|
|
diff --git a/tools/include/linux/bits.h b/tools/include/linux/bits.h
|
|
new file mode 100644
|
|
index 000000000000..2b7b532c1d51
|
|
--- /dev/null
|
|
+++ b/tools/include/linux/bits.h
|
|
@@ -0,0 +1,26 @@
|
|
+/* SPDX-License-Identifier: GPL-2.0 */
|
|
+#ifndef __LINUX_BITS_H
|
|
+#define __LINUX_BITS_H
|
|
+#include <asm/bitsperlong.h>
|
|
+
|
|
+#define BIT(nr) (1UL << (nr))
|
|
+#define BIT_ULL(nr) (1ULL << (nr))
|
|
+#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG))
|
|
+#define BIT_WORD(nr) ((nr) / BITS_PER_LONG)
|
|
+#define BIT_ULL_MASK(nr) (1ULL << ((nr) % BITS_PER_LONG_LONG))
|
|
+#define BIT_ULL_WORD(nr) ((nr) / BITS_PER_LONG_LONG)
|
|
+#define BITS_PER_BYTE 8
|
|
+
|
|
+/*
|
|
+ * Create a contiguous bitmask starting at bit position @l and ending at
|
|
+ * position @h. For example
|
|
+ * GENMASK_ULL(39, 21) gives us the 64bit vector 0x000000ffffe00000.
|
|
+ */
|
|
+#define GENMASK(h, l) \
|
|
+ (((~0UL) - (1UL << (l)) + 1) & (~0UL >> (BITS_PER_LONG - 1 - (h))))
|
|
+
|
|
+#define GENMASK_ULL(h, l) \
|
|
+ (((~0ULL) - (1ULL << (l)) + 1) & \
|
|
+ (~0ULL >> (BITS_PER_LONG_LONG - 1 - (h))))
|
|
+
|
|
+#endif /* __LINUX_BITS_H */
|
|
diff --git a/tools/perf/check-headers.sh b/tools/perf/check-headers.sh
|
|
index 50cd6228f506..df1dbee8d98d 100755
|
|
--- a/tools/perf/check-headers.sh
|
|
+++ b/tools/perf/check-headers.sh
|
|
@@ -11,6 +11,7 @@ include/uapi/linux/sched.h
|
|
include/uapi/linux/stat.h
|
|
include/uapi/linux/vhost.h
|
|
include/uapi/sound/asound.h
|
|
+include/linux/bits.h
|
|
include/linux/hash.h
|
|
include/uapi/linux/hw_breakpoint.h
|
|
arch/x86/include/asm/disabled-features.h
|