Merge 4.14.114 into android-4.14
Changes in 4.14.114 bonding: fix event handling for stacked bonds net: atm: Fix potential Spectre v1 vulnerabilities net: bridge: fix per-port af_packet sockets net: bridge: multicast: use rcu to access port list from br_multicast_start_querier net: fou: do not use guehdr after iptunnel_pull_offloads in gue_udp_recv tcp: tcp_grow_window() needs to respect tcp_space() team: set slave to promisc if team is already in promisc mode vhost: reject zero size iova range ipv4: recompile ip options in ipv4_link_failure ipv4: ensure rcu_read_lock() in ipv4_link_failure() net: thunderx: raise XDP MTU to 1508 net: thunderx: don't allow jumbo frames with XDP CIFS: keep FileInfo handle live during oplock break KVM: x86: Don't clear EFER during SMM transitions for 32-bit vCPU KVM: x86: svm: make sure NMI is injected after nmi_singlestep Staging: iio: meter: fixed typo staging: iio: ad7192: Fix ad7193 channel address iio: gyro: mpu3050: fix chip ID reading iio/gyro/bmg160: Use millidegrees for temperature scale iio: cros_ec: Fix the maths for gyro scale calculation iio: ad_sigma_delta: select channel when reading register iio: dac: mcp4725: add missing powerdown bits in store eeprom iio: Fix scan mask selection iio: adc: at91: disable adc channel interrupt in timeout case iio: core: fix a possible circular locking dependency io: accel: kxcjk1013: restore the range after resume. staging: comedi: vmk80xx: Fix use of uninitialized semaphore staging: comedi: vmk80xx: Fix possible double-free of ->usb_rx_buf staging: comedi: ni_usb6501: Fix use of uninitialized mutex staging: comedi: ni_usb6501: Fix possible double-free of ->usb_rx_buf ALSA: hda/realtek - add two more pin configuration sets to quirk table ALSA: core: Fix card races between register and disconnect scsi: core: set result when the command cannot be dispatched Revert "scsi: fcoe: clear FC_RP_STARTED flags when receiving a LOGO" Revert "svm: Fix AVIC incomplete IPI emulation" coredump: fix race condition between mmget_not_zero()/get_task_mm() and core dumping crypto: x86/poly1305 - fix overflow during partial reduction arm64: futex: Restore oldval initialization to work around buggy compilers x86/kprobes: Verify stack frame on kretprobe kprobes: Mark ftrace mcount handler functions nokprobe kprobes: Fix error check when reusing optimized probes rt2x00: do not increment sequence number while re-transmitting mac80211: do not call driver wake_tx_queue op during reconfig perf/x86/amd: Add event map for AMD Family 17h x86/cpu/bugs: Use __initconst for 'const' init data perf/x86: Fix incorrect PEBS_REGS x86/speculation: Prevent deadlock on ssb_state::lock crypto: crypto4xx - properly set IV after de- and encrypt mmc: sdhci: Fix data command CRC error handling mmc: sdhci: Rename SDHCI_ACMD12_ERR and SDHCI_INT_ACMD12ERR mmc: sdhci: Handle auto-command errors modpost: file2alias: go back to simple devtable lookup modpost: file2alias: check prototype of handler tpm/tpm_i2c_atmel: Return -E2BIG when the transfer is incomplete ipv6: frags: fix a lockdep false positive net: IP defrag: encapsulate rbtree defrag code into callable functions ipv6: remove dependency of nf_defrag_ipv6 on ipv6 module net: IP6 defrag: use rbtrees for IPv6 defrag net: IP6 defrag: use rbtrees in nf_conntrack_reasm.c Revert "kbuild: use -Oz instead of -Os when using clang" sched/fair: Limit sched_cfs_period_timer() loop to avoid hard lockup device_cgroup: fix RCU imbalance in error case mm/vmstat.c: fix /proc/vmstat format for CONFIG_DEBUG_TLBFLUSH=y CONFIG_SMP=n ALSA: info: Fix racy addition/deletion of nodes percpu: stop printing kernel addresses tools include: Adopt linux/bits.h iomap: report collisions between directio and buffered writes to userspace xfs: add the ability to join a held buffer to a defer_ops xfs: hold xfs_buf locked between shortform->leaf conversion and the addition of an attribute i2c-hid: properly terminate i2c_hid_dmi_desc_override_table[] array Revert "locking/lockdep: Add debug_locks check in __lock_downgrade()" kernel/sysctl.c: fix out-of-bounds access when setting file-max Linux 4.14.114 Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
This commit is contained in:
5
Makefile
5
Makefile
@@ -1,7 +1,7 @@
|
|||||||
# SPDX-License-Identifier: GPL-2.0
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
VERSION = 4
|
VERSION = 4
|
||||||
PATCHLEVEL = 14
|
PATCHLEVEL = 14
|
||||||
SUBLEVEL = 113
|
SUBLEVEL = 114
|
||||||
EXTRAVERSION =
|
EXTRAVERSION =
|
||||||
NAME = Petit Gorille
|
NAME = Petit Gorille
|
||||||
|
|
||||||
@@ -672,8 +672,7 @@ KBUILD_CFLAGS += $(call cc-disable-warning, int-in-bool-context)
|
|||||||
KBUILD_CFLAGS += $(call cc-disable-warning, attribute-alias)
|
KBUILD_CFLAGS += $(call cc-disable-warning, attribute-alias)
|
||||||
|
|
||||||
ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE
|
ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE
|
||||||
KBUILD_CFLAGS += $(call cc-option,-Oz,-Os)
|
KBUILD_CFLAGS += -Os $(call cc-disable-warning,maybe-uninitialized,)
|
||||||
KBUILD_CFLAGS += $(call cc-disable-warning,maybe-uninitialized,)
|
|
||||||
else
|
else
|
||||||
ifdef CONFIG_PROFILE_ALL_BRANCHES
|
ifdef CONFIG_PROFILE_ALL_BRANCHES
|
||||||
KBUILD_CFLAGS += -O2 $(call cc-disable-warning,maybe-uninitialized,)
|
KBUILD_CFLAGS += -O2 $(call cc-disable-warning,maybe-uninitialized,)
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ do { \
|
|||||||
static inline int
|
static inline int
|
||||||
arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *_uaddr)
|
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);
|
u32 __user *uaddr = __uaccess_mask_ptr(_uaddr);
|
||||||
|
|
||||||
pagefault_disable();
|
pagefault_disable();
|
||||||
|
|||||||
@@ -323,6 +323,12 @@ ENTRY(poly1305_4block_avx2)
|
|||||||
vpaddq t2,t1,t1
|
vpaddq t2,t1,t1
|
||||||
vmovq t1x,d4
|
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
|
# d1 += d0 >> 26
|
||||||
mov d0,%rax
|
mov d0,%rax
|
||||||
shr $26,%rax
|
shr $26,%rax
|
||||||
@@ -361,16 +367,16 @@ ENTRY(poly1305_4block_avx2)
|
|||||||
# h0 += (d4 >> 26) * 5
|
# h0 += (d4 >> 26) * 5
|
||||||
mov d4,%rax
|
mov d4,%rax
|
||||||
shr $26,%rax
|
shr $26,%rax
|
||||||
lea (%eax,%eax,4),%eax
|
lea (%rax,%rax,4),%rax
|
||||||
add %eax,%ebx
|
add %rax,%rbx
|
||||||
# h4 = d4 & 0x3ffffff
|
# h4 = d4 & 0x3ffffff
|
||||||
mov d4,%rax
|
mov d4,%rax
|
||||||
and $0x3ffffff,%eax
|
and $0x3ffffff,%eax
|
||||||
mov %eax,h4
|
mov %eax,h4
|
||||||
|
|
||||||
# h1 += h0 >> 26
|
# h1 += h0 >> 26
|
||||||
mov %ebx,%eax
|
mov %rbx,%rax
|
||||||
shr $26,%eax
|
shr $26,%rax
|
||||||
add %eax,h1
|
add %eax,h1
|
||||||
# h0 = h0 & 0x3ffffff
|
# h0 = h0 & 0x3ffffff
|
||||||
andl $0x3ffffff,%ebx
|
andl $0x3ffffff,%ebx
|
||||||
|
|||||||
@@ -253,16 +253,16 @@ ENTRY(poly1305_block_sse2)
|
|||||||
# h0 += (d4 >> 26) * 5
|
# h0 += (d4 >> 26) * 5
|
||||||
mov d4,%rax
|
mov d4,%rax
|
||||||
shr $26,%rax
|
shr $26,%rax
|
||||||
lea (%eax,%eax,4),%eax
|
lea (%rax,%rax,4),%rax
|
||||||
add %eax,%ebx
|
add %rax,%rbx
|
||||||
# h4 = d4 & 0x3ffffff
|
# h4 = d4 & 0x3ffffff
|
||||||
mov d4,%rax
|
mov d4,%rax
|
||||||
and $0x3ffffff,%eax
|
and $0x3ffffff,%eax
|
||||||
mov %eax,h4
|
mov %eax,h4
|
||||||
|
|
||||||
# h1 += h0 >> 26
|
# h1 += h0 >> 26
|
||||||
mov %ebx,%eax
|
mov %rbx,%rax
|
||||||
shr $26,%eax
|
shr $26,%rax
|
||||||
add %eax,h1
|
add %eax,h1
|
||||||
# h0 = h0 & 0x3ffffff
|
# h0 = h0 & 0x3ffffff
|
||||||
andl $0x3ffffff,%ebx
|
andl $0x3ffffff,%ebx
|
||||||
@@ -520,6 +520,12 @@ ENTRY(poly1305_2block_sse2)
|
|||||||
paddq t2,t1
|
paddq t2,t1
|
||||||
movq t1,d4
|
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
|
# d1 += d0 >> 26
|
||||||
mov d0,%rax
|
mov d0,%rax
|
||||||
shr $26,%rax
|
shr $26,%rax
|
||||||
@@ -558,16 +564,16 @@ ENTRY(poly1305_2block_sse2)
|
|||||||
# h0 += (d4 >> 26) * 5
|
# h0 += (d4 >> 26) * 5
|
||||||
mov d4,%rax
|
mov d4,%rax
|
||||||
shr $26,%rax
|
shr $26,%rax
|
||||||
lea (%eax,%eax,4),%eax
|
lea (%rax,%rax,4),%rax
|
||||||
add %eax,%ebx
|
add %rax,%rbx
|
||||||
# h4 = d4 & 0x3ffffff
|
# h4 = d4 & 0x3ffffff
|
||||||
mov d4,%rax
|
mov d4,%rax
|
||||||
and $0x3ffffff,%eax
|
and $0x3ffffff,%eax
|
||||||
mov %eax,h4
|
mov %eax,h4
|
||||||
|
|
||||||
# h1 += h0 >> 26
|
# h1 += h0 >> 26
|
||||||
mov %ebx,%eax
|
mov %rbx,%rax
|
||||||
shr $26,%eax
|
shr $26,%rax
|
||||||
add %eax,h1
|
add %eax,h1
|
||||||
# h0 = h0 & 0x3ffffff
|
# h0 = h0 & 0x3ffffff
|
||||||
andl $0x3ffffff,%ebx
|
andl $0x3ffffff,%ebx
|
||||||
|
|||||||
@@ -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] =
|
static const u64 amd_perfmon_event_map[PERF_COUNT_HW_MAX] =
|
||||||
{
|
{
|
||||||
[PERF_COUNT_HW_CPU_CYCLES] = 0x0076,
|
[PERF_COUNT_HW_CPU_CYCLES] = 0x0076,
|
||||||
[PERF_COUNT_HW_INSTRUCTIONS] = 0x00c0,
|
[PERF_COUNT_HW_INSTRUCTIONS] = 0x00c0,
|
||||||
[PERF_COUNT_HW_CACHE_REFERENCES] = 0x077d,
|
[PERF_COUNT_HW_CACHE_REFERENCES] = 0x077d,
|
||||||
[PERF_COUNT_HW_CACHE_MISSES] = 0x077e,
|
[PERF_COUNT_HW_CACHE_MISSES] = 0x077e,
|
||||||
[PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = 0x00c2,
|
[PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = 0x00c2,
|
||||||
[PERF_COUNT_HW_BRANCH_MISSES] = 0x00c3,
|
[PERF_COUNT_HW_BRANCH_MISSES] = 0x00c3,
|
||||||
[PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] = 0x00d0, /* "Decoder empty" event */
|
[PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] = 0x00d0, /* "Decoder empty" event */
|
||||||
[PERF_COUNT_HW_STALLED_CYCLES_BACKEND] = 0x00d1, /* "Dispatch stalls" 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)
|
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];
|
return amd_perfmon_event_map[hw_event];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3001,7 +3001,7 @@ static unsigned long intel_pmu_free_running_flags(struct perf_event *event)
|
|||||||
flags &= ~PERF_SAMPLE_TIME;
|
flags &= ~PERF_SAMPLE_TIME;
|
||||||
if (!event->attr.exclude_kernel)
|
if (!event->attr.exclude_kernel)
|
||||||
flags &= ~PERF_SAMPLE_REGS_USER;
|
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);
|
flags &= ~(PERF_SAMPLE_REGS_USER | PERF_SAMPLE_REGS_INTR);
|
||||||
return flags;
|
return flags;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,25 +95,25 @@ struct amd_nb {
|
|||||||
PERF_SAMPLE_TRANSACTION | PERF_SAMPLE_PHYS_ADDR | \
|
PERF_SAMPLE_TRANSACTION | PERF_SAMPLE_PHYS_ADDR | \
|
||||||
PERF_SAMPLE_REGS_INTR | PERF_SAMPLE_REGS_USER)
|
PERF_SAMPLE_REGS_INTR | PERF_SAMPLE_REGS_USER)
|
||||||
|
|
||||||
#define PEBS_REGS \
|
#define PEBS_GP_REGS \
|
||||||
(PERF_REG_X86_AX | \
|
((1ULL << PERF_REG_X86_AX) | \
|
||||||
PERF_REG_X86_BX | \
|
(1ULL << PERF_REG_X86_BX) | \
|
||||||
PERF_REG_X86_CX | \
|
(1ULL << PERF_REG_X86_CX) | \
|
||||||
PERF_REG_X86_DX | \
|
(1ULL << PERF_REG_X86_DX) | \
|
||||||
PERF_REG_X86_DI | \
|
(1ULL << PERF_REG_X86_DI) | \
|
||||||
PERF_REG_X86_SI | \
|
(1ULL << PERF_REG_X86_SI) | \
|
||||||
PERF_REG_X86_SP | \
|
(1ULL << PERF_REG_X86_SP) | \
|
||||||
PERF_REG_X86_BP | \
|
(1ULL << PERF_REG_X86_BP) | \
|
||||||
PERF_REG_X86_IP | \
|
(1ULL << PERF_REG_X86_IP) | \
|
||||||
PERF_REG_X86_FLAGS | \
|
(1ULL << PERF_REG_X86_FLAGS) | \
|
||||||
PERF_REG_X86_R8 | \
|
(1ULL << PERF_REG_X86_R8) | \
|
||||||
PERF_REG_X86_R9 | \
|
(1ULL << PERF_REG_X86_R9) | \
|
||||||
PERF_REG_X86_R10 | \
|
(1ULL << PERF_REG_X86_R10) | \
|
||||||
PERF_REG_X86_R11 | \
|
(1ULL << PERF_REG_X86_R11) | \
|
||||||
PERF_REG_X86_R12 | \
|
(1ULL << PERF_REG_X86_R12) | \
|
||||||
PERF_REG_X86_R13 | \
|
(1ULL << PERF_REG_X86_R13) | \
|
||||||
PERF_REG_X86_R14 | \
|
(1ULL << PERF_REG_X86_R14) | \
|
||||||
PERF_REG_X86_R15)
|
(1ULL << PERF_REG_X86_R15))
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Per register state.
|
* Per register state.
|
||||||
|
|||||||
@@ -271,7 +271,7 @@ static const struct {
|
|||||||
const char *option;
|
const char *option;
|
||||||
enum spectre_v2_user_cmd cmd;
|
enum spectre_v2_user_cmd cmd;
|
||||||
bool secure;
|
bool secure;
|
||||||
} v2_user_options[] __initdata = {
|
} v2_user_options[] __initconst = {
|
||||||
{ "auto", SPECTRE_V2_USER_CMD_AUTO, false },
|
{ "auto", SPECTRE_V2_USER_CMD_AUTO, false },
|
||||||
{ "off", SPECTRE_V2_USER_CMD_NONE, false },
|
{ "off", SPECTRE_V2_USER_CMD_NONE, false },
|
||||||
{ "on", SPECTRE_V2_USER_CMD_FORCE, true },
|
{ "on", SPECTRE_V2_USER_CMD_FORCE, true },
|
||||||
@@ -406,7 +406,7 @@ static const struct {
|
|||||||
const char *option;
|
const char *option;
|
||||||
enum spectre_v2_mitigation_cmd cmd;
|
enum spectre_v2_mitigation_cmd cmd;
|
||||||
bool secure;
|
bool secure;
|
||||||
} mitigation_options[] __initdata = {
|
} mitigation_options[] __initconst = {
|
||||||
{ "off", SPECTRE_V2_CMD_NONE, false },
|
{ "off", SPECTRE_V2_CMD_NONE, false },
|
||||||
{ "on", SPECTRE_V2_CMD_FORCE, true },
|
{ "on", SPECTRE_V2_CMD_FORCE, true },
|
||||||
{ "retpoline", SPECTRE_V2_CMD_RETPOLINE, false },
|
{ "retpoline", SPECTRE_V2_CMD_RETPOLINE, false },
|
||||||
@@ -642,7 +642,7 @@ static const char * const ssb_strings[] = {
|
|||||||
static const struct {
|
static const struct {
|
||||||
const char *option;
|
const char *option;
|
||||||
enum ssb_mitigation_cmd cmd;
|
enum ssb_mitigation_cmd cmd;
|
||||||
} ssb_mitigation_options[] __initdata = {
|
} ssb_mitigation_options[] __initconst = {
|
||||||
{ "auto", SPEC_STORE_BYPASS_CMD_AUTO }, /* Platform decides */
|
{ "auto", SPEC_STORE_BYPASS_CMD_AUTO }, /* Platform decides */
|
||||||
{ "on", SPEC_STORE_BYPASS_CMD_ON }, /* Disable Speculative Store Bypass */
|
{ "on", SPEC_STORE_BYPASS_CMD_ON }, /* Disable Speculative Store Bypass */
|
||||||
{ "off", SPEC_STORE_BYPASS_CMD_NONE }, /* Don't touch Speculative Store Bypass */
|
{ "off", SPEC_STORE_BYPASS_CMD_NONE }, /* Don't touch Speculative Store Bypass */
|
||||||
|
|||||||
@@ -553,6 +553,7 @@ void arch_prepare_kretprobe(struct kretprobe_instance *ri, struct pt_regs *regs)
|
|||||||
unsigned long *sara = stack_addr(regs);
|
unsigned long *sara = stack_addr(regs);
|
||||||
|
|
||||||
ri->ret_addr = (kprobe_opcode_t *) *sara;
|
ri->ret_addr = (kprobe_opcode_t *) *sara;
|
||||||
|
ri->fp = sara;
|
||||||
|
|
||||||
/* Replace the return addr with trampoline addr */
|
/* Replace the return addr with trampoline addr */
|
||||||
*sara = (unsigned long) &kretprobe_trampoline;
|
*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 flags, orig_ret_address = 0;
|
||||||
unsigned long trampoline_address = (unsigned long)&kretprobe_trampoline;
|
unsigned long trampoline_address = (unsigned long)&kretprobe_trampoline;
|
||||||
kprobe_opcode_t *correct_ret_addr = NULL;
|
kprobe_opcode_t *correct_ret_addr = NULL;
|
||||||
|
void *frame_pointer;
|
||||||
|
bool skipped = false;
|
||||||
|
|
||||||
INIT_HLIST_HEAD(&empty_rp);
|
INIT_HLIST_HEAD(&empty_rp);
|
||||||
kretprobe_hash_lock(current, &head, &flags);
|
kretprobe_hash_lock(current, &head, &flags);
|
||||||
/* fixup registers */
|
/* fixup registers */
|
||||||
#ifdef CONFIG_X86_64
|
#ifdef CONFIG_X86_64
|
||||||
regs->cs = __KERNEL_CS;
|
regs->cs = __KERNEL_CS;
|
||||||
|
/* On x86-64, we use pt_regs->sp for return address holder. */
|
||||||
|
frame_pointer = ®s->sp;
|
||||||
#else
|
#else
|
||||||
regs->cs = __KERNEL_CS | get_kernel_rpl();
|
regs->cs = __KERNEL_CS | get_kernel_rpl();
|
||||||
regs->gs = 0;
|
regs->gs = 0;
|
||||||
|
/* On x86-32, we use pt_regs->flags for return address holder. */
|
||||||
|
frame_pointer = ®s->flags;
|
||||||
#endif
|
#endif
|
||||||
regs->ip = trampoline_address;
|
regs->ip = trampoline_address;
|
||||||
regs->orig_ax = ~0UL;
|
regs->orig_ax = ~0UL;
|
||||||
@@ -784,8 +791,25 @@ __visible __used void *trampoline_handler(struct pt_regs *regs)
|
|||||||
if (ri->task != current)
|
if (ri->task != current)
|
||||||
/* another task is sharing our hash bucket */
|
/* another task is sharing our hash bucket */
|
||||||
continue;
|
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;
|
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)
|
if (orig_ret_address != trampoline_address)
|
||||||
/*
|
/*
|
||||||
@@ -803,6 +827,8 @@ __visible __used void *trampoline_handler(struct pt_regs *regs)
|
|||||||
if (ri->task != current)
|
if (ri->task != current)
|
||||||
/* another task is sharing our hash bucket */
|
/* another task is sharing our hash bucket */
|
||||||
continue;
|
continue;
|
||||||
|
if (ri->fp != frame_pointer)
|
||||||
|
continue;
|
||||||
|
|
||||||
orig_ret_address = (unsigned long)ri->ret_addr;
|
orig_ret_address = (unsigned long)ri->ret_addr;
|
||||||
if (ri->rp && ri->rp->handler) {
|
if (ri->rp && ri->rp->handler) {
|
||||||
|
|||||||
@@ -465,10 +465,12 @@ static unsigned long speculation_ctrl_update_tif(struct task_struct *tsk)
|
|||||||
|
|
||||||
void speculation_ctrl_update(unsigned long tif)
|
void speculation_ctrl_update(unsigned long tif)
|
||||||
{
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
/* Forced update. Make sure all relevant TIF flags are different */
|
/* Forced update. Make sure all relevant TIF flags are different */
|
||||||
preempt_disable();
|
local_irq_save(flags);
|
||||||
__speculation_ctrl_update(~tif, tif);
|
__speculation_ctrl_update(~tif, tif);
|
||||||
preempt_enable();
|
local_irq_restore(flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Called from seccomp/prctl update */
|
/* Called from seccomp/prctl update */
|
||||||
|
|||||||
@@ -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
|
* CR0/CR3/CR4/EFER. It's all a bit more complicated if the vCPU
|
||||||
* supports long mode.
|
* supports long mode.
|
||||||
*/
|
*/
|
||||||
cr4 = ctxt->ops->get_cr(ctxt, 4);
|
|
||||||
if (emulator_has_longmode(ctxt)) {
|
if (emulator_has_longmode(ctxt)) {
|
||||||
struct desc_struct cs_desc;
|
struct desc_struct cs_desc;
|
||||||
|
|
||||||
/* Zero CR4.PCIDE before CR0.PG. */
|
/* 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);
|
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. */
|
/* A 32-bit code segment is required to clear EFER.LMA. */
|
||||||
memset(&cs_desc, 0, sizeof(cs_desc));
|
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)
|
if (cr0 & X86_CR0_PE)
|
||||||
ctxt->ops->set_cr(ctxt, 0, cr0 & ~(X86_CR0_PG | 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 (emulator_has_longmode(ctxt)) {
|
||||||
if (cr4 & X86_CR4_PAE)
|
/* Clear CR4.PAE before clearing EFER.LME. */
|
||||||
ctxt->ops->set_cr(ctxt, 4, cr4 & ~X86_CR4_PAE);
|
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. */
|
/* And finally go back to 32-bit mode. */
|
||||||
efer = 0;
|
efer = 0;
|
||||||
ctxt->ops->set_msr(ctxt, MSR_EFER, efer);
|
ctxt->ops->set_msr(ctxt, MSR_EFER, efer);
|
||||||
|
}
|
||||||
|
|
||||||
smbase = ctxt->ops->get_smbase(ctxt);
|
smbase = ctxt->ops->get_smbase(ctxt);
|
||||||
if (emulator_has_longmode(ctxt))
|
if (emulator_has_longmode(ctxt))
|
||||||
|
|||||||
@@ -2211,6 +2211,7 @@ static int pf_interception(struct vcpu_svm *svm)
|
|||||||
static int db_interception(struct vcpu_svm *svm)
|
static int db_interception(struct vcpu_svm *svm)
|
||||||
{
|
{
|
||||||
struct kvm_run *kvm_run = svm->vcpu.run;
|
struct kvm_run *kvm_run = svm->vcpu.run;
|
||||||
|
struct kvm_vcpu *vcpu = &svm->vcpu;
|
||||||
|
|
||||||
if (!(svm->vcpu.guest_debug &
|
if (!(svm->vcpu.guest_debug &
|
||||||
(KVM_GUESTDBG_SINGLESTEP | KVM_GUESTDBG_USE_HW_BP)) &&
|
(KVM_GUESTDBG_SINGLESTEP | KVM_GUESTDBG_USE_HW_BP)) &&
|
||||||
@@ -2221,6 +2222,8 @@ static int db_interception(struct vcpu_svm *svm)
|
|||||||
|
|
||||||
if (svm->nmi_singlestep) {
|
if (svm->nmi_singlestep) {
|
||||||
disable_nmi_singlestep(svm);
|
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 &
|
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);
|
kvm_lapic_reg_write(apic, APIC_ICR, icrl);
|
||||||
break;
|
break;
|
||||||
case AVIC_IPI_FAILURE_TARGET_NOT_RUNNING: {
|
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;
|
struct kvm_lapic *apic = svm->vcpu.arch.apic;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Update ICR high and low, then emulate sending IPI,
|
* At this point, we expect that the AVIC HW has already
|
||||||
* which is handled when writing APIC_ICR.
|
* 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_for_each_vcpu(i, vcpu, kvm) {
|
||||||
kvm_lapic_reg_write(apic, APIC_ICR, icrl);
|
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;
|
break;
|
||||||
}
|
}
|
||||||
case AVIC_IPI_FAILURE_INVALID_TARGET:
|
case AVIC_IPI_FAILURE_INVALID_TARGET:
|
||||||
|
|||||||
@@ -4660,7 +4660,49 @@ static const struct hash_testvec poly1305_tv_template[] = {
|
|||||||
.psize = 80,
|
.psize = 80,
|
||||||
.digest = "\x13\x00\x00\x00\x00\x00\x00\x00"
|
.digest = "\x13\x00\x00\x00\x00\x00\x00\x00"
|
||||||
"\x00\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",
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/* NHPoly1305 test vectors from https://github.com/google/adiantum */
|
/* NHPoly1305 test vectors from https://github.com/google/adiantum */
|
||||||
|
|||||||
@@ -69,6 +69,10 @@ static int i2c_atmel_send(struct tpm_chip *chip, u8 *buf, size_t len)
|
|||||||
if (status < 0)
|
if (status < 0)
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
|
/* The upper layer does not support incomplete sends. */
|
||||||
|
if (status != len)
|
||||||
|
return -E2BIG;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -138,7 +138,8 @@ static int crypto4xx_setkey_aes(struct crypto_ablkcipher *cipher,
|
|||||||
sa = (struct dynamic_sa_ctl *) ctx->sa_in;
|
sa = (struct dynamic_sa_ctl *) ctx->sa_in;
|
||||||
ctx->hash_final = 0;
|
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_LOAD_HASH_FROM_SA, SA_LOAD_IV_FROM_STATE,
|
||||||
SA_NO_HEADER_PROC, SA_HASH_ALG_NULL,
|
SA_NO_HEADER_PROC, SA_HASH_ALG_NULL,
|
||||||
SA_CIPHER_ALG_AES, SA_PAD_TYPE_ZERO,
|
SA_CIPHER_ALG_AES, SA_PAD_TYPE_ZERO,
|
||||||
|
|||||||
@@ -645,6 +645,15 @@ static u32 crypto4xx_ablkcipher_done(struct crypto4xx_device *dev,
|
|||||||
addr = dma_map_page(dev->core_dev->device, sg_page(dst),
|
addr = dma_map_page(dev->core_dev->device, sg_page(dst),
|
||||||
dst->offset, dst->length, DMA_FROM_DEVICE);
|
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);
|
crypto4xx_ret_sg_desc(dev, pd_uinfo);
|
||||||
if (ablk_req->base.complete != NULL)
|
if (ablk_req->base.complete != NULL)
|
||||||
ablk_req->base.complete(&ablk_req->base, 0);
|
ablk_req->base.complete(&ablk_req->base, 0);
|
||||||
|
|||||||
@@ -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"),
|
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "FlexBook edge11 - M-FBE11"),
|
||||||
},
|
},
|
||||||
.driver_data = (void *)&sipodev_desc
|
.driver_data = (void *)&sipodev_desc
|
||||||
}
|
},
|
||||||
|
{ } /* Terminate list */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1340,6 +1340,8 @@ static int kxcjk1013_resume(struct device *dev)
|
|||||||
|
|
||||||
mutex_lock(&data->mutex);
|
mutex_lock(&data->mutex);
|
||||||
ret = kxcjk1013_set_mode(data, OPERATION);
|
ret = kxcjk1013_set_mode(data, OPERATION);
|
||||||
|
if (ret == 0)
|
||||||
|
ret = kxcjk1013_set_range(data, data->range);
|
||||||
mutex_unlock(&data->mutex);
|
mutex_unlock(&data->mutex);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|||||||
@@ -121,6 +121,7 @@ static int ad_sd_read_reg_raw(struct ad_sigma_delta *sigma_delta,
|
|||||||
if (sigma_delta->info->has_registers) {
|
if (sigma_delta->info->has_registers) {
|
||||||
data[0] = reg << sigma_delta->info->addr_shift;
|
data[0] = reg << sigma_delta->info->addr_shift;
|
||||||
data[0] |= sigma_delta->info->read_mask;
|
data[0] |= sigma_delta->info->read_mask;
|
||||||
|
data[0] |= sigma_delta->comm;
|
||||||
spi_message_add_tail(&t[0], &m);
|
spi_message_add_tail(&t[0], &m);
|
||||||
}
|
}
|
||||||
spi_message_add_tail(&t[1], &m);
|
spi_message_add_tail(&t[1], &m);
|
||||||
|
|||||||
@@ -705,23 +705,29 @@ static int at91_adc_read_raw(struct iio_dev *idev,
|
|||||||
ret = wait_event_interruptible_timeout(st->wq_data_avail,
|
ret = wait_event_interruptible_timeout(st->wq_data_avail,
|
||||||
st->done,
|
st->done,
|
||||||
msecs_to_jiffies(1000));
|
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_writel(st, AT91_ADC_CHDR,
|
||||||
AT91_ADC_CH(chan->channel));
|
AT91_ADC_CH(chan->channel));
|
||||||
at91_adc_writel(st, AT91_ADC_IDR, BIT(chan->channel));
|
at91_adc_writel(st, AT91_ADC_IDR, BIT(chan->channel));
|
||||||
|
|
||||||
st->last_value = 0;
|
if (ret > 0) {
|
||||||
st->done = false;
|
/* 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);
|
mutex_unlock(&st->lock);
|
||||||
return IIO_VAL_INT;
|
return ret;
|
||||||
|
|
||||||
case IIO_CHAN_INFO_SCALE:
|
case IIO_CHAN_INFO_SCALE:
|
||||||
*val = st->vref_mv;
|
*val = st->vref_mv;
|
||||||
|
|||||||
@@ -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
|
* Do not use IIO_DEGREE_TO_RAD to avoid precision
|
||||||
* loss. Round to the nearest integer.
|
* loss. Round to the nearest integer.
|
||||||
*/
|
*/
|
||||||
*val = div_s64(val64 * 314159 + 9000000ULL, 1000);
|
*val = 0;
|
||||||
*val2 = 18000 << (CROS_EC_SENSOR_BITS - 1);
|
*val2 = div_s64(val64 * 3141592653ULL,
|
||||||
ret = IIO_VAL_FRACTIONAL;
|
180 << (CROS_EC_SENSOR_BITS - 1));
|
||||||
|
ret = IIO_VAL_INT_PLUS_NANO;
|
||||||
break;
|
break;
|
||||||
case MOTIONSENSE_TYPE_MAG:
|
case MOTIONSENSE_TYPE_MAG:
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -98,6 +98,7 @@ static ssize_t mcp4725_store_eeprom(struct device *dev,
|
|||||||
|
|
||||||
inoutbuf[0] = 0x60; /* write EEPROM */
|
inoutbuf[0] = 0x60; /* write EEPROM */
|
||||||
inoutbuf[0] |= data->ref_mode << 3;
|
inoutbuf[0] |= data->ref_mode << 3;
|
||||||
|
inoutbuf[0] |= data->powerdown ? ((data->powerdown_mode + 1) << 1) : 0;
|
||||||
inoutbuf[1] = data->dac_value >> 4;
|
inoutbuf[1] = data->dac_value >> 4;
|
||||||
inoutbuf[2] = (data->dac_value & 0xf) << 4;
|
inoutbuf[2] = (data->dac_value & 0xf) << 4;
|
||||||
|
|
||||||
|
|||||||
@@ -583,11 +583,10 @@ static int bmg160_read_raw(struct iio_dev *indio_dev,
|
|||||||
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
|
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
|
||||||
return bmg160_get_filter(data, val);
|
return bmg160_get_filter(data, val);
|
||||||
case IIO_CHAN_INFO_SCALE:
|
case IIO_CHAN_INFO_SCALE:
|
||||||
*val = 0;
|
|
||||||
switch (chan->type) {
|
switch (chan->type) {
|
||||||
case IIO_TEMP:
|
case IIO_TEMP:
|
||||||
*val2 = 500000;
|
*val = 500;
|
||||||
return IIO_VAL_INT_PLUS_MICRO;
|
return IIO_VAL_INT;
|
||||||
case IIO_ANGL_VEL:
|
case IIO_ANGL_VEL:
|
||||||
{
|
{
|
||||||
int i;
|
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) {
|
for (i = 0; i < ARRAY_SIZE(bmg160_scale_table); ++i) {
|
||||||
if (bmg160_scale_table[i].dps_range ==
|
if (bmg160_scale_table[i].dps_range ==
|
||||||
data->dps_range) {
|
data->dps_range) {
|
||||||
|
*val = 0;
|
||||||
*val2 = bmg160_scale_table[i].scale;
|
*val2 = bmg160_scale_table[i].scale;
|
||||||
return IIO_VAL_INT_PLUS_MICRO;
|
return IIO_VAL_INT_PLUS_MICRO;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,8 @@
|
|||||||
|
|
||||||
#include "mpu3050.h"
|
#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
|
* 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;
|
goto err_power_down;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (val != MPU3050_CHIP_ID) {
|
if ((val & MPU3050_CHIP_ID_MASK) != MPU3050_CHIP_ID) {
|
||||||
dev_err(dev, "unsupported chip id %02x\n", (u8)val);
|
dev_err(dev, "unsupported chip id %02x\n",
|
||||||
|
(u8)(val & MPU3050_CHIP_ID_MASK));
|
||||||
ret = -ENODEV;
|
ret = -ENODEV;
|
||||||
goto err_power_down;
|
goto err_power_down;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -320,9 +320,8 @@ static int iio_scan_mask_set(struct iio_dev *indio_dev,
|
|||||||
const unsigned long *mask;
|
const unsigned long *mask;
|
||||||
unsigned long *trialmask;
|
unsigned long *trialmask;
|
||||||
|
|
||||||
trialmask = kmalloc_array(BITS_TO_LONGS(indio_dev->masklength),
|
trialmask = kcalloc(BITS_TO_LONGS(indio_dev->masklength),
|
||||||
sizeof(*trialmask),
|
sizeof(*trialmask), GFP_KERNEL);
|
||||||
GFP_KERNEL);
|
|
||||||
if (trialmask == NULL)
|
if (trialmask == NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
if (!indio_dev->masklength) {
|
if (!indio_dev->masklength) {
|
||||||
|
|||||||
@@ -1741,10 +1741,10 @@ EXPORT_SYMBOL(iio_device_register);
|
|||||||
**/
|
**/
|
||||||
void iio_device_unregister(struct iio_dev *indio_dev)
|
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);
|
cdev_device_del(&indio_dev->chrdev, &indio_dev->dev);
|
||||||
|
|
||||||
|
mutex_lock(&indio_dev->info_exist_lock);
|
||||||
|
|
||||||
iio_device_unregister_debugfs(indio_dev);
|
iio_device_unregister_debugfs(indio_dev);
|
||||||
|
|
||||||
iio_disable_all_buffers(indio_dev);
|
iio_disable_all_buffers(indio_dev);
|
||||||
|
|||||||
@@ -425,7 +425,7 @@ static u16 esdhc_readw_le(struct sdhci_host *host, int reg)
|
|||||||
val = readl(host->ioaddr + ESDHC_MIX_CTRL);
|
val = readl(host->ioaddr + ESDHC_MIX_CTRL);
|
||||||
else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING)
|
else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING)
|
||||||
/* the std tuning bits is in ACMD12_ERR for imx6sl */
|
/* 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)
|
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);
|
writel(new_val , host->ioaddr + ESDHC_MIX_CTRL);
|
||||||
} else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
|
} 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);
|
u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
|
||||||
if (val & SDHCI_CTRL_TUNED_CLK) {
|
if (val & SDHCI_CTRL_TUNED_CLK) {
|
||||||
v |= ESDHC_MIX_CTRL_SMPCLK_SEL;
|
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;
|
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);
|
writel(m, host->ioaddr + ESDHC_MIX_CTRL);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -937,9 +937,9 @@ static void esdhc_reset_tuning(struct sdhci_host *host)
|
|||||||
writel(ctrl, host->ioaddr + ESDHC_MIX_CTRL);
|
writel(ctrl, host->ioaddr + ESDHC_MIX_CTRL);
|
||||||
writel(0, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
|
writel(0, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
|
||||||
} else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
|
} 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;
|
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 */
|
/* clear tuning bits in case ROM has set it already */
|
||||||
writel(0x0, host->ioaddr + ESDHC_MIX_CTRL);
|
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);
|
writel(0x0, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -82,8 +82,8 @@ void sdhci_dumpregs(struct sdhci_host *host)
|
|||||||
SDHCI_DUMP("Int enab: 0x%08x | Sig enab: 0x%08x\n",
|
SDHCI_DUMP("Int enab: 0x%08x | Sig enab: 0x%08x\n",
|
||||||
sdhci_readl(host, SDHCI_INT_ENABLE),
|
sdhci_readl(host, SDHCI_INT_ENABLE),
|
||||||
sdhci_readl(host, SDHCI_SIGNAL_ENABLE));
|
sdhci_readl(host, SDHCI_SIGNAL_ENABLE));
|
||||||
SDHCI_DUMP("AC12 err: 0x%08x | Slot int: 0x%08x\n",
|
SDHCI_DUMP("ACmd stat: 0x%08x | Slot int: 0x%08x\n",
|
||||||
sdhci_readw(host, SDHCI_ACMD12_ERR),
|
sdhci_readw(host, SDHCI_AUTO_CMD_STATUS),
|
||||||
sdhci_readw(host, SDHCI_SLOT_INT_STATUS));
|
sdhci_readw(host, SDHCI_SLOT_INT_STATUS));
|
||||||
SDHCI_DUMP("Caps: 0x%08x | Caps_1: 0x%08x\n",
|
SDHCI_DUMP("Caps: 0x%08x | Caps_1: 0x%08x\n",
|
||||||
sdhci_readl(host, SDHCI_CAPABILITIES),
|
sdhci_readl(host, SDHCI_CAPABILITIES),
|
||||||
@@ -790,6 +790,11 @@ static void sdhci_set_transfer_irqs(struct sdhci_host *host)
|
|||||||
else
|
else
|
||||||
host->ier = (host->ier & ~dma_irqs) | pio_irqs;
|
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_INT_ENABLE);
|
||||||
sdhci_writel(host, host->ier, SDHCI_SIGNAL_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) &&
|
return (!(host->flags & SDHCI_DEVICE_DEAD) &&
|
||||||
((mrq->cmd && mrq->cmd->error) ||
|
((mrq->cmd && mrq->cmd->error) ||
|
||||||
(mrq->sbc && mrq->sbc->error) ||
|
(mrq->sbc && mrq->sbc->error) ||
|
||||||
(mrq->data && ((mrq->data->error && !mrq->data->stop) ||
|
(mrq->data && mrq->data->stop && mrq->data->stop->error) ||
|
||||||
(mrq->data->stop && mrq->data->stop->error))) ||
|
|
||||||
(host->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST)));
|
(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 = NULL;
|
||||||
host->data_cmd = 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)) ==
|
if ((host->flags & (SDHCI_REQ_USE_DMA | SDHCI_USE_ADMA)) ==
|
||||||
(SDHCI_REQ_USE_DMA | SDHCI_USE_ADMA))
|
(SDHCI_REQ_USE_DMA | SDHCI_USE_ADMA))
|
||||||
sdhci_adma_table_post(host, data);
|
sdhci_adma_table_post(host, data);
|
||||||
@@ -1079,17 +1093,6 @@ static void sdhci_finish_data(struct sdhci_host *host)
|
|||||||
if (data->stop &&
|
if (data->stop &&
|
||||||
(data->error ||
|
(data->error ||
|
||||||
!data->mrq->sbc)) {
|
!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
|
* 'cap_cmd_during_tfr' request must not use the command line
|
||||||
* after mmc_command_done() has been called. It is upper layer's
|
* 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) {
|
if (!host->cmd) {
|
||||||
/*
|
/*
|
||||||
* SDHCI recovers from errors by resetting the cmd and data
|
* 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
|
else
|
||||||
host->cmd->error = -EILSEQ;
|
host->cmd->error = -EILSEQ;
|
||||||
|
|
||||||
/*
|
/* Treat data command CRC error the same as data CRC error */
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
if (host->cmd->data &&
|
if (host->cmd->data &&
|
||||||
(intmask & (SDHCI_INT_CRC | SDHCI_INT_TIMEOUT)) ==
|
(intmask & (SDHCI_INT_CRC | SDHCI_INT_TIMEOUT)) ==
|
||||||
SDHCI_INT_CRC) {
|
SDHCI_INT_CRC) {
|
||||||
host->cmd = NULL;
|
host->cmd = NULL;
|
||||||
|
*intmask_p |= SDHCI_INT_DATA_CRC;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2604,6 +2614,21 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask)
|
|||||||
return;
|
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)
|
if (intmask & SDHCI_INT_RESPONSE)
|
||||||
sdhci_finish_command(host);
|
sdhci_finish_command(host);
|
||||||
}
|
}
|
||||||
@@ -2824,7 +2849,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (intmask & SDHCI_INT_CMD_MASK)
|
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)
|
if (intmask & SDHCI_INT_DATA_MASK)
|
||||||
sdhci_data_irq(host, intmask & SDHCI_INT_DATA_MASK);
|
sdhci_data_irq(host, intmask & SDHCI_INT_DATA_MASK);
|
||||||
|
|||||||
@@ -144,14 +144,15 @@
|
|||||||
#define SDHCI_INT_DATA_CRC 0x00200000
|
#define SDHCI_INT_DATA_CRC 0x00200000
|
||||||
#define SDHCI_INT_DATA_END_BIT 0x00400000
|
#define SDHCI_INT_DATA_END_BIT 0x00400000
|
||||||
#define SDHCI_INT_BUS_POWER 0x00800000
|
#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_ADMA_ERROR 0x02000000
|
||||||
|
|
||||||
#define SDHCI_INT_NORMAL_MASK 0x00007FFF
|
#define SDHCI_INT_NORMAL_MASK 0x00007FFF
|
||||||
#define SDHCI_INT_ERROR_MASK 0xFFFF8000
|
#define SDHCI_INT_ERROR_MASK 0xFFFF8000
|
||||||
|
|
||||||
#define SDHCI_INT_CMD_MASK (SDHCI_INT_RESPONSE | SDHCI_INT_TIMEOUT | \
|
#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 | \
|
#define SDHCI_INT_DATA_MASK (SDHCI_INT_DATA_END | SDHCI_INT_DMA_END | \
|
||||||
SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | \
|
SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | \
|
||||||
SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_DATA_CRC | \
|
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_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_HOST_CONTROL2 0x3E
|
||||||
#define SDHCI_CTRL_UHS_MASK 0x0007
|
#define SDHCI_CTRL_UHS_MASK 0x0007
|
||||||
|
|||||||
@@ -3169,8 +3169,12 @@ static int bond_netdev_event(struct notifier_block *this,
|
|||||||
return NOTIFY_DONE;
|
return NOTIFY_DONE;
|
||||||
|
|
||||||
if (event_dev->flags & IFF_MASTER) {
|
if (event_dev->flags & IFF_MASTER) {
|
||||||
|
int ret;
|
||||||
|
|
||||||
netdev_dbg(event_dev, "IFF_MASTER\n");
|
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) {
|
if (event_dev->flags & IFF_SLAVE) {
|
||||||
|
|||||||
@@ -29,6 +29,13 @@
|
|||||||
#define DRV_NAME "thunder-nicvf"
|
#define DRV_NAME "thunder-nicvf"
|
||||||
#define DRV_VERSION "1.0"
|
#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 */
|
/* Supported devices */
|
||||||
static const struct pci_device_id nicvf_id_table[] = {
|
static const struct pci_device_id nicvf_id_table[] = {
|
||||||
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM,
|
{ 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);
|
struct nicvf *nic = netdev_priv(netdev);
|
||||||
int orig_mtu = netdev->mtu;
|
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;
|
netdev->mtu = new_mtu;
|
||||||
|
|
||||||
if (!netif_running(netdev))
|
if (!netif_running(netdev))
|
||||||
@@ -1702,8 +1718,10 @@ static int nicvf_xdp_setup(struct nicvf *nic, struct bpf_prog *prog)
|
|||||||
bool bpf_attached = false;
|
bool bpf_attached = false;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
/* For now just support only the usual MTU sized frames */
|
/* For now just support only the usual MTU sized frames,
|
||||||
if (prog && (dev->mtu > 1500)) {
|
* 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",
|
netdev_warn(dev, "Jumbo frames not yet supported with XDP, current MTU %d.\n",
|
||||||
dev->mtu);
|
dev->mtu);
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|||||||
@@ -1245,6 +1245,23 @@ static int team_port_add(struct team *team, struct net_device *port_dev)
|
|||||||
goto err_option_port_add;
|
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);
|
netif_addr_lock_bh(dev);
|
||||||
dev_uc_sync_multiple(port_dev, dev);
|
dev_uc_sync_multiple(port_dev, dev);
|
||||||
dev_mc_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;
|
return 0;
|
||||||
|
|
||||||
|
err_set_slave_promisc:
|
||||||
|
__team_option_inst_del_port(team, port);
|
||||||
|
|
||||||
err_option_port_add:
|
err_option_port_add:
|
||||||
team_upper_dev_unlink(team, port);
|
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);
|
team_port_disable(team, port);
|
||||||
list_del_rcu(&port->list);
|
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);
|
team_upper_dev_unlink(team, port);
|
||||||
netdev_rx_handler_unregister(port_dev);
|
netdev_rx_handler_unregister(port_dev);
|
||||||
team_port_disable_netpoll(port);
|
team_port_disable_netpoll(port);
|
||||||
|
|||||||
@@ -672,7 +672,6 @@ enum rt2x00_state_flags {
|
|||||||
CONFIG_CHANNEL_HT40,
|
CONFIG_CHANNEL_HT40,
|
||||||
CONFIG_POWERSAVING,
|
CONFIG_POWERSAVING,
|
||||||
CONFIG_HT_DISABLED,
|
CONFIG_HT_DISABLED,
|
||||||
CONFIG_QOS_DISABLED,
|
|
||||||
CONFIG_MONITORING,
|
CONFIG_MONITORING,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -670,18 +670,8 @@ void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw,
|
|||||||
rt2x00dev->intf_associated--;
|
rt2x00dev->intf_associated--;
|
||||||
|
|
||||||
rt2x00leds_led_assoc(rt2x00dev, !!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
|
* When the erp information has changed, we should perform
|
||||||
* additional configuration steps. For all other changes we are done.
|
* additional configuration steps. For all other changes we are done.
|
||||||
|
|||||||
@@ -200,15 +200,18 @@ static void rt2x00queue_create_tx_descriptor_seq(struct rt2x00_dev *rt2x00dev,
|
|||||||
if (!rt2x00_has_cap_flag(rt2x00dev, REQUIRE_SW_SEQNO)) {
|
if (!rt2x00_has_cap_flag(rt2x00dev, REQUIRE_SW_SEQNO)) {
|
||||||
/*
|
/*
|
||||||
* rt2800 has a H/W (or F/W) bug, device incorrectly increase
|
* rt2800 has a H/W (or F/W) bug, device incorrectly increase
|
||||||
* seqno on retransmited data (non-QOS) frames. To workaround
|
* seqno on retransmitted data (non-QOS) and management frames.
|
||||||
* the problem let's generate seqno in software if QOS is
|
* To workaround the problem let's generate seqno in software.
|
||||||
* disabled.
|
* 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))
|
if (ieee80211_is_beacon(hdr->frame_control)) {
|
||||||
__clear_bit(ENTRY_TXD_GENERATE_SEQ, &txdesc->flags);
|
__set_bit(ENTRY_TXD_GENERATE_SEQ, &txdesc->flags);
|
||||||
else
|
|
||||||
/* H/W will generate sequence number */
|
/* H/W will generate sequence number */
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
__clear_bit(ENTRY_TXD_GENERATE_SEQ, &txdesc->flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -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_DBG(rdata, "Received LOGO request while in state %s\n",
|
||||||
fc_rport_state(rdata));
|
fc_rport_state(rdata));
|
||||||
|
|
||||||
rdata->flags &= ~FC_RP_STARTED;
|
|
||||||
fc_rport_enter_delete(rdata, RPORT_EV_STOP);
|
fc_rport_enter_delete(rdata, RPORT_EV_STOP);
|
||||||
mutex_unlock(&rdata->rp_mutex);
|
mutex_unlock(&rdata->rp_mutex);
|
||||||
kref_put(&rdata->kref, fc_rport_destroy);
|
kref_put(&rdata->kref, fc_rport_destroy);
|
||||||
|
|||||||
@@ -2050,8 +2050,12 @@ out:
|
|||||||
blk_mq_delay_run_hw_queue(hctx, SCSI_QUEUE_DELAY);
|
blk_mq_delay_run_hw_queue(hctx, SCSI_QUEUE_DELAY);
|
||||||
break;
|
break;
|
||||||
default:
|
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
|
* we hit an error, as we will never see this command
|
||||||
* again.
|
* again.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -472,10 +472,8 @@ static int ni6501_alloc_usb_buffers(struct comedi_device *dev)
|
|||||||
|
|
||||||
size = usb_endpoint_maxp(devpriv->ep_tx);
|
size = usb_endpoint_maxp(devpriv->ep_tx);
|
||||||
devpriv->usb_tx_buf = kzalloc(size, GFP_KERNEL);
|
devpriv->usb_tx_buf = kzalloc(size, GFP_KERNEL);
|
||||||
if (!devpriv->usb_tx_buf) {
|
if (!devpriv->usb_tx_buf)
|
||||||
kfree(devpriv->usb_rx_buf);
|
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -527,6 +525,9 @@ static int ni6501_auto_attach(struct comedi_device *dev,
|
|||||||
if (!devpriv)
|
if (!devpriv)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
mutex_init(&devpriv->mut);
|
||||||
|
usb_set_intfdata(intf, devpriv);
|
||||||
|
|
||||||
ret = ni6501_find_endpoints(dev);
|
ret = ni6501_find_endpoints(dev);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
@@ -535,9 +536,6 @@ static int ni6501_auto_attach(struct comedi_device *dev,
|
|||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
mutex_init(&devpriv->mut);
|
|
||||||
usb_set_intfdata(intf, devpriv);
|
|
||||||
|
|
||||||
ret = comedi_alloc_subdevices(dev, 2);
|
ret = comedi_alloc_subdevices(dev, 2);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|||||||
@@ -691,10 +691,8 @@ static int vmk80xx_alloc_usb_buffers(struct comedi_device *dev)
|
|||||||
|
|
||||||
size = usb_endpoint_maxp(devpriv->ep_tx);
|
size = usb_endpoint_maxp(devpriv->ep_tx);
|
||||||
devpriv->usb_tx_buf = kzalloc(size, GFP_KERNEL);
|
devpriv->usb_tx_buf = kzalloc(size, GFP_KERNEL);
|
||||||
if (!devpriv->usb_tx_buf) {
|
if (!devpriv->usb_tx_buf)
|
||||||
kfree(devpriv->usb_rx_buf);
|
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -809,6 +807,8 @@ static int vmk80xx_auto_attach(struct comedi_device *dev,
|
|||||||
|
|
||||||
devpriv->model = board->model;
|
devpriv->model = board->model;
|
||||||
|
|
||||||
|
sema_init(&devpriv->limit_sem, 8);
|
||||||
|
|
||||||
ret = vmk80xx_find_usb_endpoints(dev);
|
ret = vmk80xx_find_usb_endpoints(dev);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
@@ -817,8 +817,6 @@ static int vmk80xx_auto_attach(struct comedi_device *dev,
|
|||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
sema_init(&devpriv->limit_sem, 8);
|
|
||||||
|
|
||||||
usb_set_intfdata(intf, devpriv);
|
usb_set_intfdata(intf, devpriv);
|
||||||
|
|
||||||
if (devpriv->model == VMK8055_MODEL)
|
if (devpriv->model == VMK8055_MODEL)
|
||||||
|
|||||||
@@ -109,10 +109,10 @@
|
|||||||
#define AD7192_CH_AIN3 BIT(6) /* AIN3 - AINCOM */
|
#define AD7192_CH_AIN3 BIT(6) /* AIN3 - AINCOM */
|
||||||
#define AD7192_CH_AIN4 BIT(7) /* AIN4 - AINCOM */
|
#define AD7192_CH_AIN4 BIT(7) /* AIN4 - AINCOM */
|
||||||
|
|
||||||
#define AD7193_CH_AIN1P_AIN2M 0x000 /* AIN1(+) - AIN2(-) */
|
#define AD7193_CH_AIN1P_AIN2M 0x001 /* AIN1(+) - AIN2(-) */
|
||||||
#define AD7193_CH_AIN3P_AIN4M 0x001 /* AIN3(+) - AIN4(-) */
|
#define AD7193_CH_AIN3P_AIN4M 0x002 /* AIN3(+) - AIN4(-) */
|
||||||
#define AD7193_CH_AIN5P_AIN6M 0x002 /* AIN5(+) - AIN6(-) */
|
#define AD7193_CH_AIN5P_AIN6M 0x004 /* AIN5(+) - AIN6(-) */
|
||||||
#define AD7193_CH_AIN7P_AIN8M 0x004 /* AIN7(+) - AIN8(-) */
|
#define AD7193_CH_AIN7P_AIN8M 0x008 /* AIN7(+) - AIN8(-) */
|
||||||
#define AD7193_CH_TEMP 0x100 /* Temp senseor */
|
#define AD7193_CH_TEMP 0x100 /* Temp senseor */
|
||||||
#define AD7193_CH_AIN2P_AIN2M 0x200 /* AIN2(+) - AIN2(-) */
|
#define AD7193_CH_AIN2P_AIN2M 0x200 /* AIN2(+) - AIN2(-) */
|
||||||
#define AD7193_CH_AIN1 0x401 /* AIN1 - AINCOM */
|
#define AD7193_CH_AIN1 0x401 /* AIN1 - AINCOM */
|
||||||
|
|||||||
@@ -269,7 +269,7 @@ static IIO_DEV_ATTR_VPEAK(0644,
|
|||||||
static IIO_DEV_ATTR_IPEAK(0644,
|
static IIO_DEV_ATTR_IPEAK(0644,
|
||||||
ade7854_read_32bit,
|
ade7854_read_32bit,
|
||||||
ade7854_write_32bit,
|
ade7854_write_32bit,
|
||||||
ADE7854_VPEAK);
|
ADE7854_IPEAK);
|
||||||
static IIO_DEV_ATTR_APHCAL(0644,
|
static IIO_DEV_ATTR_APHCAL(0644,
|
||||||
ade7854_read_16bit,
|
ade7854_read_16bit,
|
||||||
ade7854_write_16bit,
|
ade7854_write_16bit,
|
||||||
|
|||||||
@@ -918,8 +918,12 @@ static int vhost_new_umem_range(struct vhost_umem *umem,
|
|||||||
u64 start, u64 size, u64 end,
|
u64 start, u64 size, u64 end,
|
||||||
u64 userspace_addr, int perm)
|
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)
|
if (!node)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
|||||||
@@ -1189,6 +1189,7 @@ cifsFileInfo_get_locked(struct cifsFileInfo *cifs_file)
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct cifsFileInfo *cifsFileInfo_get(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);
|
void cifsFileInfo_put(struct cifsFileInfo *cifs_file);
|
||||||
|
|
||||||
#define CIFS_CACHE_READ_FLG 1
|
#define CIFS_CACHE_READ_FLG 1
|
||||||
@@ -1693,6 +1694,7 @@ GLOBAL_EXTERN spinlock_t gidsidlock;
|
|||||||
#endif /* CONFIG_CIFS_ACL */
|
#endif /* CONFIG_CIFS_ACL */
|
||||||
|
|
||||||
void cifs_oplock_break(struct work_struct *work);
|
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 const struct slow_work_ops cifs_oplock_break_ops;
|
||||||
extern struct workqueue_struct *cifsiod_wq;
|
extern struct workqueue_struct *cifsiod_wq;
|
||||||
|
|||||||
@@ -358,12 +358,30 @@ cifsFileInfo_get(struct cifsFileInfo *cifs_file)
|
|||||||
return cifs_file;
|
return cifs_file;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Release a reference on the file private data. This may involve closing
|
* cifsFileInfo_put - release a reference of file priv data
|
||||||
* the filehandle out on the server. Must be called without holding
|
*
|
||||||
* tcon->open_file_lock and cifs_file->file_info_lock.
|
* Always potentially wait for oplock handler. See _cifsFileInfo_put().
|
||||||
*/
|
*/
|
||||||
void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
|
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 inode *inode = d_inode(cifs_file->dentry);
|
||||||
struct cifs_tcon *tcon = tlink_tcon(cifs_file->tlink);
|
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);
|
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) {
|
if (!tcon->need_reconnect && !cifs_file->invalidHandle) {
|
||||||
struct TCP_Server_Info *server = tcon->ses->server;
|
struct TCP_Server_Info *server = tcon->ses->server;
|
||||||
@@ -4136,6 +4155,7 @@ void cifs_oplock_break(struct work_struct *work)
|
|||||||
cinode);
|
cinode);
|
||||||
cifs_dbg(FYI, "Oplock release rc = %d\n", rc);
|
cifs_dbg(FYI, "Oplock release rc = %d\n", rc);
|
||||||
}
|
}
|
||||||
|
_cifsFileInfo_put(cfile, false /* do not wait for ourself */);
|
||||||
cifs_done_oplock_break(cinode);
|
cifs_done_oplock_break(cinode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -486,8 +486,7 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)
|
|||||||
CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2,
|
CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2,
|
||||||
&pCifsInode->flags);
|
&pCifsInode->flags);
|
||||||
|
|
||||||
queue_work(cifsoplockd_wq,
|
cifs_queue_oplock_break(netfile);
|
||||||
&netfile->oplock_break);
|
|
||||||
netfile->oplock_break_cancelled = false;
|
netfile->oplock_break_cancelled = false;
|
||||||
|
|
||||||
spin_unlock(&tcon->open_file_lock);
|
spin_unlock(&tcon->open_file_lock);
|
||||||
@@ -584,6 +583,28 @@ void cifs_put_writer(struct cifsInodeInfo *cinode)
|
|||||||
spin_unlock(&cinode->writers_lock);
|
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)
|
void cifs_done_oplock_break(struct cifsInodeInfo *cinode)
|
||||||
{
|
{
|
||||||
clear_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, &cinode->flags);
|
clear_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, &cinode->flags);
|
||||||
|
|||||||
@@ -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,
|
clear_bit(CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2,
|
||||||
&cinode->flags);
|
&cinode->flags);
|
||||||
|
|
||||||
queue_work(cifsoplockd_wq, &cfile->oplock_break);
|
cifs_queue_oplock_break(cfile);
|
||||||
kfree(lw);
|
kfree(lw);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -661,8 +661,8 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
|
|||||||
CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2,
|
CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2,
|
||||||
&cinode->flags);
|
&cinode->flags);
|
||||||
spin_unlock(&cfile->file_info_lock);
|
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(&tcon->open_file_lock);
|
||||||
spin_unlock(&cifs_tcp_ses_lock);
|
spin_unlock(&cifs_tcp_ses_lock);
|
||||||
|
|||||||
@@ -219,6 +219,27 @@ static inline struct page *dio_get_page(struct dio *dio,
|
|||||||
return dio->pages[sdio->head];
|
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
|
* dio_complete() - called when all DIO BIO I/O has been completed
|
||||||
* @offset: the byte offset in the file of the completed operation
|
* @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,
|
err = invalidate_inode_pages2_range(dio->inode->i_mapping,
|
||||||
offset >> PAGE_SHIFT,
|
offset >> PAGE_SHIFT,
|
||||||
(offset + ret - 1) >> 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))
|
if (!(dio->flags & DIO_SKIP_DIO_COUNT))
|
||||||
|
|||||||
12
fs/iomap.c
12
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,
|
err = invalidate_inode_pages2_range(inode->i_mapping,
|
||||||
offset >> PAGE_SHIFT,
|
offset >> PAGE_SHIFT,
|
||||||
(offset + dio->size - 1) >> 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));
|
inode_dio_end(file_inode(iocb->ki_filp));
|
||||||
@@ -1010,9 +1011,16 @@ iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter,
|
|||||||
if (ret)
|
if (ret)
|
||||||
goto out_free_dio;
|
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,
|
ret = invalidate_inode_pages2_range(mapping,
|
||||||
start >> PAGE_SHIFT, end >> PAGE_SHIFT);
|
start >> PAGE_SHIFT, end >> PAGE_SHIFT);
|
||||||
WARN_ON_ONCE(ret);
|
if (ret)
|
||||||
|
dio_warn_stale_pagecache(iocb->ki_filp);
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
|
||||||
if (iov_iter_rw(iter) == WRITE && !dio->wait_for_completion &&
|
if (iov_iter_rw(iter) == WRITE && !dio->wait_for_completion &&
|
||||||
|
|||||||
@@ -1222,6 +1222,24 @@ static ssize_t clear_refs_write(struct file *file, const char __user *buf,
|
|||||||
count = -EINTR;
|
count = -EINTR;
|
||||||
goto out_mm;
|
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) {
|
for (vma = mm->mmap; vma; vma = vma->vm_next) {
|
||||||
vma->vm_flags &= ~VM_SOFTDIRTY;
|
vma->vm_flags &= ~VM_SOFTDIRTY;
|
||||||
vma_set_page_prot(vma);
|
vma_set_page_prot(vma);
|
||||||
|
|||||||
@@ -627,6 +627,8 @@ static void userfaultfd_event_wait_completion(struct userfaultfd_ctx *ctx,
|
|||||||
|
|
||||||
/* the various vma->vm_userfaultfd_ctx still points to it */
|
/* the various vma->vm_userfaultfd_ctx still points to it */
|
||||||
down_write(&mm->mmap_sem);
|
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)
|
for (vma = mm->mmap; vma; vma = vma->vm_next)
|
||||||
if (vma->vm_userfaultfd_ctx.ctx == release_new_ctx) {
|
if (vma->vm_userfaultfd_ctx.ctx == release_new_ctx) {
|
||||||
vma->vm_userfaultfd_ctx = NULL_VM_UFFD_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.
|
* taking the mmap_sem for writing.
|
||||||
*/
|
*/
|
||||||
down_write(&mm->mmap_sem);
|
down_write(&mm->mmap_sem);
|
||||||
|
if (!mmget_still_valid(mm))
|
||||||
|
goto skip_mm;
|
||||||
prev = NULL;
|
prev = NULL;
|
||||||
for (vma = mm->mmap; vma; vma = vma->vm_next) {
|
for (vma = mm->mmap; vma; vma = vma->vm_next) {
|
||||||
cond_resched();
|
cond_resched();
|
||||||
@@ -890,6 +894,7 @@ static int userfaultfd_release(struct inode *inode, struct file *file)
|
|||||||
vma->vm_flags = new_flags;
|
vma->vm_flags = new_flags;
|
||||||
vma->vm_userfaultfd_ctx = NULL_VM_UFFD_CTX;
|
vma->vm_userfaultfd_ctx = NULL_VM_UFFD_CTX;
|
||||||
}
|
}
|
||||||
|
skip_mm:
|
||||||
up_write(&mm->mmap_sem);
|
up_write(&mm->mmap_sem);
|
||||||
mmput(mm);
|
mmput(mm);
|
||||||
wakeup:
|
wakeup:
|
||||||
@@ -1328,6 +1333,8 @@ static int userfaultfd_register(struct userfaultfd_ctx *ctx,
|
|||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
down_write(&mm->mmap_sem);
|
down_write(&mm->mmap_sem);
|
||||||
|
if (!mmget_still_valid(mm))
|
||||||
|
goto out_unlock;
|
||||||
vma = find_vma_prev(mm, start, &prev);
|
vma = find_vma_prev(mm, start, &prev);
|
||||||
if (!vma)
|
if (!vma)
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
@@ -1516,6 +1523,8 @@ static int userfaultfd_unregister(struct userfaultfd_ctx *ctx,
|
|||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
down_write(&mm->mmap_sem);
|
down_write(&mm->mmap_sem);
|
||||||
|
if (!mmget_still_valid(mm))
|
||||||
|
goto out_unlock;
|
||||||
vma = find_vma_prev(mm, start, &prev);
|
vma = find_vma_prev(mm, start, &prev);
|
||||||
if (!vma)
|
if (!vma)
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
|
|||||||
@@ -212,6 +212,7 @@ xfs_attr_set(
|
|||||||
int flags)
|
int flags)
|
||||||
{
|
{
|
||||||
struct xfs_mount *mp = dp->i_mount;
|
struct xfs_mount *mp = dp->i_mount;
|
||||||
|
struct xfs_buf *leaf_bp = NULL;
|
||||||
struct xfs_da_args args;
|
struct xfs_da_args args;
|
||||||
struct xfs_defer_ops dfops;
|
struct xfs_defer_ops dfops;
|
||||||
struct xfs_trans_res tres;
|
struct xfs_trans_res tres;
|
||||||
@@ -327,9 +328,16 @@ xfs_attr_set(
|
|||||||
* GROT: another possible req'mt for a double-split btree op.
|
* GROT: another possible req'mt for a double-split btree op.
|
||||||
*/
|
*/
|
||||||
xfs_defer_init(args.dfops, args.firstblock);
|
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)
|
if (error)
|
||||||
goto out_defer_cancel;
|
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);
|
xfs_defer_ijoin(args.dfops, dp);
|
||||||
error = xfs_defer_finish(&args.trans, args.dfops);
|
error = xfs_defer_finish(&args.trans, args.dfops);
|
||||||
if (error)
|
if (error)
|
||||||
@@ -337,13 +345,14 @@ xfs_attr_set(
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Commit the leaf transformation. We'll need another (linked)
|
* 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);
|
error = xfs_trans_roll_inode(&args.trans, dp);
|
||||||
if (error)
|
if (error)
|
||||||
goto out;
|
goto out;
|
||||||
|
xfs_trans_bjoin(args.trans, leaf_bp);
|
||||||
|
leaf_bp = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (xfs_bmap_one_block(dp, XFS_ATTR_FORK))
|
if (xfs_bmap_one_block(dp, XFS_ATTR_FORK))
|
||||||
@@ -374,8 +383,9 @@ xfs_attr_set(
|
|||||||
|
|
||||||
out_defer_cancel:
|
out_defer_cancel:
|
||||||
xfs_defer_cancel(&dfops);
|
xfs_defer_cancel(&dfops);
|
||||||
args.trans = NULL;
|
|
||||||
out:
|
out:
|
||||||
|
if (leaf_bp)
|
||||||
|
xfs_trans_brelse(args.trans, leaf_bp);
|
||||||
if (args.trans)
|
if (args.trans)
|
||||||
xfs_trans_cancel(args.trans);
|
xfs_trans_cancel(args.trans);
|
||||||
xfs_iunlock(dp, XFS_ILOCK_EXCL);
|
xfs_iunlock(dp, XFS_ILOCK_EXCL);
|
||||||
|
|||||||
@@ -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
|
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_inode_t *dp;
|
||||||
xfs_attr_shortform_t *sf;
|
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);
|
sfe = XFS_ATTR_SF_NEXTENTRY(sfe);
|
||||||
}
|
}
|
||||||
error = 0;
|
error = 0;
|
||||||
|
*leaf_bp = bp;
|
||||||
out:
|
out:
|
||||||
kmem_free(tmpbuffer);
|
kmem_free(tmpbuffer);
|
||||||
return error;
|
return error;
|
||||||
|
|||||||
@@ -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);
|
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_lookup(struct xfs_da_args *args);
|
||||||
int xfs_attr_shortform_getvalue(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_remove(struct xfs_da_args *args);
|
||||||
int xfs_attr_shortform_allfit(struct xfs_buf *bp, struct xfs_inode *dp);
|
int xfs_attr_shortform_allfit(struct xfs_buf *bp, struct xfs_inode *dp);
|
||||||
int xfs_attr_shortform_bytesfit(struct xfs_inode *dp, int bytes);
|
int xfs_attr_shortform_bytesfit(struct xfs_inode *dp, int bytes);
|
||||||
|
|||||||
@@ -249,6 +249,10 @@ xfs_defer_trans_roll(
|
|||||||
for (i = 0; i < XFS_DEFER_OPS_NR_INODES && dop->dop_inodes[i]; i++)
|
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);
|
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);
|
trace_xfs_defer_trans_roll((*tp)->t_mountp, dop);
|
||||||
|
|
||||||
/* Roll the transaction. */
|
/* 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++)
|
for (i = 0; i < XFS_DEFER_OPS_NR_INODES && dop->dop_inodes[i]; i++)
|
||||||
xfs_trans_ijoin(*tp, dop->dop_inodes[i], 0);
|
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;
|
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;
|
return -EFSCORRUPTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -493,9 +528,7 @@ xfs_defer_init(
|
|||||||
struct xfs_defer_ops *dop,
|
struct xfs_defer_ops *dop,
|
||||||
xfs_fsblock_t *fbp)
|
xfs_fsblock_t *fbp)
|
||||||
{
|
{
|
||||||
dop->dop_committed = false;
|
memset(dop, 0, sizeof(struct xfs_defer_ops));
|
||||||
dop->dop_low = false;
|
|
||||||
memset(&dop->dop_inodes, 0, sizeof(dop->dop_inodes));
|
|
||||||
*fbp = NULLFSBLOCK;
|
*fbp = NULLFSBLOCK;
|
||||||
INIT_LIST_HEAD(&dop->dop_intake);
|
INIT_LIST_HEAD(&dop->dop_intake);
|
||||||
INIT_LIST_HEAD(&dop->dop_pending);
|
INIT_LIST_HEAD(&dop->dop_pending);
|
||||||
|
|||||||
@@ -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_INODES 2 /* join up to two inodes */
|
||||||
|
#define XFS_DEFER_OPS_NR_BUFS 2 /* join up to two buffers */
|
||||||
|
|
||||||
struct xfs_defer_ops {
|
struct xfs_defer_ops {
|
||||||
bool dop_committed; /* did any trans commit? */
|
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_intake; /* unlogged pending work */
|
||||||
struct list_head dop_pending; /* logged 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_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,
|
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);
|
void xfs_defer_init(struct xfs_defer_ops *dop, xfs_fsblock_t *fbp);
|
||||||
bool xfs_defer_has_unfinished_work(struct xfs_defer_ops *dop);
|
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_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. */
|
/* Description of a deferred type. */
|
||||||
struct xfs_defer_op_type {
|
struct xfs_defer_op_type {
|
||||||
|
|||||||
@@ -2989,6 +2989,7 @@ enum {
|
|||||||
};
|
};
|
||||||
|
|
||||||
void dio_end_io(struct bio *bio);
|
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,
|
ssize_t __blockdev_direct_IO(struct kiocb *iocb, struct inode *inode,
|
||||||
struct block_device *bdev, struct iov_iter *iter,
|
struct block_device *bdev, struct iov_iter *iter,
|
||||||
|
|||||||
@@ -198,6 +198,7 @@ struct kretprobe_instance {
|
|||||||
struct kretprobe *rp;
|
struct kretprobe *rp;
|
||||||
kprobe_opcode_t *ret_addr;
|
kprobe_opcode_t *ret_addr;
|
||||||
struct task_struct *task;
|
struct task_struct *task;
|
||||||
|
void *fp;
|
||||||
char data[0];
|
char data[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
* mmget() - Pin the address space associated with a &struct mm_struct.
|
||||||
* @mm: The address space to pin.
|
* @mm: The address space to pin.
|
||||||
|
|||||||
@@ -77,8 +77,8 @@ struct inet_frag_queue {
|
|||||||
struct timer_list timer;
|
struct timer_list timer;
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
refcount_t refcnt;
|
refcount_t refcnt;
|
||||||
struct sk_buff *fragments; /* Used in IPv6. */
|
struct sk_buff *fragments; /* used in 6lopwpan IPv6. */
|
||||||
struct rb_root rb_fragments; /* Used in IPv4. */
|
struct rb_root rb_fragments; /* Used in IPv4/IPv6. */
|
||||||
struct sk_buff *fragments_tail;
|
struct sk_buff *fragments_tail;
|
||||||
struct sk_buff *last_run_head;
|
struct sk_buff *last_run_head;
|
||||||
ktime_t stamp;
|
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];
|
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
|
#endif
|
||||||
|
|||||||
@@ -512,35 +512,6 @@ static inline bool ipv6_prefix_equal(const struct in6_addr *addr1,
|
|||||||
}
|
}
|
||||||
#endif
|
#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)
|
static inline bool ipv6_addr_any(const struct in6_addr *a)
|
||||||
{
|
{
|
||||||
#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && BITS_PER_LONG == 64
|
#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && BITS_PER_LONG == 64
|
||||||
|
|||||||
111
include/net/ipv6_frag.h
Normal file
111
include/net/ipv6_frag.h
Normal file
@@ -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
|
||||||
@@ -703,7 +703,6 @@ static void unoptimize_kprobe(struct kprobe *p, bool force)
|
|||||||
static int reuse_unused_kprobe(struct kprobe *ap)
|
static int reuse_unused_kprobe(struct kprobe *ap)
|
||||||
{
|
{
|
||||||
struct optimized_kprobe *op;
|
struct optimized_kprobe *op;
|
||||||
int ret;
|
|
||||||
|
|
||||||
BUG_ON(!kprobe_unused(ap));
|
BUG_ON(!kprobe_unused(ap));
|
||||||
/*
|
/*
|
||||||
@@ -717,9 +716,8 @@ static int reuse_unused_kprobe(struct kprobe *ap)
|
|||||||
/* Enable the probe again */
|
/* Enable the probe again */
|
||||||
ap->flags &= ~KPROBE_FLAG_DISABLED;
|
ap->flags &= ~KPROBE_FLAG_DISABLED;
|
||||||
/* Optimize it again (remove from op->list) */
|
/* Optimize it again (remove from op->list) */
|
||||||
ret = kprobe_optready(ap);
|
if (!kprobe_optready(ap))
|
||||||
if (ret)
|
return -EINVAL;
|
||||||
return ret;
|
|
||||||
|
|
||||||
optimize_kprobe(ap);
|
optimize_kprobe(ap);
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -3650,9 +3650,6 @@ __lock_set_class(struct lockdep_map *lock, const char *name,
|
|||||||
unsigned int depth;
|
unsigned int depth;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (unlikely(!debug_locks))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
depth = curr->lockdep_depth;
|
depth = curr->lockdep_depth;
|
||||||
/*
|
/*
|
||||||
* This function is about (re)setting the class of a held lock,
|
* This function is about (re)setting the class of a held lock,
|
||||||
|
|||||||
@@ -4934,12 +4934,15 @@ static enum hrtimer_restart sched_cfs_slack_timer(struct hrtimer *timer)
|
|||||||
return HRTIMER_NORESTART;
|
return HRTIMER_NORESTART;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern const u64 max_cfs_quota_period;
|
||||||
|
|
||||||
static enum hrtimer_restart sched_cfs_period_timer(struct hrtimer *timer)
|
static enum hrtimer_restart sched_cfs_period_timer(struct hrtimer *timer)
|
||||||
{
|
{
|
||||||
struct cfs_bandwidth *cfs_b =
|
struct cfs_bandwidth *cfs_b =
|
||||||
container_of(timer, struct cfs_bandwidth, period_timer);
|
container_of(timer, struct cfs_bandwidth, period_timer);
|
||||||
int overrun;
|
int overrun;
|
||||||
int idle = 0;
|
int idle = 0;
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
raw_spin_lock(&cfs_b->lock);
|
raw_spin_lock(&cfs_b->lock);
|
||||||
for (;;) {
|
for (;;) {
|
||||||
@@ -4947,6 +4950,28 @@ static enum hrtimer_restart sched_cfs_period_timer(struct hrtimer *timer)
|
|||||||
if (!overrun)
|
if (!overrun)
|
||||||
break;
|
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);
|
idle = do_sched_cfs_period_timer(cfs_b, overrun);
|
||||||
}
|
}
|
||||||
if (idle)
|
if (idle)
|
||||||
|
|||||||
@@ -125,6 +125,7 @@ static int zero;
|
|||||||
static int __maybe_unused one = 1;
|
static int __maybe_unused one = 1;
|
||||||
static int __maybe_unused two = 2;
|
static int __maybe_unused two = 2;
|
||||||
static int __maybe_unused four = 4;
|
static int __maybe_unused four = 4;
|
||||||
|
static unsigned long zero_ul;
|
||||||
static unsigned long one_ul = 1;
|
static unsigned long one_ul = 1;
|
||||||
static unsigned long long_max = LONG_MAX;
|
static unsigned long long_max = LONG_MAX;
|
||||||
static int one_hundred = 100;
|
static int one_hundred = 100;
|
||||||
@@ -1735,7 +1736,7 @@ static struct ctl_table fs_table[] = {
|
|||||||
.maxlen = sizeof(files_stat.max_files),
|
.maxlen = sizeof(files_stat.max_files),
|
||||||
.mode = 0644,
|
.mode = 0644,
|
||||||
.proc_handler = proc_doulongvec_minmax,
|
.proc_handler = proc_doulongvec_minmax,
|
||||||
.extra1 = &zero,
|
.extra1 = &zero_ul,
|
||||||
.extra2 = &long_max,
|
.extra2 = &long_max,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
#include <linux/list.h>
|
#include <linux/list.h>
|
||||||
#include <linux/hash.h>
|
#include <linux/hash.h>
|
||||||
#include <linux/rcupdate.h>
|
#include <linux/rcupdate.h>
|
||||||
|
#include <linux/kprobes.h>
|
||||||
|
|
||||||
#include <trace/events/sched.h>
|
#include <trace/events/sched.h>
|
||||||
|
|
||||||
@@ -6036,7 +6037,7 @@ void ftrace_reset_array_ops(struct trace_array *tr)
|
|||||||
tr->ops->func = ftrace_stub;
|
tr->ops->func = ftrace_stub;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static nokprobe_inline void
|
||||||
__ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip,
|
__ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip,
|
||||||
struct ftrace_ops *ignored, struct pt_regs *regs)
|
struct ftrace_ops *ignored, struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
@@ -6099,12 +6100,14 @@ static void ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip,
|
|||||||
{
|
{
|
||||||
__ftrace_ops_list_func(ip, parent_ip, NULL, regs);
|
__ftrace_ops_list_func(ip, parent_ip, NULL, regs);
|
||||||
}
|
}
|
||||||
|
NOKPROBE_SYMBOL(ftrace_ops_list_func);
|
||||||
#else
|
#else
|
||||||
static void ftrace_ops_no_ops(unsigned long ip, unsigned long parent_ip,
|
static void ftrace_ops_no_ops(unsigned long ip, unsigned long parent_ip,
|
||||||
struct ftrace_ops *op, struct pt_regs *regs)
|
struct ftrace_ops *op, struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
__ftrace_ops_list_func(ip, parent_ip, NULL, NULL);
|
__ftrace_ops_list_func(ip, parent_ip, NULL, NULL);
|
||||||
}
|
}
|
||||||
|
NOKPROBE_SYMBOL(ftrace_ops_no_ops);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -6134,6 +6137,7 @@ static void ftrace_ops_assist_func(unsigned long ip, unsigned long parent_ip,
|
|||||||
preempt_enable_notrace();
|
preempt_enable_notrace();
|
||||||
trace_clear_recursion(bit);
|
trace_clear_recursion(bit);
|
||||||
}
|
}
|
||||||
|
NOKPROBE_SYMBOL(ftrace_ops_assist_func);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ftrace_ops_get_func - get the function a trampoline should call
|
* ftrace_ops_get_func - get the function a trampoline should call
|
||||||
|
|||||||
@@ -45,6 +45,7 @@
|
|||||||
#include <linux/moduleparam.h>
|
#include <linux/moduleparam.h>
|
||||||
#include <linux/pkeys.h>
|
#include <linux/pkeys.h>
|
||||||
#include <linux/oom.h>
|
#include <linux/oom.h>
|
||||||
|
#include <linux/sched/mm.h>
|
||||||
|
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
#include <asm/cacheflush.h>
|
#include <asm/cacheflush.h>
|
||||||
@@ -2457,7 +2458,8 @@ find_extend_vma(struct mm_struct *mm, unsigned long addr)
|
|||||||
vma = find_vma_prev(mm, addr, &prev);
|
vma = find_vma_prev(mm, addr, &prev);
|
||||||
if (vma && (vma->vm_start <= addr))
|
if (vma && (vma->vm_start <= addr))
|
||||||
return vma;
|
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;
|
return NULL;
|
||||||
if (prev->vm_flags & VM_LOCKED)
|
if (prev->vm_flags & VM_LOCKED)
|
||||||
populate_vma_page_range(prev, addr, prev->vm_end, NULL);
|
populate_vma_page_range(prev, addr, prev->vm_end, NULL);
|
||||||
@@ -2483,6 +2485,9 @@ find_extend_vma(struct mm_struct *mm, unsigned long addr)
|
|||||||
return vma;
|
return vma;
|
||||||
if (!(vma->vm_flags & VM_GROWSDOWN))
|
if (!(vma->vm_flags & VM_GROWSDOWN))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
/* don't alter vm_start if the coredump is running */
|
||||||
|
if (!mmget_still_valid(mm))
|
||||||
|
return NULL;
|
||||||
start = vma->vm_start;
|
start = vma->vm_start;
|
||||||
if (expand_stack(vma, addr))
|
if (expand_stack(vma, addr))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|||||||
@@ -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;
|
ai->groups[group].base_offset = areas[group] - base;
|
||||||
}
|
}
|
||||||
|
|
||||||
pr_info("Embedded %zu pages/cpu @%p s%zu r%zu d%zu u%zu\n",
|
pr_info("Embedded %zu pages/cpu s%zu r%zu d%zu u%zu\n",
|
||||||
PFN_DOWN(size_sum), base, ai->static_size, ai->reserved_size,
|
PFN_DOWN(size_sum), ai->static_size, ai->reserved_size,
|
||||||
ai->dyn_size, ai->unit_size);
|
ai->dyn_size, ai->unit_size);
|
||||||
|
|
||||||
rc = pcpu_setup_first_chunk(ai, base);
|
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 */
|
/* we're ready, commit */
|
||||||
pr_info("%d %s pages/cpu @%p s%zu r%zu d%zu\n",
|
pr_info("%d %s pages/cpu s%zu r%zu d%zu\n",
|
||||||
unit_pages, psize_str, vm.addr, ai->static_size,
|
unit_pages, psize_str, ai->static_size,
|
||||||
ai->reserved_size, ai->dyn_size);
|
ai->reserved_size, ai->dyn_size);
|
||||||
|
|
||||||
rc = pcpu_setup_first_chunk(ai, vm.addr);
|
rc = pcpu_setup_first_chunk(ai, vm.addr);
|
||||||
|
|||||||
@@ -1202,13 +1202,8 @@ const char * const vmstat_text[] = {
|
|||||||
#endif
|
#endif
|
||||||
#endif /* CONFIG_MEMORY_BALLOON */
|
#endif /* CONFIG_MEMORY_BALLOON */
|
||||||
#ifdef CONFIG_DEBUG_TLBFLUSH
|
#ifdef CONFIG_DEBUG_TLBFLUSH
|
||||||
#ifdef CONFIG_SMP
|
|
||||||
"nr_tlb_remote_flush",
|
"nr_tlb_remote_flush",
|
||||||
"nr_tlb_remote_flush_received",
|
"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_all",
|
||||||
"nr_tlb_local_flush_one",
|
"nr_tlb_local_flush_one",
|
||||||
#endif /* CONFIG_DEBUG_TLBFLUSH */
|
#endif /* CONFIG_DEBUG_TLBFLUSH */
|
||||||
|
|||||||
@@ -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)
|
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;
|
return -EINVAL;
|
||||||
vcc->proto_data = dev_lec[arg];
|
vcc->proto_data = dev_lec[arg];
|
||||||
return lec_mcast_make(netdev_priv(dev_lec[arg]), vcc);
|
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;
|
i = arg;
|
||||||
if (arg >= MAX_LEC_ITF)
|
if (arg >= MAX_LEC_ITF)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
i = array_index_nospec(arg, MAX_LEC_ITF);
|
||||||
if (!dev_lec[i]) {
|
if (!dev_lec[i]) {
|
||||||
int size;
|
int size;
|
||||||
|
|
||||||
|
|||||||
@@ -236,13 +236,10 @@ static void __br_handle_local_finish(struct sk_buff *skb)
|
|||||||
/* note: already called with rcu_read_lock */
|
/* note: already called with rcu_read_lock */
|
||||||
static int br_handle_local_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
|
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_handle_local_finish(skb);
|
||||||
|
|
||||||
BR_INPUT_SKB_CB(skb)->brdev = p->br->dev;
|
/* return 1 to signal the okfn() was called so it's ok to use the skb */
|
||||||
br_pass_frame_up(skb);
|
return 1;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -318,10 +315,18 @@ rx_handler_result_t br_handle_frame(struct sk_buff **pskb)
|
|||||||
goto forward;
|
goto forward;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Deliver packet to local host only */
|
/* The else clause should be hit when nf_hook():
|
||||||
NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN, dev_net(skb->dev),
|
* - returns < 0 (drop/error)
|
||||||
NULL, skb, skb->dev, NULL, br_handle_local_finish);
|
* - returns = 0 (stolen/nf_queue)
|
||||||
return RX_HANDLER_CONSUMED;
|
* 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:
|
forward:
|
||||||
|
|||||||
@@ -2119,7 +2119,8 @@ static void br_multicast_start_querier(struct net_bridge *br,
|
|||||||
|
|
||||||
__br_multicast_open(br, query);
|
__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 ||
|
if (port->state == BR_STATE_DISABLED ||
|
||||||
port->state == BR_STATE_BLOCKING)
|
port->state == BR_STATE_BLOCKING)
|
||||||
continue;
|
continue;
|
||||||
@@ -2131,6 +2132,7 @@ static void br_multicast_start_querier(struct net_bridge *br,
|
|||||||
br_multicast_enable(&port->ip6_own_query);
|
br_multicast_enable(&port->ip6_own_query);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
int br_multicast_toggle(struct net_bridge *br, unsigned long val)
|
int br_multicast_toggle(struct net_bridge *br, unsigned long val)
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
#include <net/ieee802154_netdev.h>
|
#include <net/ieee802154_netdev.h>
|
||||||
#include <net/6lowpan.h>
|
#include <net/6lowpan.h>
|
||||||
#include <net/ipv6.h>
|
#include <net/ipv6_frag.h>
|
||||||
#include <net/inet_frag.h>
|
#include <net/inet_frag.h>
|
||||||
|
|
||||||
#include "6lowpan_i.h"
|
#include "6lowpan_i.h"
|
||||||
|
|||||||
@@ -120,6 +120,7 @@ static int gue_udp_recv(struct sock *sk, struct sk_buff *skb)
|
|||||||
struct guehdr *guehdr;
|
struct guehdr *guehdr;
|
||||||
void *data;
|
void *data;
|
||||||
u16 doffset = 0;
|
u16 doffset = 0;
|
||||||
|
u8 proto_ctype;
|
||||||
|
|
||||||
if (!fou)
|
if (!fou)
|
||||||
return 1;
|
return 1;
|
||||||
@@ -211,13 +212,14 @@ static int gue_udp_recv(struct sock *sk, struct sk_buff *skb)
|
|||||||
if (unlikely(guehdr->control))
|
if (unlikely(guehdr->control))
|
||||||
return gue_control_message(skb, guehdr);
|
return gue_control_message(skb, guehdr);
|
||||||
|
|
||||||
|
proto_ctype = guehdr->proto_ctype;
|
||||||
__skb_pull(skb, sizeof(struct udphdr) + hdrlen);
|
__skb_pull(skb, sizeof(struct udphdr) + hdrlen);
|
||||||
skb_reset_transport_header(skb);
|
skb_reset_transport_header(skb);
|
||||||
|
|
||||||
if (iptunnel_pull_offloads(skb))
|
if (iptunnel_pull_offloads(skb))
|
||||||
goto drop;
|
goto drop;
|
||||||
|
|
||||||
return -guehdr->proto_ctype;
|
return -proto_ctype;
|
||||||
|
|
||||||
drop:
|
drop:
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
|
|||||||
@@ -24,6 +24,62 @@
|
|||||||
#include <net/sock.h>
|
#include <net/sock.h>
|
||||||
#include <net/inet_frag.h>
|
#include <net/inet_frag.h>
|
||||||
#include <net/inet_ecn.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
|
/* Given the OR values of all fragments, apply RFC 3168 5.3 requirements
|
||||||
* Value : 0xff if frame should be dropped.
|
* 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);
|
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)
|
void inet_frag_destroy(struct inet_frag_queue *q)
|
||||||
{
|
{
|
||||||
struct sk_buff *fp;
|
struct sk_buff *fp;
|
||||||
@@ -224,3 +302,218 @@ struct inet_frag_queue *inet_frag_find(struct netns_frags *nf, void *key)
|
|||||||
return fq;
|
return fq;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(inet_frag_find);
|
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);
|
||||||
|
|||||||
@@ -57,57 +57,6 @@
|
|||||||
*/
|
*/
|
||||||
static const char ip_frag_cache_name[] = "ip4-frags";
|
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. */
|
/* Describe an entry in the "incomplete datagrams" queue. */
|
||||||
struct ipq {
|
struct ipq {
|
||||||
struct inet_frag_queue q;
|
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
|
* pull the head out of the tree in order to be able to
|
||||||
* deal with head->dev.
|
* deal with head->dev.
|
||||||
*/
|
*/
|
||||||
if (qp->q.fragments) {
|
head = inet_frag_pull_head(&qp->q);
|
||||||
head = qp->q.fragments;
|
if (!head)
|
||||||
qp->q.fragments = head->next;
|
goto out;
|
||||||
} 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->dev = dev_get_by_index_rcu(net, qp->iif);
|
head->dev = dev_get_by_index_rcu(net, qp->iif);
|
||||||
if (!head->dev)
|
if (!head->dev)
|
||||||
goto out;
|
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)
|
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 net *net = container_of(qp->q.net, struct net, ipv4.frags);
|
||||||
struct rb_node **rbn, *parent;
|
int ihl, end, flags, offset;
|
||||||
struct sk_buff *skb1, *prev_tail;
|
struct sk_buff *prev_tail;
|
||||||
int ihl, end, skb1_run_end;
|
|
||||||
struct net_device *dev;
|
struct net_device *dev;
|
||||||
unsigned int fragsize;
|
unsigned int fragsize;
|
||||||
int flags, offset;
|
|
||||||
int err = -ENOENT;
|
int err = -ENOENT;
|
||||||
u8 ecn;
|
u8 ecn;
|
||||||
|
|
||||||
@@ -382,7 +311,7 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
|
|||||||
*/
|
*/
|
||||||
if (end < qp->q.len ||
|
if (end < qp->q.len ||
|
||||||
((qp->q.flags & INET_FRAG_LAST_IN) && 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.flags |= INET_FRAG_LAST_IN;
|
||||||
qp->q.len = end;
|
qp->q.len = end;
|
||||||
} else {
|
} else {
|
||||||
@@ -394,82 +323,33 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
|
|||||||
if (end > qp->q.len) {
|
if (end > qp->q.len) {
|
||||||
/* Some bits beyond end -> corruption. */
|
/* Some bits beyond end -> corruption. */
|
||||||
if (qp->q.flags & INET_FRAG_LAST_IN)
|
if (qp->q.flags & INET_FRAG_LAST_IN)
|
||||||
goto err;
|
goto discard_qp;
|
||||||
qp->q.len = end;
|
qp->q.len = end;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (end == offset)
|
if (end == offset)
|
||||||
goto err;
|
goto discard_qp;
|
||||||
|
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
if (!pskb_pull(skb, skb_network_offset(skb) + ihl))
|
if (!pskb_pull(skb, skb_network_offset(skb) + ihl))
|
||||||
goto err;
|
goto discard_qp;
|
||||||
|
|
||||||
err = pskb_trim_rcsum(skb, end - offset);
|
err = pskb_trim_rcsum(skb, end - offset);
|
||||||
if (err)
|
if (err)
|
||||||
goto err;
|
goto discard_qp;
|
||||||
|
|
||||||
/* Note : skb->rbnode and skb->dev share the same location. */
|
/* Note : skb->rbnode and skb->dev share the same location. */
|
||||||
dev = skb->dev;
|
dev = skb->dev;
|
||||||
/* Makes sure compiler wont do silly aliasing games */
|
/* Makes sure compiler wont do silly aliasing games */
|
||||||
barrier();
|
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;
|
prev_tail = qp->q.fragments_tail;
|
||||||
if (!prev_tail)
|
err = inet_frag_queue_insert(&qp->q, skb, offset, end);
|
||||||
ip4_frag_create_run(&qp->q, skb); /* First fragment. */
|
if (err)
|
||||||
else if (prev_tail->ip_defrag_offset + prev_tail->len < end) {
|
goto insert_error;
|
||||||
/* 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dev)
|
if (dev)
|
||||||
qp->iif = dev->ifindex;
|
qp->iif = dev->ifindex;
|
||||||
skb->ip_defrag_offset = offset;
|
|
||||||
|
|
||||||
qp->q.stamp = skb->tstamp;
|
qp->q.stamp = skb->tstamp;
|
||||||
qp->q.meat += skb->len;
|
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;
|
skb->_skb_refdst = 0UL;
|
||||||
err = ip_frag_reasm(qp, skb, prev_tail, dev);
|
err = ip_frag_reasm(qp, skb, prev_tail, dev);
|
||||||
skb->_skb_refdst = orefdst;
|
skb->_skb_refdst = orefdst;
|
||||||
|
if (err)
|
||||||
|
inet_frag_kill(&qp->q);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
skb_dst_drop(skb);
|
skb_dst_drop(skb);
|
||||||
return -EINPROGRESS;
|
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:
|
discard_qp:
|
||||||
inet_frag_kill(&qp->q);
|
inet_frag_kill(&qp->q);
|
||||||
__IP_INC_STATS(net, IPSTATS_MIB_REASM_OVERLAPS);
|
__IP_INC_STATS(net, IPSTATS_MIB_REASMFAILS);
|
||||||
err:
|
err:
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
return err;
|
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 net *net = container_of(qp->q.net, struct net, ipv4.frags);
|
||||||
struct iphdr *iph;
|
struct iphdr *iph;
|
||||||
struct sk_buff *fp, *head = skb_rb_first(&qp->q.rb_fragments);
|
void *reasm_data;
|
||||||
struct sk_buff **nextp; /* To build frag_list. */
|
int len, err;
|
||||||
struct rb_node *rbn;
|
|
||||||
int len;
|
|
||||||
int ihlen;
|
|
||||||
int delta;
|
|
||||||
int err;
|
|
||||||
u8 ecn;
|
u8 ecn;
|
||||||
|
|
||||||
ipq_kill(qp);
|
ipq_kill(qp);
|
||||||
@@ -530,117 +414,23 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *skb,
|
|||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
goto out_fail;
|
goto out_fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Make the one we just received the head. */
|
/* Make the one we just received the head. */
|
||||||
if (head != skb) {
|
reasm_data = inet_frag_reasm_prepare(&qp->q, skb, prev_tail);
|
||||||
fp = skb_clone(skb, GFP_ATOMIC);
|
if (!reasm_data)
|
||||||
if (!fp)
|
goto out_nomem;
|
||||||
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;
|
|
||||||
|
|
||||||
|
len = ip_hdrlen(skb) + qp->q.len;
|
||||||
err = -E2BIG;
|
err = -E2BIG;
|
||||||
if (len > 65535)
|
if (len > 65535)
|
||||||
goto out_oversize;
|
goto out_oversize;
|
||||||
|
|
||||||
delta = - head->truesize;
|
inet_frag_reasm_finish(&qp->q, skb, reasm_data);
|
||||||
|
|
||||||
/* Head of list must not be cloned. */
|
skb->dev = dev;
|
||||||
if (skb_unclone(head, GFP_ATOMIC))
|
IPCB(skb)->frag_max_size = max(qp->max_df_size, qp->q.max_size);
|
||||||
goto out_nomem;
|
|
||||||
|
|
||||||
delta += head->truesize;
|
iph = ip_hdr(skb);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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, &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->tot_len = htons(len);
|
iph->tot_len = htons(len);
|
||||||
iph->tos |= ecn;
|
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.
|
* from one very small df-fragment and one large non-df frag.
|
||||||
*/
|
*/
|
||||||
if (qp->max_df_size == qp->q.max_size) {
|
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);
|
iph->frag_off = htons(IP_DF);
|
||||||
} else {
|
} else {
|
||||||
iph->frag_off = 0;
|
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);
|
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
|
#ifdef CONFIG_SYSCTL
|
||||||
static int dist_min;
|
static int dist_min;
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
static void ipv4_link_failure(struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
|
struct ip_options opt;
|
||||||
struct rtable *rt;
|
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);
|
rt = skb_rtable(skb);
|
||||||
if (rt)
|
if (rt)
|
||||||
|
|||||||
@@ -390,11 +390,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)
|
static void tcp_grow_window(struct sock *sk, const struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
struct tcp_sock *tp = tcp_sk(sk);
|
struct tcp_sock *tp = tcp_sk(sk);
|
||||||
|
int room;
|
||||||
|
|
||||||
|
room = min_t(int, tp->window_clamp, tcp_space(sk)) - tp->rcv_ssthresh;
|
||||||
|
|
||||||
/* Check #1 */
|
/* Check #1 */
|
||||||
if (tp->rcv_ssthresh < tp->window_clamp &&
|
if (room > 0 && !tcp_under_memory_pressure(sk)) {
|
||||||
(int)tp->rcv_ssthresh < tcp_space(sk) &&
|
|
||||||
!tcp_under_memory_pressure(sk)) {
|
|
||||||
int incr;
|
int incr;
|
||||||
|
|
||||||
/* Check #2. Increase window, if skb with such overhead
|
/* Check #2. Increase window, if skb with such overhead
|
||||||
@@ -407,8 +408,7 @@ static void tcp_grow_window(struct sock *sk, const struct sk_buff *skb)
|
|||||||
|
|
||||||
if (incr) {
|
if (incr) {
|
||||||
incr = max_t(int, incr, 2 * skb->len);
|
incr = max_t(int, incr, 2 * skb->len);
|
||||||
tp->rcv_ssthresh = min(tp->rcv_ssthresh + incr,
|
tp->rcv_ssthresh += min(room, incr);
|
||||||
tp->window_clamp);
|
|
||||||
inet_csk(sk)->icsk_ack.quick |= 1;
|
inet_csk(sk)->icsk_ack.quick |= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,9 +33,8 @@
|
|||||||
|
|
||||||
#include <net/sock.h>
|
#include <net/sock.h>
|
||||||
#include <net/snmp.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/protocol.h>
|
||||||
#include <net/transp_v6.h>
|
#include <net/transp_v6.h>
|
||||||
#include <net/rawv6.h>
|
#include <net/rawv6.h>
|
||||||
@@ -52,14 +51,6 @@
|
|||||||
|
|
||||||
static const char nf_frags_cache_name[] = "nf-frags";
|
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;
|
static struct inet_frags nf_frags;
|
||||||
|
|
||||||
#ifdef CONFIG_SYSCTL
|
#ifdef CONFIG_SYSCTL
|
||||||
@@ -145,6 +136,9 @@ static void __net_exit nf_ct_frags6_sysctl_unregister(struct net *net)
|
|||||||
}
|
}
|
||||||
#endif
|
#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)
|
static inline u8 ip6_frag_ecn(const struct ipv6hdr *ipv6h)
|
||||||
{
|
{
|
||||||
return 1 << (ipv6_get_dsfield(ipv6h) & INET_ECN_MASK);
|
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);
|
fq = container_of(frag, struct frag_queue, q);
|
||||||
net = container_of(fq->q.net, struct net, nf_frag.frags);
|
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. */
|
/* 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,
|
static int nf_ct_frag6_queue(struct frag_queue *fq, struct sk_buff *skb,
|
||||||
const struct frag_hdr *fhdr, int nhoff)
|
const struct frag_hdr *fhdr, int nhoff)
|
||||||
{
|
{
|
||||||
struct sk_buff *prev, *next;
|
|
||||||
unsigned int payload_len;
|
unsigned int payload_len;
|
||||||
int offset, end;
|
struct net_device *dev;
|
||||||
|
struct sk_buff *prev;
|
||||||
|
int offset, end, err;
|
||||||
u8 ecn;
|
u8 ecn;
|
||||||
|
|
||||||
if (fq->q.flags & INET_FRAG_COMPLETE) {
|
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;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Find out which fragments are in front and at the back of us
|
/* Note : skb->rbnode and skb->dev share the same location. */
|
||||||
* in the chain of fragments so far. We must know where to put
|
dev = skb->dev;
|
||||||
* this fragment, right?
|
/* Makes sure compiler wont do silly aliasing games */
|
||||||
*/
|
barrier();
|
||||||
|
|
||||||
prev = fq->q.fragments_tail;
|
prev = fq->q.fragments_tail;
|
||||||
if (!prev || NFCT_FRAG6_CB(prev)->offset < offset) {
|
err = inet_frag_queue_insert(&fq->q, skb, offset, end);
|
||||||
next = NULL;
|
if (err)
|
||||||
goto found;
|
goto insert_error;
|
||||||
}
|
|
||||||
prev = NULL;
|
|
||||||
for (next = fq->q.fragments; next != NULL; next = next->next) {
|
|
||||||
if (NFCT_FRAG6_CB(next)->offset >= offset)
|
|
||||||
break; /* bingo! */
|
|
||||||
prev = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
found:
|
if (dev)
|
||||||
/* RFC5722, Section 4:
|
fq->iif = dev->ifindex;
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* 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.stamp = skb->tstamp;
|
||||||
fq->q.meat += skb->len;
|
fq->q.meat += skb->len;
|
||||||
fq->ecn |= ecn;
|
fq->ecn |= ecn;
|
||||||
@@ -327,11 +286,25 @@ found:
|
|||||||
fq->q.flags |= INET_FRAG_FIRST_IN;
|
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);
|
inet_frag_kill(&fq->q);
|
||||||
err:
|
err:
|
||||||
|
skb_dst_drop(skb);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -341,147 +314,67 @@ err:
|
|||||||
* It is called with locked fq, and caller must check that
|
* It is called with locked fq, and caller must check that
|
||||||
* queue is eligible for reassembly i.e. it is not COMPLETE,
|
* queue is eligible for reassembly i.e. it is not COMPLETE,
|
||||||
* the last and the first frames arrived and all the bits are here.
|
* 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
|
static int nf_ct_frag6_reasm(struct frag_queue *fq, struct sk_buff *skb,
|
||||||
nf_ct_frag6_reasm(struct frag_queue *fq, struct sk_buff *prev, struct net_device *dev)
|
struct sk_buff *prev_tail, struct net_device *dev)
|
||||||
{
|
{
|
||||||
struct sk_buff *fp, *head = fq->q.fragments;
|
void *reasm_data;
|
||||||
int payload_len, delta;
|
int payload_len;
|
||||||
u8 ecn;
|
u8 ecn;
|
||||||
|
|
||||||
inet_frag_kill(&fq->q);
|
inet_frag_kill(&fq->q);
|
||||||
|
|
||||||
WARN_ON(head == NULL);
|
|
||||||
WARN_ON(NFCT_FRAG6_CB(head)->offset != 0);
|
|
||||||
|
|
||||||
ecn = ip_frag_ecn_table[fq->ecn];
|
ecn = ip_frag_ecn_table[fq->ecn];
|
||||||
if (unlikely(ecn == 0xff))
|
if (unlikely(ecn == 0xff))
|
||||||
return false;
|
goto err;
|
||||||
|
|
||||||
/* Unfragmented part is taken from the first segment. */
|
reasm_data = inet_frag_reasm_prepare(&fq->q, skb, prev_tail);
|
||||||
payload_len = ((head->data - skb_network_header(head)) -
|
if (!reasm_data)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
payload_len = ((skb->data - skb_network_header(skb)) -
|
||||||
sizeof(struct ipv6hdr) + fq->q.len -
|
sizeof(struct ipv6hdr) + fq->q.len -
|
||||||
sizeof(struct frag_hdr));
|
sizeof(struct frag_hdr));
|
||||||
if (payload_len > IPV6_MAXPLEN) {
|
if (payload_len > IPV6_MAXPLEN) {
|
||||||
net_dbg_ratelimited("nf_ct_frag6_reasm: payload len = %d\n",
|
net_dbg_ratelimited("nf_ct_frag6_reasm: payload len = %d\n",
|
||||||
payload_len);
|
payload_len);
|
||||||
return false;
|
goto err;
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We have to remove fragment header from datagram and to relocate
|
/* We have to remove fragment header from datagram and to relocate
|
||||||
* header in order to calculate ICV correctly. */
|
* header in order to calculate ICV correctly. */
|
||||||
skb_network_header(head)[fq->nhoffset] = skb_transport_header(head)[0];
|
skb_network_header(skb)[fq->nhoffset] = skb_transport_header(skb)[0];
|
||||||
memmove(head->head + sizeof(struct frag_hdr), head->head,
|
memmove(skb->head + sizeof(struct frag_hdr), skb->head,
|
||||||
(head->data - head->head) - sizeof(struct frag_hdr));
|
(skb->data - skb->head) - sizeof(struct frag_hdr));
|
||||||
head->mac_header += sizeof(struct frag_hdr);
|
skb->mac_header += sizeof(struct frag_hdr);
|
||||||
head->network_header += sizeof(struct frag_hdr);
|
skb->network_header += sizeof(struct frag_hdr);
|
||||||
|
|
||||||
skb_shinfo(head)->frag_list = head->next;
|
skb_reset_transport_header(skb);
|
||||||
skb_reset_transport_header(head);
|
|
||||||
skb_push(head, head->data - skb_network_header(head));
|
|
||||||
|
|
||||||
for (fp = head->next; fp; fp = fp->next) {
|
inet_frag_reasm_finish(&fq->q, skb, reasm_data);
|
||||||
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);
|
|
||||||
|
|
||||||
head->ignore_df = 1;
|
skb->ignore_df = 1;
|
||||||
head->next = NULL;
|
skb->dev = dev;
|
||||||
head->dev = dev;
|
ipv6_hdr(skb)->payload_len = htons(payload_len);
|
||||||
head->tstamp = fq->q.stamp;
|
ipv6_change_dsfield(ipv6_hdr(skb), 0xff, ecn);
|
||||||
ipv6_hdr(head)->payload_len = htons(payload_len);
|
IP6CB(skb)->frag_max_size = sizeof(struct ipv6hdr) + fq->q.max_size;
|
||||||
ipv6_change_dsfield(ipv6_hdr(head), 0xff, ecn);
|
|
||||||
IP6CB(head)->frag_max_size = sizeof(struct ipv6hdr) + fq->q.max_size;
|
|
||||||
|
|
||||||
/* Yes, and fold redundant checksum back. 8) */
|
/* Yes, and fold redundant checksum back. 8) */
|
||||||
if (head->ip_summed == CHECKSUM_COMPLETE)
|
if (skb->ip_summed == CHECKSUM_COMPLETE)
|
||||||
head->csum = csum_partial(skb_network_header(head),
|
skb->csum = csum_partial(skb_network_header(skb),
|
||||||
skb_network_header_len(head),
|
skb_network_header_len(skb),
|
||||||
head->csum);
|
skb->csum);
|
||||||
|
|
||||||
fq->q.fragments = NULL;
|
fq->q.fragments = NULL;
|
||||||
fq->q.rb_fragments = RB_ROOT;
|
fq->q.rb_fragments = RB_ROOT;
|
||||||
fq->q.fragments_tail = NULL;
|
fq->q.fragments_tail = NULL;
|
||||||
|
fq->q.last_run_head = NULL;
|
||||||
|
|
||||||
return true;
|
return 0;
|
||||||
|
|
||||||
|
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)
|
int nf_ct_frag6_gather(struct net *net, struct sk_buff *skb, u32 user)
|
||||||
{
|
{
|
||||||
u16 savethdr = skb->transport_header;
|
u16 savethdr = skb->transport_header;
|
||||||
struct net_device *dev = skb->dev;
|
|
||||||
int fhoff, nhoff, ret;
|
int fhoff, nhoff, ret;
|
||||||
struct frag_hdr *fhdr;
|
struct frag_hdr *fhdr;
|
||||||
struct frag_queue *fq;
|
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);
|
hdr = ipv6_hdr(skb);
|
||||||
fhdr = (struct frag_hdr *)skb_transport_header(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);
|
skb_orphan(skb);
|
||||||
fq = fq_find(net, fhdr->identification, user, hdr,
|
fq = fq_find(net, fhdr->identification, user, hdr,
|
||||||
skb->dev ? skb->dev->ifindex : 0);
|
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);
|
spin_lock_bh(&fq->q.lock);
|
||||||
|
|
||||||
ret = nf_ct_frag6_queue(fq, skb, fhdr, nhoff);
|
ret = nf_ct_frag6_queue(fq, skb, fhdr, nhoff);
|
||||||
if (ret < 0) {
|
if (ret == -EPROTO) {
|
||||||
if (ret == -EPROTO) {
|
skb->transport_header = savethdr;
|
||||||
skb->transport_header = savethdr;
|
ret = 0;
|
||||||
ret = 0;
|
|
||||||
}
|
|
||||||
goto out_unlock;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* after queue has assumed skb ownership, only 0 or -EINPROGRESS
|
/* after queue has assumed skb ownership, only 0 or -EINPROGRESS
|
||||||
* must be returned.
|
* must be returned.
|
||||||
*/
|
*/
|
||||||
ret = -EINPROGRESS;
|
if (ret)
|
||||||
if (fq->q.flags == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) &&
|
ret = -EINPROGRESS;
|
||||||
fq->q.meat == fq->q.len &&
|
|
||||||
nf_ct_frag6_reasm(fq, skb, dev))
|
|
||||||
ret = 0;
|
|
||||||
|
|
||||||
out_unlock:
|
|
||||||
spin_unlock_bh(&fq->q.lock);
|
spin_unlock_bh(&fq->q.lock);
|
||||||
inet_frag_put(&fq->q);
|
inet_frag_put(&fq->q);
|
||||||
return ret;
|
return ret;
|
||||||
@@ -641,16 +522,24 @@ static struct pernet_operations nf_ct_net_ops = {
|
|||||||
.exit = nf_ct_net_exit,
|
.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 nf_ct_frag6_init(void)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
nf_frags.constructor = ip6_frag_init;
|
nf_frags.constructor = ip6frag_init;
|
||||||
nf_frags.destructor = NULL;
|
nf_frags.destructor = NULL;
|
||||||
nf_frags.qsize = sizeof(struct frag_queue);
|
nf_frags.qsize = sizeof(struct frag_queue);
|
||||||
nf_frags.frag_expire = nf_ct_frag6_expire;
|
nf_frags.frag_expire = nf_ct_frag6_expire;
|
||||||
nf_frags.frags_cache_name = nf_frags_cache_name;
|
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);
|
ret = inet_frags_init(&nf_frags);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out;
|
goto out;
|
||||||
|
|||||||
@@ -14,8 +14,7 @@
|
|||||||
#include <linux/skbuff.h>
|
#include <linux/skbuff.h>
|
||||||
#include <linux/icmp.h>
|
#include <linux/icmp.h>
|
||||||
#include <linux/sysctl.h>
|
#include <linux/sysctl.h>
|
||||||
#include <net/ipv6.h>
|
#include <net/ipv6_frag.h>
|
||||||
#include <net/inet_frag.h>
|
|
||||||
|
|
||||||
#include <linux/netfilter_ipv6.h>
|
#include <linux/netfilter_ipv6.h>
|
||||||
#include <linux/netfilter_bridge.h>
|
#include <linux/netfilter_bridge.h>
|
||||||
|
|||||||
@@ -57,18 +57,11 @@
|
|||||||
#include <net/rawv6.h>
|
#include <net/rawv6.h>
|
||||||
#include <net/ndisc.h>
|
#include <net/ndisc.h>
|
||||||
#include <net/addrconf.h>
|
#include <net/addrconf.h>
|
||||||
#include <net/inet_frag.h>
|
#include <net/ipv6_frag.h>
|
||||||
#include <net/inet_ecn.h>
|
#include <net/inet_ecn.h>
|
||||||
|
|
||||||
static const char ip6_frag_cache_name[] = "ip6-frags";
|
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)
|
static u8 ip6_frag_ecn(const struct ipv6hdr *ipv6h)
|
||||||
{
|
{
|
||||||
return 1 << (ipv6_get_dsfield(ipv6h) & INET_ECN_MASK);
|
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 struct inet_frags ip6_frags;
|
||||||
|
|
||||||
static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *prev,
|
static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *skb,
|
||||||
struct net_device *dev);
|
struct sk_buff *prev_tail, 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 void ip6_frag_expire(struct timer_list *t)
|
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);
|
fq = container_of(frag, struct frag_queue, q);
|
||||||
net = container_of(fq->q.net, struct net, ipv6.frags);
|
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 *
|
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,
|
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);
|
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;
|
u8 ecn;
|
||||||
|
|
||||||
if (fq->q.flags & INET_FRAG_COMPLETE)
|
if (fq->q.flags & INET_FRAG_COMPLETE)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
|
err = -EINVAL;
|
||||||
offset = ntohs(fhdr->frag_off) & ~0x7;
|
offset = ntohs(fhdr->frag_off) & ~0x7;
|
||||||
end = offset + (ntohs(ipv6_hdr(skb)->payload_len) -
|
end = offset + (ntohs(ipv6_hdr(skb)->payload_len) -
|
||||||
((u8 *)(fhdr + 1) - (u8 *)(ipv6_hdr(skb) + 1)));
|
((u8 *)(fhdr + 1) - (u8 *)(ipv6_hdr(skb) + 1)));
|
||||||
|
|
||||||
if ((unsigned int)end > IPV6_MAXPLEN) {
|
if ((unsigned int)end > IPV6_MAXPLEN) {
|
||||||
__IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
|
*prob_offset = (u8 *)&fhdr->frag_off - skb_network_header(skb);
|
||||||
IPSTATS_MIB_INHDRERRORS);
|
/* note that if prob_offset is set, the skb is freed elsewhere,
|
||||||
icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
|
* we do not free it here.
|
||||||
((u8 *)&fhdr->frag_off -
|
*/
|
||||||
skb_network_header(skb)));
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,7 +150,7 @@ static int ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb,
|
|||||||
*/
|
*/
|
||||||
if (end < fq->q.len ||
|
if (end < fq->q.len ||
|
||||||
((fq->q.flags & INET_FRAG_LAST_IN) && 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.flags |= INET_FRAG_LAST_IN;
|
||||||
fq->q.len = end;
|
fq->q.len = end;
|
||||||
} else {
|
} 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
|
/* RFC2460 says always send parameter problem in
|
||||||
* this case. -DaveM
|
* this case. -DaveM
|
||||||
*/
|
*/
|
||||||
__IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
|
*prob_offset = offsetof(struct ipv6hdr, payload_len);
|
||||||
IPSTATS_MIB_INHDRERRORS);
|
|
||||||
icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
|
|
||||||
offsetof(struct ipv6hdr, payload_len));
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (end > fq->q.len) {
|
if (end > fq->q.len) {
|
||||||
/* Some bits beyond end -> corruption. */
|
/* Some bits beyond end -> corruption. */
|
||||||
if (fq->q.flags & INET_FRAG_LAST_IN)
|
if (fq->q.flags & INET_FRAG_LAST_IN)
|
||||||
goto err;
|
goto discard_fq;
|
||||||
fq->q.len = end;
|
fq->q.len = end;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (end == offset)
|
if (end == offset)
|
||||||
goto err;
|
goto discard_fq;
|
||||||
|
|
||||||
|
err = -ENOMEM;
|
||||||
/* Point into the IP datagram 'data' part. */
|
/* Point into the IP datagram 'data' part. */
|
||||||
if (!pskb_pull(skb, (u8 *) (fhdr + 1) - skb->data))
|
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;
|
goto discard_fq;
|
||||||
|
|
||||||
/* Look for overlap with succeeding segment. */
|
err = pskb_trim_rcsum(skb, end - offset);
|
||||||
if (next && FRAG6_CB(next)->offset < end)
|
if (err)
|
||||||
goto discard_fq;
|
goto discard_fq;
|
||||||
|
|
||||||
FRAG6_CB(skb)->offset = offset;
|
/* Note : skb->rbnode and skb->dev share the same location. */
|
||||||
|
|
||||||
/* 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;
|
|
||||||
|
|
||||||
dev = skb->dev;
|
dev = skb->dev;
|
||||||
if (dev) {
|
/* Makes sure compiler wont do silly aliasing games */
|
||||||
|
barrier();
|
||||||
|
|
||||||
|
prev_tail = fq->q.fragments_tail;
|
||||||
|
err = inet_frag_queue_insert(&fq->q, skb, offset, end);
|
||||||
|
if (err)
|
||||||
|
goto insert_error;
|
||||||
|
|
||||||
|
if (dev)
|
||||||
fq->iif = dev->ifindex;
|
fq->iif = dev->ifindex;
|
||||||
skb->dev = NULL;
|
|
||||||
}
|
|
||||||
fq->q.stamp = skb->tstamp;
|
fq->q.stamp = skb->tstamp;
|
||||||
fq->q.meat += skb->len;
|
fq->q.meat += skb->len;
|
||||||
fq->ecn |= ecn;
|
fq->ecn |= ecn;
|
||||||
@@ -313,44 +216,48 @@ found:
|
|||||||
|
|
||||||
if (fq->q.flags == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) &&
|
if (fq->q.flags == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) &&
|
||||||
fq->q.meat == fq->q.len) {
|
fq->q.meat == fq->q.len) {
|
||||||
int res;
|
|
||||||
unsigned long orefdst = skb->_skb_refdst;
|
unsigned long orefdst = skb->_skb_refdst;
|
||||||
|
|
||||||
skb->_skb_refdst = 0UL;
|
skb->_skb_refdst = 0UL;
|
||||||
res = ip6_frag_reasm(fq, prev, dev);
|
err = ip6_frag_reasm(fq, skb, prev_tail, dev);
|
||||||
skb->_skb_refdst = orefdst;
|
skb->_skb_refdst = orefdst;
|
||||||
return res;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
skb_dst_drop(skb);
|
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:
|
discard_fq:
|
||||||
inet_frag_kill(&fq->q);
|
inet_frag_kill(&fq->q);
|
||||||
err:
|
|
||||||
__IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
|
__IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
|
||||||
IPSTATS_MIB_REASMFAILS);
|
IPSTATS_MIB_REASMFAILS);
|
||||||
|
err:
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
return -1;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check if this packet is complete.
|
* 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
|
* It is called with locked fq, and caller must check that
|
||||||
* queue is eligible for reassembly i.e. it is not COMPLETE,
|
* queue is eligible for reassembly i.e. it is not COMPLETE,
|
||||||
* the last and the first frames arrived and all the bits are here.
|
* 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,
|
static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *skb,
|
||||||
struct net_device *dev)
|
struct sk_buff *prev_tail, struct net_device *dev)
|
||||||
{
|
{
|
||||||
struct net *net = container_of(fq->q.net, struct net, ipv6.frags);
|
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;
|
unsigned int nhoff;
|
||||||
int sum_truesize;
|
void *reasm_data;
|
||||||
|
int payload_len;
|
||||||
u8 ecn;
|
u8 ecn;
|
||||||
|
|
||||||
inet_frag_kill(&fq->q);
|
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))
|
if (unlikely(ecn == 0xff))
|
||||||
goto out_fail;
|
goto out_fail;
|
||||||
|
|
||||||
/* Make the one we just received the head. */
|
reasm_data = inet_frag_reasm_prepare(&fq->q, skb, prev_tail);
|
||||||
if (prev) {
|
if (!reasm_data)
|
||||||
head = prev->next;
|
goto out_oom;
|
||||||
fp = skb_clone(head, GFP_ATOMIC);
|
|
||||||
|
|
||||||
if (!fp)
|
payload_len = ((skb->data - skb_network_header(skb)) -
|
||||||
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);
|
|
||||||
|
|
||||||
/* Unfragmented part is taken from the first segment. */
|
|
||||||
payload_len = ((head->data - skb_network_header(head)) -
|
|
||||||
sizeof(struct ipv6hdr) + fq->q.len -
|
sizeof(struct ipv6hdr) + fq->q.len -
|
||||||
sizeof(struct frag_hdr));
|
sizeof(struct frag_hdr));
|
||||||
if (payload_len > IPV6_MAXPLEN)
|
if (payload_len > IPV6_MAXPLEN)
|
||||||
goto out_oversize;
|
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
|
/* We have to remove fragment header from datagram and to relocate
|
||||||
* header in order to calculate ICV correctly. */
|
* header in order to calculate ICV correctly. */
|
||||||
nhoff = fq->nhoffset;
|
nhoff = fq->nhoffset;
|
||||||
skb_network_header(head)[nhoff] = skb_transport_header(head)[0];
|
skb_network_header(skb)[nhoff] = skb_transport_header(skb)[0];
|
||||||
memmove(head->head + sizeof(struct frag_hdr), head->head,
|
memmove(skb->head + sizeof(struct frag_hdr), skb->head,
|
||||||
(head->data - head->head) - sizeof(struct frag_hdr));
|
(skb->data - skb->head) - sizeof(struct frag_hdr));
|
||||||
if (skb_mac_header_was_set(head))
|
if (skb_mac_header_was_set(skb))
|
||||||
head->mac_header += sizeof(struct frag_hdr);
|
skb->mac_header += sizeof(struct frag_hdr);
|
||||||
head->network_header += sizeof(struct frag_hdr);
|
skb->network_header += sizeof(struct frag_hdr);
|
||||||
|
|
||||||
skb_reset_transport_header(head);
|
skb_reset_transport_header(skb);
|
||||||
skb_push(head, head->data - skb_network_header(head));
|
|
||||||
|
|
||||||
sum_truesize = head->truesize;
|
inet_frag_reasm_finish(&fq->q, skb, reasm_data);
|
||||||
for (fp = head->next; fp;) {
|
|
||||||
bool headstolen;
|
|
||||||
int delta;
|
|
||||||
struct sk_buff *next = fp->next;
|
|
||||||
|
|
||||||
sum_truesize += fp->truesize;
|
skb->dev = dev;
|
||||||
if (head->ip_summed != fp->ip_summed)
|
ipv6_hdr(skb)->payload_len = htons(payload_len);
|
||||||
head->ip_summed = CHECKSUM_NONE;
|
ipv6_change_dsfield(ipv6_hdr(skb), 0xff, ecn);
|
||||||
else if (head->ip_summed == CHECKSUM_COMPLETE)
|
IP6CB(skb)->nhoff = nhoff;
|
||||||
head->csum = csum_add(head->csum, fp->csum);
|
IP6CB(skb)->flags |= IP6SKB_FRAGMENTED;
|
||||||
|
IP6CB(skb)->frag_max_size = fq->q.max_size;
|
||||||
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);
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
/* Yes, and fold redundant checksum back. 8) */
|
/* Yes, and fold redundant checksum back. 8) */
|
||||||
skb_postpush_rcsum(head, skb_network_header(head),
|
skb_postpush_rcsum(skb, skb_network_header(skb),
|
||||||
skb_network_header_len(head));
|
skb_network_header_len(skb));
|
||||||
|
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
__IP6_INC_STATS(net, __in6_dev_get(dev), IPSTATS_MIB_REASMOKS);
|
__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.fragments = NULL;
|
||||||
fq->q.rb_fragments = RB_ROOT;
|
fq->q.rb_fragments = RB_ROOT;
|
||||||
fq->q.fragments_tail = NULL;
|
fq->q.fragments_tail = NULL;
|
||||||
|
fq->q.last_run_head = NULL;
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
out_oversize:
|
out_oversize:
|
||||||
@@ -491,6 +319,7 @@ out_fail:
|
|||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
__IP6_INC_STATS(net, __in6_dev_get(dev), IPSTATS_MIB_REASMFAILS);
|
__IP6_INC_STATS(net, __in6_dev_get(dev), IPSTATS_MIB_REASMFAILS);
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
|
inet_frag_kill(&fq->q);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -529,22 +358,26 @@ static int ipv6_frag_rcv(struct sk_buff *skb)
|
|||||||
return 1;
|
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;
|
iif = skb->dev ? skb->dev->ifindex : 0;
|
||||||
fq = fq_find(net, fhdr->identification, hdr, iif);
|
fq = fq_find(net, fhdr->identification, hdr, iif);
|
||||||
if (fq) {
|
if (fq) {
|
||||||
|
u32 prob_offset = 0;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
spin_lock(&fq->q.lock);
|
spin_lock(&fq->q.lock);
|
||||||
|
|
||||||
fq->iif = iif;
|
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);
|
spin_unlock(&fq->q.lock);
|
||||||
inet_frag_put(&fq->q);
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -712,42 +545,19 @@ static struct pernet_operations ip6_frags_ops = {
|
|||||||
.exit = ipv6_frags_exit_net,
|
.exit = ipv6_frags_exit_net,
|
||||||
};
|
};
|
||||||
|
|
||||||
static u32 ip6_key_hashfn(const void *data, u32 len, u32 seed)
|
static const struct rhashtable_params ip6_rhash_params = {
|
||||||
{
|
|
||||||
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 = {
|
|
||||||
.head_offset = offsetof(struct inet_frag_queue, node),
|
.head_offset = offsetof(struct inet_frag_queue, node),
|
||||||
.hashfn = ip6_key_hashfn,
|
.hashfn = ip6frag_key_hashfn,
|
||||||
.obj_hashfn = ip6_obj_hashfn,
|
.obj_hashfn = ip6frag_obj_hashfn,
|
||||||
.obj_cmpfn = ip6_obj_cmpfn,
|
.obj_cmpfn = ip6frag_obj_cmpfn,
|
||||||
.automatic_shrinking = true,
|
.automatic_shrinking = true,
|
||||||
};
|
};
|
||||||
EXPORT_SYMBOL(ip6_rhash_params);
|
|
||||||
|
|
||||||
int __init ipv6_frag_init(void)
|
int __init ipv6_frag_init(void)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ip6_frags.constructor = ip6_frag_init;
|
ip6_frags.constructor = ip6frag_init;
|
||||||
ip6_frags.destructor = NULL;
|
ip6_frags.destructor = NULL;
|
||||||
ip6_frags.qsize = sizeof(struct frag_queue);
|
ip6_frags.qsize = sizeof(struct frag_queue);
|
||||||
ip6_frags.frag_expire = ip6_frag_expire;
|
ip6_frags.frag_expire = ip6_frag_expire;
|
||||||
|
|||||||
@@ -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);
|
struct ieee80211_sub_if_data *sdata = vif_to_sdata(txq->txq.vif);
|
||||||
|
|
||||||
|
if (local->in_reconfig)
|
||||||
|
return;
|
||||||
|
|
||||||
if (!check_sdata_in_driver(sdata))
|
if (!check_sdata_in_driver(sdata))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
#include <net/netfilter/nf_conntrack_seqadj.h>
|
#include <net/netfilter/nf_conntrack_seqadj.h>
|
||||||
#include <net/netfilter/nf_conntrack_zones.h>
|
#include <net/netfilter/nf_conntrack_zones.h>
|
||||||
#include <net/netfilter/ipv6/nf_defrag_ipv6.h>
|
#include <net/netfilter/ipv6/nf_defrag_ipv6.h>
|
||||||
|
#include <net/ipv6_frag.h>
|
||||||
|
|
||||||
#ifdef CONFIG_NF_NAT_NEEDED
|
#ifdef CONFIG_NF_NAT_NEEDED
|
||||||
#include <linux/netfilter/nf_nat.h>
|
#include <linux/netfilter/nf_nat.h>
|
||||||
|
|||||||
@@ -47,49 +47,9 @@ typedef struct {
|
|||||||
struct devtable {
|
struct devtable {
|
||||||
const char *device_id; /* name of table, __mod_<name>__*_device_table. */
|
const char *device_id; /* name of table, __mod_<name>__*_device_table. */
|
||||||
unsigned long id_size;
|
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
|
/* Define a variable f that holds the value of field f of struct devid
|
||||||
* based at address m.
|
* based at address m.
|
||||||
*/
|
*/
|
||||||
@@ -102,16 +62,6 @@ extern struct devtable *__start___devtable[], *__stop___devtable[];
|
|||||||
#define DEF_FIELD_ADDR(m, devid, f) \
|
#define DEF_FIELD_ADDR(m, devid, f) \
|
||||||
typeof(((struct devid *)0)->f) *f = ((m) + OFF_##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) \
|
#define ADD(str, sep, cond, field) \
|
||||||
do { \
|
do { \
|
||||||
strcat(str, sep); \
|
strcat(str, sep); \
|
||||||
@@ -431,7 +381,6 @@ static int do_hid_entry(const char *filename,
|
|||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
ADD_TO_DEVTABLE("hid", hid_device_id, do_hid_entry);
|
|
||||||
|
|
||||||
/* Looks like: ieee1394:venNmoNspNverN */
|
/* Looks like: ieee1394:venNmoNspNverN */
|
||||||
static int do_ieee1394_entry(const char *filename,
|
static int do_ieee1394_entry(const char *filename,
|
||||||
@@ -456,7 +405,6 @@ static int do_ieee1394_entry(const char *filename,
|
|||||||
add_wildcard(alias);
|
add_wildcard(alias);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
ADD_TO_DEVTABLE("ieee1394", ieee1394_device_id, do_ieee1394_entry);
|
|
||||||
|
|
||||||
/* Looks like: pci:vNdNsvNsdNbcNscNiN. */
|
/* Looks like: pci:vNdNsvNsdNbcNscNiN. */
|
||||||
static int do_pci_entry(const char *filename,
|
static int do_pci_entry(const char *filename,
|
||||||
@@ -500,7 +448,6 @@ static int do_pci_entry(const char *filename,
|
|||||||
add_wildcard(alias);
|
add_wildcard(alias);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
ADD_TO_DEVTABLE("pci", pci_device_id, do_pci_entry);
|
|
||||||
|
|
||||||
/* looks like: "ccw:tNmNdtNdmN" */
|
/* looks like: "ccw:tNmNdtNdmN" */
|
||||||
static int do_ccw_entry(const char *filename,
|
static int do_ccw_entry(const char *filename,
|
||||||
@@ -524,7 +471,6 @@ static int do_ccw_entry(const char *filename,
|
|||||||
add_wildcard(alias);
|
add_wildcard(alias);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
ADD_TO_DEVTABLE("ccw", ccw_device_id, do_ccw_entry);
|
|
||||||
|
|
||||||
/* looks like: "ap:tN" */
|
/* looks like: "ap:tN" */
|
||||||
static int do_ap_entry(const char *filename,
|
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);
|
sprintf(alias, "ap:t%02X*", dev_type);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
ADD_TO_DEVTABLE("ap", ap_device_id, do_ap_entry);
|
|
||||||
|
|
||||||
/* looks like: "css:tN" */
|
/* looks like: "css:tN" */
|
||||||
static int do_css_entry(const char *filename,
|
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);
|
sprintf(alias, "css:t%01X", type);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
ADD_TO_DEVTABLE("css", css_device_id, do_css_entry);
|
|
||||||
|
|
||||||
/* Looks like: "serio:tyNprNidNexN" */
|
/* Looks like: "serio:tyNprNidNexN" */
|
||||||
static int do_serio_entry(const char *filename,
|
static int do_serio_entry(const char *filename,
|
||||||
@@ -566,7 +510,6 @@ static int do_serio_entry(const char *filename,
|
|||||||
add_wildcard(alias);
|
add_wildcard(alias);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
ADD_TO_DEVTABLE("serio", serio_device_id, do_serio_entry);
|
|
||||||
|
|
||||||
/* looks like: "acpi:ACPI0003" or "acpi:PNP0C0B" or "acpi:LNXVIDEO" or
|
/* looks like: "acpi:ACPI0003" or "acpi:PNP0C0B" or "acpi:LNXVIDEO" or
|
||||||
* "acpi:bbsspp" (bb=base-class, ss=sub-class, pp=prog-if)
|
* "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;
|
return 1;
|
||||||
}
|
}
|
||||||
ADD_TO_DEVTABLE("acpi", acpi_device_id, do_acpi_entry);
|
|
||||||
|
|
||||||
/* looks like: "pnp:dD" */
|
/* looks like: "pnp:dD" */
|
||||||
static void do_pnp_device_entry(void *symval, unsigned long size,
|
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);
|
add_wildcard(alias);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
ADD_TO_DEVTABLE("pcmcia", pcmcia_device_id, do_pcmcia_entry);
|
|
||||||
|
|
||||||
static int do_vio_entry(const char *filename, void *symval,
|
static int do_vio_entry(const char *filename, void *symval,
|
||||||
char *alias)
|
char *alias)
|
||||||
@@ -745,7 +686,6 @@ static int do_vio_entry(const char *filename, void *symval,
|
|||||||
add_wildcard(alias);
|
add_wildcard(alias);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
ADD_TO_DEVTABLE("vio", vio_device_id, do_vio_entry);
|
|
||||||
|
|
||||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
|
#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);
|
do_input(alias, *swbit, 0, INPUT_DEVICE_ID_SW_MAX);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
ADD_TO_DEVTABLE("input", input_device_id, do_input_entry);
|
|
||||||
|
|
||||||
static int do_eisa_entry(const char *filename, void *symval,
|
static int do_eisa_entry(const char *filename, void *symval,
|
||||||
char *alias)
|
char *alias)
|
||||||
@@ -830,7 +769,6 @@ static int do_eisa_entry(const char *filename, void *symval,
|
|||||||
strcat(alias, "*");
|
strcat(alias, "*");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
ADD_TO_DEVTABLE("eisa", eisa_device_id, do_eisa_entry);
|
|
||||||
|
|
||||||
/* Looks like: parisc:tNhvNrevNsvN */
|
/* Looks like: parisc:tNhvNrevNsvN */
|
||||||
static int do_parisc_entry(const char *filename, void *symval,
|
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);
|
add_wildcard(alias);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
ADD_TO_DEVTABLE("parisc", parisc_device_id, do_parisc_entry);
|
|
||||||
|
|
||||||
/* Looks like: sdio:cNvNdN. */
|
/* Looks like: sdio:cNvNdN. */
|
||||||
static int do_sdio_entry(const char *filename,
|
static int do_sdio_entry(const char *filename,
|
||||||
@@ -867,7 +804,6 @@ static int do_sdio_entry(const char *filename,
|
|||||||
add_wildcard(alias);
|
add_wildcard(alias);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
ADD_TO_DEVTABLE("sdio", sdio_device_id, do_sdio_entry);
|
|
||||||
|
|
||||||
/* Looks like: ssb:vNidNrevN. */
|
/* Looks like: ssb:vNidNrevN. */
|
||||||
static int do_ssb_entry(const char *filename,
|
static int do_ssb_entry(const char *filename,
|
||||||
@@ -884,7 +820,6 @@ static int do_ssb_entry(const char *filename,
|
|||||||
add_wildcard(alias);
|
add_wildcard(alias);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
ADD_TO_DEVTABLE("ssb", ssb_device_id, do_ssb_entry);
|
|
||||||
|
|
||||||
/* Looks like: bcma:mNidNrevNclN. */
|
/* Looks like: bcma:mNidNrevNclN. */
|
||||||
static int do_bcma_entry(const char *filename,
|
static int do_bcma_entry(const char *filename,
|
||||||
@@ -903,7 +838,6 @@ static int do_bcma_entry(const char *filename,
|
|||||||
add_wildcard(alias);
|
add_wildcard(alias);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
ADD_TO_DEVTABLE("bcma", bcma_device_id, do_bcma_entry);
|
|
||||||
|
|
||||||
/* Looks like: virtio:dNvN */
|
/* Looks like: virtio:dNvN */
|
||||||
static int do_virtio_entry(const char *filename, void *symval,
|
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);
|
add_wildcard(alias);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
ADD_TO_DEVTABLE("virtio", virtio_device_id, do_virtio_entry);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Looks like: vmbus:guid
|
* Looks like: vmbus:guid
|
||||||
@@ -942,7 +875,6 @@ static int do_vmbus_entry(const char *filename, void *symval,
|
|||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
ADD_TO_DEVTABLE("vmbus", hv_vmbus_device_id, do_vmbus_entry);
|
|
||||||
|
|
||||||
/* Looks like: i2c:S */
|
/* Looks like: i2c:S */
|
||||||
static int do_i2c_entry(const char *filename, void *symval,
|
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;
|
return 1;
|
||||||
}
|
}
|
||||||
ADD_TO_DEVTABLE("i2c", i2c_device_id, do_i2c_entry);
|
|
||||||
|
|
||||||
/* Looks like: spi:S */
|
/* Looks like: spi:S */
|
||||||
static int do_spi_entry(const char *filename, void *symval,
|
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;
|
return 1;
|
||||||
}
|
}
|
||||||
ADD_TO_DEVTABLE("spi", spi_device_id, do_spi_entry);
|
|
||||||
|
|
||||||
static const struct dmifield {
|
static const struct dmifield {
|
||||||
const char *prefix;
|
const char *prefix;
|
||||||
@@ -1019,7 +949,6 @@ static int do_dmi_entry(const char *filename, void *symval,
|
|||||||
strcat(alias, ":");
|
strcat(alias, ":");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
ADD_TO_DEVTABLE("dmi", dmi_system_id, do_dmi_entry);
|
|
||||||
|
|
||||||
static int do_platform_entry(const char *filename,
|
static int do_platform_entry(const char *filename,
|
||||||
void *symval, char *alias)
|
void *symval, char *alias)
|
||||||
@@ -1028,7 +957,6 @@ static int do_platform_entry(const char *filename,
|
|||||||
sprintf(alias, PLATFORM_MODULE_PREFIX "%s", *name);
|
sprintf(alias, PLATFORM_MODULE_PREFIX "%s", *name);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
ADD_TO_DEVTABLE("platform", platform_device_id, do_platform_entry);
|
|
||||||
|
|
||||||
static int do_mdio_entry(const char *filename,
|
static int do_mdio_entry(const char *filename,
|
||||||
void *symval, char *alias)
|
void *symval, char *alias)
|
||||||
@@ -1053,7 +981,6 @@ static int do_mdio_entry(const char *filename,
|
|||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
ADD_TO_DEVTABLE("mdio", mdio_device_id, do_mdio_entry);
|
|
||||||
|
|
||||||
/* Looks like: zorro:iN. */
|
/* Looks like: zorro:iN. */
|
||||||
static int do_zorro_entry(const char *filename, void *symval,
|
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);
|
ADD(alias, "i", id != ZORRO_WILDCARD, id);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
ADD_TO_DEVTABLE("zorro", zorro_device_id, do_zorro_entry);
|
|
||||||
|
|
||||||
/* looks like: "pnp:dD" */
|
/* looks like: "pnp:dD" */
|
||||||
static int do_isapnp_entry(const char *filename,
|
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);
|
(function >> 12) & 0x0f, (function >> 8) & 0x0f);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
ADD_TO_DEVTABLE("isapnp", isapnp_device_id, do_isapnp_entry);
|
|
||||||
|
|
||||||
/* Looks like: "ipack:fNvNdN". */
|
/* Looks like: "ipack:fNvNdN". */
|
||||||
static int do_ipack_entry(const char *filename,
|
static int do_ipack_entry(const char *filename,
|
||||||
@@ -1096,7 +1021,6 @@ static int do_ipack_entry(const char *filename,
|
|||||||
add_wildcard(alias);
|
add_wildcard(alias);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
ADD_TO_DEVTABLE("ipack", ipack_device_id, do_ipack_entry);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Append a match expression for a single masked hex digit.
|
* Append a match expression for a single masked hex digit.
|
||||||
@@ -1167,7 +1091,6 @@ static int do_amba_entry(const char *filename,
|
|||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
ADD_TO_DEVTABLE("amba", amba_id, do_amba_entry);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* looks like: "mipscdmm:tN"
|
* looks like: "mipscdmm:tN"
|
||||||
@@ -1183,7 +1106,6 @@ static int do_mips_cdmm_entry(const char *filename,
|
|||||||
sprintf(alias, "mipscdmm:t%02X*", type);
|
sprintf(alias, "mipscdmm:t%02X*", type);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
ADD_TO_DEVTABLE("mipscdmm", mips_cdmm_device_id, do_mips_cdmm_entry);
|
|
||||||
|
|
||||||
/* LOOKS like cpu:type:x86,venVVVVfamFFFFmodMMMM:feature:*,FEAT,*
|
/* LOOKS like cpu:type:x86,venVVVVfamFFFFmodMMMM:feature:*,FEAT,*
|
||||||
* All fields are numbers. It would be nicer to use strings for vendor
|
* 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);
|
sprintf(alias + strlen(alias), "%04X*", feature);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
ADD_TO_DEVTABLE("x86cpu", x86_cpu_id, do_x86cpu_entry);
|
|
||||||
|
|
||||||
/* LOOKS like cpu:type:*:feature:*FEAT* */
|
/* LOOKS like cpu:type:*:feature:*FEAT* */
|
||||||
static int do_cpu_entry(const char *filename, void *symval, char *alias)
|
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);
|
sprintf(alias, "cpu:type:*:feature:*%04X*", feature);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
ADD_TO_DEVTABLE("cpu", cpu_feature, do_cpu_entry);
|
|
||||||
|
|
||||||
/* Looks like: mei:S:uuid:N:* */
|
/* Looks like: mei:S:uuid:N:* */
|
||||||
static int do_mei_entry(const char *filename, void *symval,
|
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;
|
return 1;
|
||||||
}
|
}
|
||||||
ADD_TO_DEVTABLE("mei", mei_cl_device_id, do_mei_entry);
|
|
||||||
|
|
||||||
/* Looks like: rapidio:vNdNavNadN */
|
/* Looks like: rapidio:vNdNavNadN */
|
||||||
static int do_rio_entry(const char *filename,
|
static int do_rio_entry(const char *filename,
|
||||||
@@ -1257,7 +1176,6 @@ static int do_rio_entry(const char *filename,
|
|||||||
add_wildcard(alias);
|
add_wildcard(alias);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
ADD_TO_DEVTABLE("rapidio", rio_device_id, do_rio_entry);
|
|
||||||
|
|
||||||
/* Looks like: ulpi:vNpN */
|
/* Looks like: ulpi:vNpN */
|
||||||
static int do_ulpi_entry(const char *filename, void *symval,
|
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;
|
return 1;
|
||||||
}
|
}
|
||||||
ADD_TO_DEVTABLE("ulpi", ulpi_device_id, do_ulpi_entry);
|
|
||||||
|
|
||||||
/* Looks like: hdaudio:vNrNaN */
|
/* Looks like: hdaudio:vNrNaN */
|
||||||
static int do_hda_entry(const char *filename, void *symval, char *alias)
|
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);
|
add_wildcard(alias);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
ADD_TO_DEVTABLE("hdaudio", hda_device_id, do_hda_entry);
|
|
||||||
|
|
||||||
/* Looks like: fsl-mc:vNdN */
|
/* Looks like: fsl-mc:vNdN */
|
||||||
static int do_fsl_mc_entry(const char *filename, void *symval,
|
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);
|
sprintf(alias, "fsl-mc:v%08Xd%s", vendor, *obj_type);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
ADD_TO_DEVTABLE("fslmc", fsl_mc_device_id, do_fsl_mc_entry);
|
|
||||||
|
|
||||||
/* Does namelen bytes of name exactly match the symbol? */
|
/* Does namelen bytes of name exactly match the symbol? */
|
||||||
static bool sym_is(const char *name, unsigned namelen, const char *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,
|
static void do_table(void *symval, unsigned long size,
|
||||||
unsigned long id_size,
|
unsigned long id_size,
|
||||||
const char *device_id,
|
const char *device_id,
|
||||||
void *function,
|
int (*do_entry)(const char *filename, void *symval, char *alias),
|
||||||
struct module *mod)
|
struct module *mod)
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
char alias[500];
|
char alias[500];
|
||||||
int (*do_entry)(const char *, void *entry, char *alias) = function;
|
|
||||||
|
|
||||||
device_id_check(mod->name, device_id, size, id_size, symval);
|
device_id_check(mod->name, device_id, size, id_size, symval);
|
||||||
/* Leave last one: it's the terminator. */
|
/* 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.
|
/* Create MODULE_ALIAS() statements.
|
||||||
* At this time, we cannot write the actual output C source yet,
|
* At this time, we cannot write the actual output C source yet,
|
||||||
* so we write into the mod->dev_table_buf buffer. */
|
* 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"))
|
else if (sym_is(name, namelen, "pnp_card"))
|
||||||
do_pnp_card_entries(symval, sym->st_size, mod);
|
do_pnp_card_entries(symval, sym->st_size, mod);
|
||||||
else {
|
else {
|
||||||
struct devtable **p;
|
int i;
|
||||||
INIT_SECTION(__devtable);
|
|
||||||
|
|
||||||
for (p = __start___devtable; p < __stop___devtable; p++) {
|
for (i = 0; i < ARRAY_SIZE(devtable); i++) {
|
||||||
if (sym_is(name, namelen, (*p)->device_id)) {
|
const struct devtable *p = &devtable[i];
|
||||||
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -569,7 +569,7 @@ static int propagate_exception(struct dev_cgroup *devcg_root,
|
|||||||
devcg->behavior == DEVCG_DEFAULT_ALLOW) {
|
devcg->behavior == DEVCG_DEFAULT_ALLOW) {
|
||||||
rc = dev_exception_add(devcg, ex);
|
rc = dev_exception_add(devcg, ex);
|
||||||
if (rc)
|
if (rc)
|
||||||
break;
|
return rc;
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
* in the other possible cases:
|
* in the other possible cases:
|
||||||
|
|||||||
@@ -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->children);
|
||||||
INIT_LIST_HEAD(&entry->list);
|
INIT_LIST_HEAD(&entry->list);
|
||||||
entry->parent = parent;
|
entry->parent = parent;
|
||||||
if (parent)
|
if (parent) {
|
||||||
|
mutex_lock(&parent->access);
|
||||||
list_add_tail(&entry->list, &parent->children);
|
list_add_tail(&entry->list, &parent->children);
|
||||||
|
mutex_unlock(&parent->access);
|
||||||
|
}
|
||||||
return entry;
|
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)
|
list_for_each_entry_safe(p, n, &entry->children, list)
|
||||||
snd_info_free_entry(p);
|
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);
|
kfree(entry->name);
|
||||||
if (entry->private_free)
|
if (entry->private_free)
|
||||||
entry->private_free(entry);
|
entry->private_free(entry);
|
||||||
|
|||||||
@@ -406,14 +406,7 @@ int snd_card_disconnect(struct snd_card *card)
|
|||||||
card->shutdown = 1;
|
card->shutdown = 1;
|
||||||
spin_unlock(&card->files_lock);
|
spin_unlock(&card->files_lock);
|
||||||
|
|
||||||
/* phase 1: disable fops (user space) operations for ALSA API */
|
/* replace file->f_op with special dummy operations */
|
||||||
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 */
|
|
||||||
|
|
||||||
spin_lock(&card->files_lock);
|
spin_lock(&card->files_lock);
|
||||||
list_for_each_entry(mfile, &card->files_list, list) {
|
list_for_each_entry(mfile, &card->files_list, list) {
|
||||||
/* it's critical part, use endless loop */
|
/* it's critical part, use endless loop */
|
||||||
@@ -429,7 +422,7 @@ int snd_card_disconnect(struct snd_card *card)
|
|||||||
}
|
}
|
||||||
spin_unlock(&card->files_lock);
|
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() */
|
/* at this point, they cannot respond to any calls except release() */
|
||||||
|
|
||||||
#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
|
#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
|
||||||
@@ -445,6 +438,13 @@ int snd_card_disconnect(struct snd_card *card)
|
|||||||
device_del(&card->card_dev);
|
device_del(&card->card_dev);
|
||||||
card->registered = false;
|
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
|
#ifdef CONFIG_PM
|
||||||
wake_up(&card->power_sleep);
|
wake_up(&card->power_sleep);
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -6743,6 +6743,8 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
|
|||||||
{0x12, 0x90a60140},
|
{0x12, 0x90a60140},
|
||||||
{0x14, 0x90170150},
|
{0x14, 0x90170150},
|
||||||
{0x21, 0x02211020}),
|
{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,
|
SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL2_MIC_NO_PRESENCE,
|
||||||
{0x14, 0x90170110},
|
{0x14, 0x90170110},
|
||||||
{0x21, 0x02211020}),
|
{0x21, 0x02211020}),
|
||||||
@@ -6853,6 +6855,10 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
|
|||||||
{0x21, 0x0221101f}),
|
{0x21, 0x0221101f}),
|
||||||
SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
|
SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
|
||||||
ALC256_STANDARD_PINS),
|
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,
|
SND_HDA_PIN_QUIRK(0x10ec0256, 0x1043, "ASUS", ALC256_FIXUP_ASUS_MIC,
|
||||||
{0x14, 0x90170110},
|
{0x14, 0x90170110},
|
||||||
{0x1b, 0x90a70130},
|
{0x1b, 0x90a70130},
|
||||||
|
|||||||
@@ -3,8 +3,6 @@
|
|||||||
#define _TOOLS_LINUX_BITOPS_H_
|
#define _TOOLS_LINUX_BITOPS_H_
|
||||||
|
|
||||||
#include <asm/types.h>
|
#include <asm/types.h>
|
||||||
#include <linux/compiler.h>
|
|
||||||
|
|
||||||
#ifndef __WORDSIZE
|
#ifndef __WORDSIZE
|
||||||
#define __WORDSIZE (__SIZEOF_LONG__ * 8)
|
#define __WORDSIZE (__SIZEOF_LONG__ * 8)
|
||||||
#endif
|
#endif
|
||||||
@@ -12,10 +10,9 @@
|
|||||||
#ifndef BITS_PER_LONG
|
#ifndef BITS_PER_LONG
|
||||||
# define BITS_PER_LONG __WORDSIZE
|
# define BITS_PER_LONG __WORDSIZE
|
||||||
#endif
|
#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_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_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))
|
#define BITS_TO_U32(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u32))
|
||||||
|
|||||||
26
tools/include/linux/bits.h
Normal file
26
tools/include/linux/bits.h
Normal file
@@ -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 */
|
||||||
@@ -11,6 +11,7 @@ include/uapi/linux/sched.h
|
|||||||
include/uapi/linux/stat.h
|
include/uapi/linux/stat.h
|
||||||
include/uapi/linux/vhost.h
|
include/uapi/linux/vhost.h
|
||||||
include/uapi/sound/asound.h
|
include/uapi/sound/asound.h
|
||||||
|
include/linux/bits.h
|
||||||
include/linux/hash.h
|
include/linux/hash.h
|
||||||
include/uapi/linux/hw_breakpoint.h
|
include/uapi/linux/hw_breakpoint.h
|
||||||
arch/x86/include/asm/disabled-features.h
|
arch/x86/include/asm/disabled-features.h
|
||||||
|
|||||||
Reference in New Issue
Block a user