mirror of
https://github.com/Fishwaldo/build.git
synced 2025-03-21 22:31:51 +00:00
7549 lines
248 KiB
Diff
7549 lines
248 KiB
Diff
diff --git a/Documentation/devicetree/bindings/ata/sata_rcar.txt b/Documentation/devicetree/bindings/ata/sata_rcar.txt
|
|
index 1e6111333fa8..7dd32d321a34 100644
|
|
--- a/Documentation/devicetree/bindings/ata/sata_rcar.txt
|
|
+++ b/Documentation/devicetree/bindings/ata/sata_rcar.txt
|
|
@@ -3,7 +3,8 @@
|
|
Required properties:
|
|
- compatible : should contain one of the following:
|
|
- "renesas,sata-r8a7779" for R-Car H1
|
|
- - "renesas,sata-r8a7790" for R-Car H2
|
|
+ - "renesas,sata-r8a7790-es1" for R-Car H2 ES1
|
|
+ - "renesas,sata-r8a7790" for R-Car H2 other than ES1
|
|
- "renesas,sata-r8a7791" for R-Car M2
|
|
- reg : address and length of the SATA registers;
|
|
- interrupts : must consist of one interrupt specifier.
|
|
diff --git a/Makefile b/Makefile
|
|
index 8fd06101c482..eb96e40238f7 100644
|
|
--- a/Makefile
|
|
+++ b/Makefile
|
|
@@ -1,6 +1,6 @@
|
|
VERSION = 3
|
|
PATCHLEVEL = 14
|
|
-SUBLEVEL = 24
|
|
+SUBLEVEL = 25
|
|
EXTRAVERSION =
|
|
NAME = Remembering Coco
|
|
|
|
diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S
|
|
index 066b03480b63..8017cde13648 100644
|
|
--- a/arch/arm/boot/compressed/head.S
|
|
+++ b/arch/arm/boot/compressed/head.S
|
|
@@ -400,8 +400,7 @@ dtb_check_done:
|
|
add sp, sp, r6
|
|
#endif
|
|
|
|
- tst r4, #1
|
|
- bleq cache_clean_flush
|
|
+ bl cache_clean_flush
|
|
|
|
adr r0, BSYM(restart)
|
|
add r0, r0, r6
|
|
@@ -1050,6 +1049,8 @@ cache_clean_flush:
|
|
b call_cache_fn
|
|
|
|
__armv4_mpu_cache_flush:
|
|
+ tst r4, #1
|
|
+ movne pc, lr
|
|
mov r2, #1
|
|
mov r3, #0
|
|
mcr p15, 0, ip, c7, c6, 0 @ invalidate D cache
|
|
@@ -1067,6 +1068,8 @@ __armv4_mpu_cache_flush:
|
|
mov pc, lr
|
|
|
|
__fa526_cache_flush:
|
|
+ tst r4, #1
|
|
+ movne pc, lr
|
|
mov r1, #0
|
|
mcr p15, 0, r1, c7, c14, 0 @ clean and invalidate D cache
|
|
mcr p15, 0, r1, c7, c5, 0 @ flush I cache
|
|
@@ -1075,13 +1078,16 @@ __fa526_cache_flush:
|
|
|
|
__armv6_mmu_cache_flush:
|
|
mov r1, #0
|
|
- mcr p15, 0, r1, c7, c14, 0 @ clean+invalidate D
|
|
+ tst r4, #1
|
|
+ mcreq p15, 0, r1, c7, c14, 0 @ clean+invalidate D
|
|
mcr p15, 0, r1, c7, c5, 0 @ invalidate I+BTB
|
|
- mcr p15, 0, r1, c7, c15, 0 @ clean+invalidate unified
|
|
+ mcreq p15, 0, r1, c7, c15, 0 @ clean+invalidate unified
|
|
mcr p15, 0, r1, c7, c10, 4 @ drain WB
|
|
mov pc, lr
|
|
|
|
__armv7_mmu_cache_flush:
|
|
+ tst r4, #1
|
|
+ bne iflush
|
|
mrc p15, 0, r10, c0, c1, 5 @ read ID_MMFR1
|
|
tst r10, #0xf << 16 @ hierarchical cache (ARMv7)
|
|
mov r10, #0
|
|
@@ -1142,6 +1148,8 @@ iflush:
|
|
mov pc, lr
|
|
|
|
__armv5tej_mmu_cache_flush:
|
|
+ tst r4, #1
|
|
+ movne pc, lr
|
|
1: mrc p15, 0, r15, c7, c14, 3 @ test,clean,invalidate D cache
|
|
bne 1b
|
|
mcr p15, 0, r0, c7, c5, 0 @ flush I cache
|
|
@@ -1149,6 +1157,8 @@ __armv5tej_mmu_cache_flush:
|
|
mov pc, lr
|
|
|
|
__armv4_mmu_cache_flush:
|
|
+ tst r4, #1
|
|
+ movne pc, lr
|
|
mov r2, #64*1024 @ default: 32K dcache size (*2)
|
|
mov r11, #32 @ default: 32 byte line size
|
|
mrc p15, 0, r3, c0, c0, 1 @ read cache type
|
|
@@ -1182,6 +1192,8 @@ no_cache_id:
|
|
|
|
__armv3_mmu_cache_flush:
|
|
__armv3_mpu_cache_flush:
|
|
+ tst r4, #1
|
|
+ movne pc, lr
|
|
mov r1, #0
|
|
mcr p15, 0, r1, c7, c0, 0 @ invalidate whole cache v3
|
|
mov pc, lr
|
|
diff --git a/arch/arm/kernel/kprobes-common.c b/arch/arm/kernel/kprobes-common.c
|
|
index 18a76282970e..380c20fb9c85 100644
|
|
--- a/arch/arm/kernel/kprobes-common.c
|
|
+++ b/arch/arm/kernel/kprobes-common.c
|
|
@@ -14,6 +14,7 @@
|
|
#include <linux/kernel.h>
|
|
#include <linux/kprobes.h>
|
|
#include <asm/system_info.h>
|
|
+#include <asm/opcodes.h>
|
|
|
|
#include "kprobes.h"
|
|
|
|
@@ -305,7 +306,8 @@ kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_specific_insn *asi)
|
|
|
|
if (handler) {
|
|
/* We can emulate the instruction in (possibly) modified form */
|
|
- asi->insn[0] = (insn & 0xfff00000) | (rn << 16) | reglist;
|
|
+ asi->insn[0] = __opcode_to_mem_arm((insn & 0xfff00000) |
|
|
+ (rn << 16) | reglist);
|
|
asi->insn_handler = handler;
|
|
return INSN_GOOD;
|
|
}
|
|
@@ -334,13 +336,14 @@ prepare_emulated_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
|
|
#ifdef CONFIG_THUMB2_KERNEL
|
|
if (thumb) {
|
|
u16 *thumb_insn = (u16 *)asi->insn;
|
|
- thumb_insn[1] = 0x4770; /* Thumb bx lr */
|
|
- thumb_insn[2] = 0x4770; /* Thumb bx lr */
|
|
+ /* Thumb bx lr */
|
|
+ thumb_insn[1] = __opcode_to_mem_thumb16(0x4770);
|
|
+ thumb_insn[2] = __opcode_to_mem_thumb16(0x4770);
|
|
return insn;
|
|
}
|
|
- asi->insn[1] = 0xe12fff1e; /* ARM bx lr */
|
|
+ asi->insn[1] = __opcode_to_mem_arm(0xe12fff1e); /* ARM bx lr */
|
|
#else
|
|
- asi->insn[1] = 0xe1a0f00e; /* mov pc, lr */
|
|
+ asi->insn[1] = __opcode_to_mem_arm(0xe1a0f00e); /* mov pc, lr */
|
|
#endif
|
|
/* Make an ARM instruction unconditional */
|
|
if (insn < 0xe0000000)
|
|
@@ -360,12 +363,12 @@ set_emulated_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
|
|
if (thumb) {
|
|
u16 *ip = (u16 *)asi->insn;
|
|
if (is_wide_instruction(insn))
|
|
- *ip++ = insn >> 16;
|
|
- *ip++ = insn;
|
|
+ *ip++ = __opcode_to_mem_thumb16(insn >> 16);
|
|
+ *ip++ = __opcode_to_mem_thumb16(insn);
|
|
return;
|
|
}
|
|
#endif
|
|
- asi->insn[0] = insn;
|
|
+ asi->insn[0] = __opcode_to_mem_arm(insn);
|
|
}
|
|
|
|
/*
|
|
diff --git a/arch/arm/kernel/kprobes-thumb.c b/arch/arm/kernel/kprobes-thumb.c
|
|
index 6123daf397a7..241222c66a13 100644
|
|
--- a/arch/arm/kernel/kprobes-thumb.c
|
|
+++ b/arch/arm/kernel/kprobes-thumb.c
|
|
@@ -11,6 +11,7 @@
|
|
#include <linux/kernel.h>
|
|
#include <linux/kprobes.h>
|
|
#include <linux/module.h>
|
|
+#include <asm/opcodes.h>
|
|
|
|
#include "kprobes.h"
|
|
|
|
@@ -163,9 +164,9 @@ t32_decode_ldmstm(kprobe_opcode_t insn, struct arch_specific_insn *asi)
|
|
enum kprobe_insn ret = kprobe_decode_ldmstm(insn, asi);
|
|
|
|
/* Fixup modified instruction to have halfwords in correct order...*/
|
|
- insn = asi->insn[0];
|
|
- ((u16 *)asi->insn)[0] = insn >> 16;
|
|
- ((u16 *)asi->insn)[1] = insn & 0xffff;
|
|
+ insn = __mem_to_opcode_arm(asi->insn[0]);
|
|
+ ((u16 *)asi->insn)[0] = __opcode_to_mem_thumb16(insn >> 16);
|
|
+ ((u16 *)asi->insn)[1] = __opcode_to_mem_thumb16(insn & 0xffff);
|
|
|
|
return ret;
|
|
}
|
|
@@ -1153,7 +1154,7 @@ t16_decode_hiregs(kprobe_opcode_t insn, struct arch_specific_insn *asi)
|
|
{
|
|
insn &= ~0x00ff;
|
|
insn |= 0x001; /* Set Rdn = R1 and Rm = R0 */
|
|
- ((u16 *)asi->insn)[0] = insn;
|
|
+ ((u16 *)asi->insn)[0] = __opcode_to_mem_thumb16(insn);
|
|
asi->insn_handler = t16_emulate_hiregs;
|
|
return INSN_GOOD;
|
|
}
|
|
@@ -1182,8 +1183,10 @@ t16_decode_push(kprobe_opcode_t insn, struct arch_specific_insn *asi)
|
|
* and call it with R9=SP and LR in the register list represented
|
|
* by R8.
|
|
*/
|
|
- ((u16 *)asi->insn)[0] = 0xe929; /* 1st half STMDB R9!,{} */
|
|
- ((u16 *)asi->insn)[1] = insn & 0x1ff; /* 2nd half (register list) */
|
|
+ /* 1st half STMDB R9!,{} */
|
|
+ ((u16 *)asi->insn)[0] = __opcode_to_mem_thumb16(0xe929);
|
|
+ /* 2nd half (register list) */
|
|
+ ((u16 *)asi->insn)[1] = __opcode_to_mem_thumb16(insn & 0x1ff);
|
|
asi->insn_handler = t16_emulate_push;
|
|
return INSN_GOOD;
|
|
}
|
|
@@ -1232,8 +1235,10 @@ t16_decode_pop(kprobe_opcode_t insn, struct arch_specific_insn *asi)
|
|
* and call it with R9=SP and PC in the register list represented
|
|
* by R8.
|
|
*/
|
|
- ((u16 *)asi->insn)[0] = 0xe8b9; /* 1st half LDMIA R9!,{} */
|
|
- ((u16 *)asi->insn)[1] = insn & 0x1ff; /* 2nd half (register list) */
|
|
+ /* 1st half LDMIA R9!,{} */
|
|
+ ((u16 *)asi->insn)[0] = __opcode_to_mem_thumb16(0xe8b9);
|
|
+ /* 2nd half (register list) */
|
|
+ ((u16 *)asi->insn)[1] = __opcode_to_mem_thumb16(insn & 0x1ff);
|
|
asi->insn_handler = insn & 0x100 ? t16_emulate_pop_pc
|
|
: t16_emulate_pop_nopc;
|
|
return INSN_GOOD;
|
|
diff --git a/arch/arm/kernel/kprobes.c b/arch/arm/kernel/kprobes.c
|
|
index a7b621ece23d..49a87b6d0bf3 100644
|
|
--- a/arch/arm/kernel/kprobes.c
|
|
+++ b/arch/arm/kernel/kprobes.c
|
|
@@ -26,6 +26,7 @@
|
|
#include <linux/stop_machine.h>
|
|
#include <linux/stringify.h>
|
|
#include <asm/traps.h>
|
|
+#include <asm/opcodes.h>
|
|
#include <asm/cacheflush.h>
|
|
|
|
#include "kprobes.h"
|
|
@@ -62,10 +63,10 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
|
|
#ifdef CONFIG_THUMB2_KERNEL
|
|
thumb = true;
|
|
addr &= ~1; /* Bit 0 would normally be set to indicate Thumb code */
|
|
- insn = ((u16 *)addr)[0];
|
|
+ insn = __mem_to_opcode_thumb16(((u16 *)addr)[0]);
|
|
if (is_wide_instruction(insn)) {
|
|
- insn <<= 16;
|
|
- insn |= ((u16 *)addr)[1];
|
|
+ u16 inst2 = __mem_to_opcode_thumb16(((u16 *)addr)[1]);
|
|
+ insn = __opcode_thumb32_compose(insn, inst2);
|
|
decode_insn = thumb32_kprobe_decode_insn;
|
|
} else
|
|
decode_insn = thumb16_kprobe_decode_insn;
|
|
@@ -73,7 +74,7 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
|
|
thumb = false;
|
|
if (addr & 0x3)
|
|
return -EINVAL;
|
|
- insn = *p->addr;
|
|
+ insn = __mem_to_opcode_arm(*p->addr);
|
|
decode_insn = arm_kprobe_decode_insn;
|
|
#endif
|
|
|
|
diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig
|
|
index ca8ecdee47d8..e9c290c21744 100644
|
|
--- a/arch/arm/mm/Kconfig
|
|
+++ b/arch/arm/mm/Kconfig
|
|
@@ -798,6 +798,7 @@ config NEED_KUSER_HELPERS
|
|
|
|
config KUSER_HELPERS
|
|
bool "Enable kuser helpers in vector page" if !NEED_KUSER_HELPERS
|
|
+ depends on MMU
|
|
default y
|
|
help
|
|
Warning: disabling this option may break user programs.
|
|
diff --git a/arch/arm64/kernel/insn.c b/arch/arm64/kernel/insn.c
|
|
index 92f36835486b..565e26f23f31 100644
|
|
--- a/arch/arm64/kernel/insn.c
|
|
+++ b/arch/arm64/kernel/insn.c
|
|
@@ -156,9 +156,10 @@ static int __kprobes aarch64_insn_patch_text_cb(void *arg)
|
|
* which ends with "dsb; isb" pair guaranteeing global
|
|
* visibility.
|
|
*/
|
|
- atomic_set(&pp->cpu_count, -1);
|
|
+ /* Notify other processors with an additional increment. */
|
|
+ atomic_inc(&pp->cpu_count);
|
|
} else {
|
|
- while (atomic_read(&pp->cpu_count) != -1)
|
|
+ while (atomic_read(&pp->cpu_count) <= num_online_cpus())
|
|
cpu_relax();
|
|
isb();
|
|
}
|
|
diff --git a/arch/arm64/lib/clear_user.S b/arch/arm64/lib/clear_user.S
|
|
index 6e0ed93d51fe..c17967fdf5f6 100644
|
|
--- a/arch/arm64/lib/clear_user.S
|
|
+++ b/arch/arm64/lib/clear_user.S
|
|
@@ -46,7 +46,7 @@ USER(9f, strh wzr, [x0], #2 )
|
|
sub x1, x1, #2
|
|
4: adds x1, x1, #1
|
|
b.mi 5f
|
|
- strb wzr, [x0]
|
|
+USER(9f, strb wzr, [x0] )
|
|
5: mov x0, #0
|
|
ret
|
|
ENDPROC(__clear_user)
|
|
diff --git a/arch/parisc/include/uapi/asm/shmbuf.h b/arch/parisc/include/uapi/asm/shmbuf.h
|
|
index 0a3eada1863b..f395cde7b593 100644
|
|
--- a/arch/parisc/include/uapi/asm/shmbuf.h
|
|
+++ b/arch/parisc/include/uapi/asm/shmbuf.h
|
|
@@ -36,23 +36,16 @@ struct shmid64_ds {
|
|
unsigned int __unused2;
|
|
};
|
|
|
|
-#ifdef CONFIG_64BIT
|
|
-/* The 'unsigned int' (formerly 'unsigned long') data types below will
|
|
- * ensure that a 32-bit app calling shmctl(*,IPC_INFO,*) will work on
|
|
- * a wide kernel, but if some of these values are meant to contain pointers
|
|
- * they may need to be 'long long' instead. -PB XXX FIXME
|
|
- */
|
|
-#endif
|
|
struct shminfo64 {
|
|
- unsigned int shmmax;
|
|
- unsigned int shmmin;
|
|
- unsigned int shmmni;
|
|
- unsigned int shmseg;
|
|
- unsigned int shmall;
|
|
- unsigned int __unused1;
|
|
- unsigned int __unused2;
|
|
- unsigned int __unused3;
|
|
- unsigned int __unused4;
|
|
+ unsigned long shmmax;
|
|
+ unsigned long shmmin;
|
|
+ unsigned long shmmni;
|
|
+ unsigned long shmseg;
|
|
+ unsigned long shmall;
|
|
+ unsigned long __unused1;
|
|
+ unsigned long __unused2;
|
|
+ unsigned long __unused3;
|
|
+ unsigned long __unused4;
|
|
};
|
|
|
|
#endif /* _PARISC_SHMBUF_H */
|
|
diff --git a/arch/parisc/kernel/syscall_table.S b/arch/parisc/kernel/syscall_table.S
|
|
index 7dd8a3b22147..fc77d53e2ca5 100644
|
|
--- a/arch/parisc/kernel/syscall_table.S
|
|
+++ b/arch/parisc/kernel/syscall_table.S
|
|
@@ -286,11 +286,11 @@
|
|
ENTRY_COMP(msgsnd)
|
|
ENTRY_COMP(msgrcv)
|
|
ENTRY_SAME(msgget) /* 190 */
|
|
- ENTRY_SAME(msgctl)
|
|
- ENTRY_SAME(shmat)
|
|
+ ENTRY_COMP(msgctl)
|
|
+ ENTRY_COMP(shmat)
|
|
ENTRY_SAME(shmdt)
|
|
ENTRY_SAME(shmget)
|
|
- ENTRY_SAME(shmctl) /* 195 */
|
|
+ ENTRY_COMP(shmctl) /* 195 */
|
|
ENTRY_SAME(ni_syscall) /* streams1 */
|
|
ENTRY_SAME(ni_syscall) /* streams2 */
|
|
ENTRY_SAME(lstat64)
|
|
@@ -323,7 +323,7 @@
|
|
ENTRY_SAME(epoll_ctl) /* 225 */
|
|
ENTRY_SAME(epoll_wait)
|
|
ENTRY_SAME(remap_file_pages)
|
|
- ENTRY_SAME(semtimedop)
|
|
+ ENTRY_COMP(semtimedop)
|
|
ENTRY_COMP(mq_open)
|
|
ENTRY_SAME(mq_unlink) /* 230 */
|
|
ENTRY_COMP(mq_timedsend)
|
|
diff --git a/arch/sparc/include/asm/atomic_32.h b/arch/sparc/include/asm/atomic_32.h
|
|
index 905832aa9e9e..a0ed182ae73c 100644
|
|
--- a/arch/sparc/include/asm/atomic_32.h
|
|
+++ b/arch/sparc/include/asm/atomic_32.h
|
|
@@ -21,7 +21,7 @@
|
|
|
|
extern int __atomic_add_return(int, atomic_t *);
|
|
extern int atomic_cmpxchg(atomic_t *, int, int);
|
|
-#define atomic_xchg(v, new) (xchg(&((v)->counter), new))
|
|
+extern int atomic_xchg(atomic_t *, int);
|
|
extern int __atomic_add_unless(atomic_t *, int, int);
|
|
extern void atomic_set(atomic_t *, int);
|
|
|
|
diff --git a/arch/sparc/include/asm/cmpxchg_32.h b/arch/sparc/include/asm/cmpxchg_32.h
|
|
index 1fae1a02e3c2..ae0f9a7a314d 100644
|
|
--- a/arch/sparc/include/asm/cmpxchg_32.h
|
|
+++ b/arch/sparc/include/asm/cmpxchg_32.h
|
|
@@ -11,22 +11,14 @@
|
|
#ifndef __ARCH_SPARC_CMPXCHG__
|
|
#define __ARCH_SPARC_CMPXCHG__
|
|
|
|
-static inline unsigned long xchg_u32(__volatile__ unsigned long *m, unsigned long val)
|
|
-{
|
|
- __asm__ __volatile__("swap [%2], %0"
|
|
- : "=&r" (val)
|
|
- : "0" (val), "r" (m)
|
|
- : "memory");
|
|
- return val;
|
|
-}
|
|
-
|
|
+extern unsigned long __xchg_u32(volatile u32 *m, u32 new);
|
|
extern void __xchg_called_with_bad_pointer(void);
|
|
|
|
static inline unsigned long __xchg(unsigned long x, __volatile__ void * ptr, int size)
|
|
{
|
|
switch (size) {
|
|
case 4:
|
|
- return xchg_u32(ptr, x);
|
|
+ return __xchg_u32(ptr, x);
|
|
}
|
|
__xchg_called_with_bad_pointer();
|
|
return x;
|
|
diff --git a/arch/sparc/include/asm/vio.h b/arch/sparc/include/asm/vio.h
|
|
index 432afa838861..55841c184e6d 100644
|
|
--- a/arch/sparc/include/asm/vio.h
|
|
+++ b/arch/sparc/include/asm/vio.h
|
|
@@ -118,12 +118,18 @@ struct vio_disk_attr_info {
|
|
u8 vdisk_type;
|
|
#define VD_DISK_TYPE_SLICE 0x01 /* Slice in block device */
|
|
#define VD_DISK_TYPE_DISK 0x02 /* Entire block device */
|
|
- u16 resv1;
|
|
+ u8 vdisk_mtype; /* v1.1 */
|
|
+#define VD_MEDIA_TYPE_FIXED 0x01 /* Fixed device */
|
|
+#define VD_MEDIA_TYPE_CD 0x02 /* CD Device */
|
|
+#define VD_MEDIA_TYPE_DVD 0x03 /* DVD Device */
|
|
+ u8 resv1;
|
|
u32 vdisk_block_size;
|
|
u64 operations;
|
|
- u64 vdisk_size;
|
|
+ u64 vdisk_size; /* v1.1 */
|
|
u64 max_xfer_size;
|
|
- u64 resv2[2];
|
|
+ u32 phys_block_size; /* v1.2 */
|
|
+ u32 resv2;
|
|
+ u64 resv3[1];
|
|
};
|
|
|
|
struct vio_disk_desc {
|
|
@@ -259,7 +265,7 @@ static inline u32 vio_dring_avail(struct vio_dring_state *dr,
|
|
unsigned int ring_size)
|
|
{
|
|
return (dr->pending -
|
|
- ((dr->prod - dr->cons) & (ring_size - 1)));
|
|
+ ((dr->prod - dr->cons) & (ring_size - 1)) - 1);
|
|
}
|
|
|
|
#define VIO_MAX_TYPE_LEN 32
|
|
diff --git a/arch/sparc/kernel/pci_schizo.c b/arch/sparc/kernel/pci_schizo.c
|
|
index 8f76f23dac38..f9c6813c132d 100644
|
|
--- a/arch/sparc/kernel/pci_schizo.c
|
|
+++ b/arch/sparc/kernel/pci_schizo.c
|
|
@@ -581,7 +581,7 @@ static irqreturn_t schizo_pcierr_intr_other(struct pci_pbm_info *pbm)
|
|
{
|
|
unsigned long csr_reg, csr, csr_error_bits;
|
|
irqreturn_t ret = IRQ_NONE;
|
|
- u16 stat;
|
|
+ u32 stat;
|
|
|
|
csr_reg = pbm->pbm_regs + SCHIZO_PCI_CTRL;
|
|
csr = upa_readq(csr_reg);
|
|
@@ -617,7 +617,7 @@ static irqreturn_t schizo_pcierr_intr_other(struct pci_pbm_info *pbm)
|
|
pbm->name);
|
|
ret = IRQ_HANDLED;
|
|
}
|
|
- pci_read_config_word(pbm->pci_bus->self, PCI_STATUS, &stat);
|
|
+ pbm->pci_ops->read(pbm->pci_bus, 0, PCI_STATUS, 2, &stat);
|
|
if (stat & (PCI_STATUS_PARITY |
|
|
PCI_STATUS_SIG_TARGET_ABORT |
|
|
PCI_STATUS_REC_TARGET_ABORT |
|
|
@@ -625,7 +625,7 @@ static irqreturn_t schizo_pcierr_intr_other(struct pci_pbm_info *pbm)
|
|
PCI_STATUS_SIG_SYSTEM_ERROR)) {
|
|
printk("%s: PCI bus error, PCI_STATUS[%04x]\n",
|
|
pbm->name, stat);
|
|
- pci_write_config_word(pbm->pci_bus->self, PCI_STATUS, 0xffff);
|
|
+ pbm->pci_ops->write(pbm->pci_bus, 0, PCI_STATUS, 2, 0xffff);
|
|
ret = IRQ_HANDLED;
|
|
}
|
|
return ret;
|
|
diff --git a/arch/sparc/kernel/smp_64.c b/arch/sparc/kernel/smp_64.c
|
|
index 50c3dd03be31..9af0a5dbb36d 100644
|
|
--- a/arch/sparc/kernel/smp_64.c
|
|
+++ b/arch/sparc/kernel/smp_64.c
|
|
@@ -823,13 +823,17 @@ void arch_send_call_function_single_ipi(int cpu)
|
|
void __irq_entry smp_call_function_client(int irq, struct pt_regs *regs)
|
|
{
|
|
clear_softint(1 << irq);
|
|
+ irq_enter();
|
|
generic_smp_call_function_interrupt();
|
|
+ irq_exit();
|
|
}
|
|
|
|
void __irq_entry smp_call_function_single_client(int irq, struct pt_regs *regs)
|
|
{
|
|
clear_softint(1 << irq);
|
|
+ irq_enter();
|
|
generic_smp_call_function_single_interrupt();
|
|
+ irq_exit();
|
|
}
|
|
|
|
static void tsb_sync(void *info)
|
|
diff --git a/arch/sparc/lib/atomic32.c b/arch/sparc/lib/atomic32.c
|
|
index 1d32b54089aa..8f2f94d53434 100644
|
|
--- a/arch/sparc/lib/atomic32.c
|
|
+++ b/arch/sparc/lib/atomic32.c
|
|
@@ -40,6 +40,19 @@ int __atomic_add_return(int i, atomic_t *v)
|
|
}
|
|
EXPORT_SYMBOL(__atomic_add_return);
|
|
|
|
+int atomic_xchg(atomic_t *v, int new)
|
|
+{
|
|
+ int ret;
|
|
+ unsigned long flags;
|
|
+
|
|
+ spin_lock_irqsave(ATOMIC_HASH(v), flags);
|
|
+ ret = v->counter;
|
|
+ v->counter = new;
|
|
+ spin_unlock_irqrestore(ATOMIC_HASH(v), flags);
|
|
+ return ret;
|
|
+}
|
|
+EXPORT_SYMBOL(atomic_xchg);
|
|
+
|
|
int atomic_cmpxchg(atomic_t *v, int old, int new)
|
|
{
|
|
int ret;
|
|
@@ -132,3 +145,17 @@ unsigned long __cmpxchg_u32(volatile u32 *ptr, u32 old, u32 new)
|
|
return (unsigned long)prev;
|
|
}
|
|
EXPORT_SYMBOL(__cmpxchg_u32);
|
|
+
|
|
+unsigned long __xchg_u32(volatile u32 *ptr, u32 new)
|
|
+{
|
|
+ unsigned long flags;
|
|
+ u32 prev;
|
|
+
|
|
+ spin_lock_irqsave(ATOMIC_HASH(ptr), flags);
|
|
+ prev = *ptr;
|
|
+ *ptr = new;
|
|
+ spin_unlock_irqrestore(ATOMIC_HASH(ptr), flags);
|
|
+
|
|
+ return (unsigned long)prev;
|
|
+}
|
|
+EXPORT_SYMBOL(__xchg_u32);
|
|
diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile
|
|
index 0fcd9133790c..14fe7cba21d1 100644
|
|
--- a/arch/x86/boot/compressed/Makefile
|
|
+++ b/arch/x86/boot/compressed/Makefile
|
|
@@ -75,8 +75,10 @@ suffix-$(CONFIG_KERNEL_XZ) := xz
|
|
suffix-$(CONFIG_KERNEL_LZO) := lzo
|
|
suffix-$(CONFIG_KERNEL_LZ4) := lz4
|
|
|
|
+RUN_SIZE = $(shell objdump -h vmlinux | \
|
|
+ perl $(srctree)/arch/x86/tools/calc_run_size.pl)
|
|
quiet_cmd_mkpiggy = MKPIGGY $@
|
|
- cmd_mkpiggy = $(obj)/mkpiggy $< > $@ || ( rm -f $@ ; false )
|
|
+ cmd_mkpiggy = $(obj)/mkpiggy $< $(RUN_SIZE) > $@ || ( rm -f $@ ; false )
|
|
|
|
targets += piggy.S
|
|
$(obj)/piggy.S: $(obj)/vmlinux.bin.$(suffix-y) $(obj)/mkpiggy FORCE
|
|
diff --git a/arch/x86/boot/compressed/head_32.S b/arch/x86/boot/compressed/head_32.S
|
|
index f45ab7a36fb6..c5b56ed10aff 100644
|
|
--- a/arch/x86/boot/compressed/head_32.S
|
|
+++ b/arch/x86/boot/compressed/head_32.S
|
|
@@ -186,7 +186,8 @@ relocated:
|
|
* Do the decompression, and jump to the new kernel..
|
|
*/
|
|
/* push arguments for decompress_kernel: */
|
|
- pushl $z_output_len /* decompressed length */
|
|
+ pushl $z_run_size /* size of kernel with .bss and .brk */
|
|
+ pushl $z_output_len /* decompressed length, end of relocs */
|
|
leal z_extract_offset_negative(%ebx), %ebp
|
|
pushl %ebp /* output address */
|
|
pushl $z_input_len /* input_len */
|
|
@@ -196,7 +197,7 @@ relocated:
|
|
pushl %eax /* heap area */
|
|
pushl %esi /* real mode pointer */
|
|
call decompress_kernel /* returns kernel location in %eax */
|
|
- addl $24, %esp
|
|
+ addl $28, %esp
|
|
|
|
/*
|
|
* Jump to the decompressed kernel.
|
|
diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S
|
|
index b10fa66a2540..34bbc0911b7c 100644
|
|
--- a/arch/x86/boot/compressed/head_64.S
|
|
+++ b/arch/x86/boot/compressed/head_64.S
|
|
@@ -334,13 +334,16 @@ relocated:
|
|
* Do the decompression, and jump to the new kernel..
|
|
*/
|
|
pushq %rsi /* Save the real mode argument */
|
|
+ movq $z_run_size, %r9 /* size of kernel with .bss and .brk */
|
|
+ pushq %r9
|
|
movq %rsi, %rdi /* real mode address */
|
|
leaq boot_heap(%rip), %rsi /* malloc area for uncompression */
|
|
leaq input_data(%rip), %rdx /* input_data */
|
|
movl $z_input_len, %ecx /* input_len */
|
|
movq %rbp, %r8 /* output target address */
|
|
- movq $z_output_len, %r9 /* decompressed length */
|
|
+ movq $z_output_len, %r9 /* decompressed length, end of relocs */
|
|
call decompress_kernel /* returns kernel location in %rax */
|
|
+ popq %r9
|
|
popq %rsi
|
|
|
|
/*
|
|
diff --git a/arch/x86/boot/compressed/misc.c b/arch/x86/boot/compressed/misc.c
|
|
index 196eaf373a06..eb25ca1eb6da 100644
|
|
--- a/arch/x86/boot/compressed/misc.c
|
|
+++ b/arch/x86/boot/compressed/misc.c
|
|
@@ -393,7 +393,8 @@ asmlinkage void *decompress_kernel(void *rmode, memptr heap,
|
|
unsigned char *input_data,
|
|
unsigned long input_len,
|
|
unsigned char *output,
|
|
- unsigned long output_len)
|
|
+ unsigned long output_len,
|
|
+ unsigned long run_size)
|
|
{
|
|
real_mode = rmode;
|
|
|
|
@@ -416,8 +417,14 @@ asmlinkage void *decompress_kernel(void *rmode, memptr heap,
|
|
free_mem_ptr = heap; /* Heap */
|
|
free_mem_end_ptr = heap + BOOT_HEAP_SIZE;
|
|
|
|
- output = choose_kernel_location(input_data, input_len,
|
|
- output, output_len);
|
|
+ /*
|
|
+ * The memory hole needed for the kernel is the larger of either
|
|
+ * the entire decompressed kernel plus relocation table, or the
|
|
+ * entire decompressed kernel plus .bss and .brk sections.
|
|
+ */
|
|
+ output = choose_kernel_location(input_data, input_len, output,
|
|
+ output_len > run_size ? output_len
|
|
+ : run_size);
|
|
|
|
/* Validate memory location choices. */
|
|
if ((unsigned long)output & (MIN_KERNEL_ALIGN - 1))
|
|
diff --git a/arch/x86/boot/compressed/mkpiggy.c b/arch/x86/boot/compressed/mkpiggy.c
|
|
index b669ab65bf6c..d8222f213182 100644
|
|
--- a/arch/x86/boot/compressed/mkpiggy.c
|
|
+++ b/arch/x86/boot/compressed/mkpiggy.c
|
|
@@ -36,11 +36,13 @@ int main(int argc, char *argv[])
|
|
uint32_t olen;
|
|
long ilen;
|
|
unsigned long offs;
|
|
+ unsigned long run_size;
|
|
FILE *f = NULL;
|
|
int retval = 1;
|
|
|
|
- if (argc < 2) {
|
|
- fprintf(stderr, "Usage: %s compressed_file\n", argv[0]);
|
|
+ if (argc < 3) {
|
|
+ fprintf(stderr, "Usage: %s compressed_file run_size\n",
|
|
+ argv[0]);
|
|
goto bail;
|
|
}
|
|
|
|
@@ -74,6 +76,7 @@ int main(int argc, char *argv[])
|
|
offs += olen >> 12; /* Add 8 bytes for each 32K block */
|
|
offs += 64*1024 + 128; /* Add 64K + 128 bytes slack */
|
|
offs = (offs+4095) & ~4095; /* Round to a 4K boundary */
|
|
+ run_size = atoi(argv[2]);
|
|
|
|
printf(".section \".rodata..compressed\",\"a\",@progbits\n");
|
|
printf(".globl z_input_len\n");
|
|
@@ -85,6 +88,8 @@ int main(int argc, char *argv[])
|
|
/* z_extract_offset_negative allows simplification of head_32.S */
|
|
printf(".globl z_extract_offset_negative\n");
|
|
printf("z_extract_offset_negative = -0x%lx\n", offs);
|
|
+ printf(".globl z_run_size\n");
|
|
+ printf("z_run_size = %lu\n", run_size);
|
|
|
|
printf(".globl input_data, input_data_end\n");
|
|
printf("input_data:\n");
|
|
diff --git a/arch/x86/kernel/cpu/microcode/amd_early.c b/arch/x86/kernel/cpu/microcode/amd_early.c
|
|
index 617a9e284245..b63773ba1646 100644
|
|
--- a/arch/x86/kernel/cpu/microcode/amd_early.c
|
|
+++ b/arch/x86/kernel/cpu/microcode/amd_early.c
|
|
@@ -108,12 +108,13 @@ static size_t compute_container_size(u8 *data, u32 total_size)
|
|
* load_microcode_amd() to save equivalent cpu table and microcode patches in
|
|
* kernel heap memory.
|
|
*/
|
|
-static void apply_ucode_in_initrd(void *ucode, size_t size)
|
|
+static void apply_ucode_in_initrd(void *ucode, size_t size, bool save_patch)
|
|
{
|
|
struct equiv_cpu_entry *eq;
|
|
size_t *cont_sz;
|
|
u32 *header;
|
|
u8 *data, **cont;
|
|
+ u8 (*patch)[PATCH_MAX_SIZE];
|
|
u16 eq_id = 0;
|
|
int offset, left;
|
|
u32 rev, eax, ebx, ecx, edx;
|
|
@@ -123,10 +124,12 @@ static void apply_ucode_in_initrd(void *ucode, size_t size)
|
|
new_rev = (u32 *)__pa_nodebug(&ucode_new_rev);
|
|
cont_sz = (size_t *)__pa_nodebug(&container_size);
|
|
cont = (u8 **)__pa_nodebug(&container);
|
|
+ patch = (u8 (*)[PATCH_MAX_SIZE])__pa_nodebug(&amd_ucode_patch);
|
|
#else
|
|
new_rev = &ucode_new_rev;
|
|
cont_sz = &container_size;
|
|
cont = &container;
|
|
+ patch = &amd_ucode_patch;
|
|
#endif
|
|
|
|
data = ucode;
|
|
@@ -213,9 +216,9 @@ static void apply_ucode_in_initrd(void *ucode, size_t size)
|
|
rev = mc->hdr.patch_id;
|
|
*new_rev = rev;
|
|
|
|
- /* save ucode patch */
|
|
- memcpy(amd_ucode_patch, mc,
|
|
- min_t(u32, header[1], PATCH_MAX_SIZE));
|
|
+ if (save_patch)
|
|
+ memcpy(patch, mc,
|
|
+ min_t(u32, header[1], PATCH_MAX_SIZE));
|
|
}
|
|
}
|
|
|
|
@@ -246,7 +249,7 @@ void __init load_ucode_amd_bsp(void)
|
|
*data = cp.data;
|
|
*size = cp.size;
|
|
|
|
- apply_ucode_in_initrd(cp.data, cp.size);
|
|
+ apply_ucode_in_initrd(cp.data, cp.size, true);
|
|
}
|
|
|
|
#ifdef CONFIG_X86_32
|
|
@@ -263,7 +266,7 @@ void load_ucode_amd_ap(void)
|
|
size_t *usize;
|
|
void **ucode;
|
|
|
|
- mc = (struct microcode_amd *)__pa(amd_ucode_patch);
|
|
+ mc = (struct microcode_amd *)__pa_nodebug(amd_ucode_patch);
|
|
if (mc->hdr.patch_id && mc->hdr.processor_rev_id) {
|
|
__apply_microcode_amd(mc);
|
|
return;
|
|
@@ -275,7 +278,7 @@ void load_ucode_amd_ap(void)
|
|
if (!*ucode || !*usize)
|
|
return;
|
|
|
|
- apply_ucode_in_initrd(*ucode, *usize);
|
|
+ apply_ucode_in_initrd(*ucode, *usize, false);
|
|
}
|
|
|
|
static void __init collect_cpu_sig_on_bsp(void *arg)
|
|
@@ -339,7 +342,7 @@ void load_ucode_amd_ap(void)
|
|
* AP has a different equivalence ID than BSP, looks like
|
|
* mixed-steppings silicon so go through the ucode blob anew.
|
|
*/
|
|
- apply_ucode_in_initrd(ucode_cpio.data, ucode_cpio.size);
|
|
+ apply_ucode_in_initrd(ucode_cpio.data, ucode_cpio.size, false);
|
|
}
|
|
}
|
|
#endif
|
|
@@ -347,7 +350,9 @@ void load_ucode_amd_ap(void)
|
|
int __init save_microcode_in_initrd_amd(void)
|
|
{
|
|
unsigned long cont;
|
|
+ int retval = 0;
|
|
enum ucode_state ret;
|
|
+ u8 *cont_va;
|
|
u32 eax;
|
|
|
|
if (!container)
|
|
@@ -355,13 +360,15 @@ int __init save_microcode_in_initrd_amd(void)
|
|
|
|
#ifdef CONFIG_X86_32
|
|
get_bsp_sig();
|
|
- cont = (unsigned long)container;
|
|
+ cont = (unsigned long)container;
|
|
+ cont_va = __va(container);
|
|
#else
|
|
/*
|
|
* We need the physical address of the container for both bitness since
|
|
* boot_params.hdr.ramdisk_image is a physical address.
|
|
*/
|
|
- cont = __pa(container);
|
|
+ cont = __pa(container);
|
|
+ cont_va = container;
|
|
#endif
|
|
|
|
/*
|
|
@@ -372,6 +379,8 @@ int __init save_microcode_in_initrd_amd(void)
|
|
if (relocated_ramdisk)
|
|
container = (u8 *)(__va(relocated_ramdisk) +
|
|
(cont - boot_params.hdr.ramdisk_image));
|
|
+ else
|
|
+ container = cont_va;
|
|
|
|
if (ucode_new_rev)
|
|
pr_info("microcode: updated early to new patch_level=0x%08x\n",
|
|
@@ -382,7 +391,7 @@ int __init save_microcode_in_initrd_amd(void)
|
|
|
|
ret = load_microcode_amd(eax, container, container_size);
|
|
if (ret != UCODE_OK)
|
|
- return -EINVAL;
|
|
+ retval = -EINVAL;
|
|
|
|
/*
|
|
* This will be freed any msec now, stash patches for the current
|
|
@@ -391,5 +400,5 @@ int __init save_microcode_in_initrd_amd(void)
|
|
container = NULL;
|
|
container_size = 0;
|
|
|
|
- return 0;
|
|
+ return retval;
|
|
}
|
|
diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c
|
|
index 1340ebfcb467..5ee8064bd1d2 100644
|
|
--- a/arch/x86/kernel/cpu/perf_event_intel.c
|
|
+++ b/arch/x86/kernel/cpu/perf_event_intel.c
|
|
@@ -2475,6 +2475,9 @@ __init int intel_pmu_init(void)
|
|
case 62: /* IvyBridge EP */
|
|
memcpy(hw_cache_event_ids, snb_hw_cache_event_ids,
|
|
sizeof(hw_cache_event_ids));
|
|
+ /* dTLB-load-misses on IVB is different than SNB */
|
|
+ hw_cache_event_ids[C(DTLB)][C(OP_READ)][C(RESULT_MISS)] = 0x8108; /* DTLB_LOAD_MISSES.DEMAND_LD_MISS_CAUSES_A_WALK */
|
|
+
|
|
memcpy(hw_cache_extra_regs, snb_hw_cache_extra_regs,
|
|
sizeof(hw_cache_extra_regs));
|
|
|
|
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
|
|
index 7461f50d5bb1..0686fe313b3b 100644
|
|
--- a/arch/x86/kernel/ptrace.c
|
|
+++ b/arch/x86/kernel/ptrace.c
|
|
@@ -1441,15 +1441,6 @@ void send_sigtrap(struct task_struct *tsk, struct pt_regs *regs,
|
|
force_sig_info(SIGTRAP, &info, tsk);
|
|
}
|
|
|
|
-
|
|
-#ifdef CONFIG_X86_32
|
|
-# define IS_IA32 1
|
|
-#elif defined CONFIG_IA32_EMULATION
|
|
-# define IS_IA32 is_compat_task()
|
|
-#else
|
|
-# define IS_IA32 0
|
|
-#endif
|
|
-
|
|
/*
|
|
* We must return the syscall number to actually look up in the table.
|
|
* This can be -1L to skip running any syscall at all.
|
|
@@ -1487,7 +1478,7 @@ long syscall_trace_enter(struct pt_regs *regs)
|
|
if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
|
|
trace_sys_enter(regs, regs->orig_ax);
|
|
|
|
- if (IS_IA32)
|
|
+ if (is_ia32_task())
|
|
audit_syscall_entry(AUDIT_ARCH_I386,
|
|
regs->orig_ax,
|
|
regs->bx, regs->cx,
|
|
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
|
|
index 51c2851ca243..fab97ade0fc8 100644
|
|
--- a/arch/x86/kvm/x86.c
|
|
+++ b/arch/x86/kvm/x86.c
|
|
@@ -4911,7 +4911,7 @@ static int handle_emulation_failure(struct kvm_vcpu *vcpu)
|
|
|
|
++vcpu->stat.insn_emulation_fail;
|
|
trace_kvm_emulate_insn_failed(vcpu);
|
|
- if (!is_guest_mode(vcpu)) {
|
|
+ if (!is_guest_mode(vcpu) && kvm_x86_ops->get_cpl(vcpu) == 0) {
|
|
vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
|
|
vcpu->run->internal.suberror = KVM_INTERNAL_ERROR_EMULATION;
|
|
vcpu->run->internal.ndata = 0;
|
|
diff --git a/arch/x86/mm/pgtable.c b/arch/x86/mm/pgtable.c
|
|
index c96314abd144..0004ac72dbdd 100644
|
|
--- a/arch/x86/mm/pgtable.c
|
|
+++ b/arch/x86/mm/pgtable.c
|
|
@@ -399,13 +399,20 @@ int pmdp_test_and_clear_young(struct vm_area_struct *vma,
|
|
int ptep_clear_flush_young(struct vm_area_struct *vma,
|
|
unsigned long address, pte_t *ptep)
|
|
{
|
|
- int young;
|
|
-
|
|
- young = ptep_test_and_clear_young(vma, address, ptep);
|
|
- if (young)
|
|
- flush_tlb_page(vma, address);
|
|
-
|
|
- return young;
|
|
+ /*
|
|
+ * On x86 CPUs, clearing the accessed bit without a TLB flush
|
|
+ * doesn't cause data corruption. [ It could cause incorrect
|
|
+ * page aging and the (mistaken) reclaim of hot pages, but the
|
|
+ * chance of that should be relatively low. ]
|
|
+ *
|
|
+ * So as a performance optimization don't flush the TLB when
|
|
+ * clearing the accessed bit, it will eventually be flushed by
|
|
+ * a context switch or a VM operation anyway. [ In the rare
|
|
+ * event of it not getting flushed for a long time the delay
|
|
+ * shouldn't really matter because there's no real memory
|
|
+ * pressure for swapout to react to. ]
|
|
+ */
|
|
+ return ptep_test_and_clear_young(vma, address, ptep);
|
|
}
|
|
|
|
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
|
|
diff --git a/arch/x86/tools/calc_run_size.pl b/arch/x86/tools/calc_run_size.pl
|
|
new file mode 100644
|
|
index 000000000000..0b0b124d3ece
|
|
--- /dev/null
|
|
+++ b/arch/x86/tools/calc_run_size.pl
|
|
@@ -0,0 +1,30 @@
|
|
+#!/usr/bin/perl
|
|
+#
|
|
+# Calculate the amount of space needed to run the kernel, including room for
|
|
+# the .bss and .brk sections.
|
|
+#
|
|
+# Usage:
|
|
+# objdump -h a.out | perl calc_run_size.pl
|
|
+use strict;
|
|
+
|
|
+my $mem_size = 0;
|
|
+my $file_offset = 0;
|
|
+
|
|
+my $sections=" *[0-9]+ \.(?:bss|brk) +";
|
|
+while (<>) {
|
|
+ if (/^$sections([0-9a-f]+) +(?:[0-9a-f]+ +){2}([0-9a-f]+)/) {
|
|
+ my $size = hex($1);
|
|
+ my $offset = hex($2);
|
|
+ $mem_size += $size;
|
|
+ if ($file_offset == 0) {
|
|
+ $file_offset = $offset;
|
|
+ } elsif ($file_offset != $offset) {
|
|
+ die ".bss and .brk lack common file offset\n";
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+if ($file_offset == 0) {
|
|
+ die "Never found .bss or .brk file offset\n";
|
|
+}
|
|
+printf("%d\n", $mem_size + $file_offset);
|
|
diff --git a/arch/xtensa/include/uapi/asm/unistd.h b/arch/xtensa/include/uapi/asm/unistd.h
|
|
index b9395529f02d..50084f7c01c8 100644
|
|
--- a/arch/xtensa/include/uapi/asm/unistd.h
|
|
+++ b/arch/xtensa/include/uapi/asm/unistd.h
|
|
@@ -384,7 +384,8 @@ __SYSCALL(174, sys_chroot, 1)
|
|
#define __NR_pivot_root 175
|
|
__SYSCALL(175, sys_pivot_root, 2)
|
|
#define __NR_umount 176
|
|
-__SYSCALL(176, sys_umount, 2)
|
|
+__SYSCALL(176, sys_oldumount, 1)
|
|
+#define __ARCH_WANT_SYS_OLDUMOUNT
|
|
#define __NR_swapoff 177
|
|
__SYSCALL(177, sys_swapoff, 1)
|
|
#define __NR_sync 178
|
|
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
|
|
index 00663d60f6d4..e662f147d436 100644
|
|
--- a/drivers/ata/ahci.c
|
|
+++ b/drivers/ata/ahci.c
|
|
@@ -61,6 +61,7 @@ enum board_ids {
|
|
/* board IDs by feature in alphabetical order */
|
|
board_ahci,
|
|
board_ahci_ign_iferr,
|
|
+ board_ahci_nomsi,
|
|
board_ahci_noncq,
|
|
board_ahci_nosntf,
|
|
board_ahci_yes_fbs,
|
|
@@ -122,6 +123,13 @@ static const struct ata_port_info ahci_port_info[] = {
|
|
.udma_mask = ATA_UDMA6,
|
|
.port_ops = &ahci_ops,
|
|
},
|
|
+ [board_ahci_nomsi] = {
|
|
+ AHCI_HFLAGS (AHCI_HFLAG_NO_MSI),
|
|
+ .flags = AHCI_FLAG_COMMON,
|
|
+ .pio_mask = ATA_PIO4,
|
|
+ .udma_mask = ATA_UDMA6,
|
|
+ .port_ops = &ahci_ops,
|
|
+ },
|
|
[board_ahci_noncq] = {
|
|
AHCI_HFLAGS (AHCI_HFLAG_NO_NCQ),
|
|
.flags = AHCI_FLAG_COMMON,
|
|
@@ -314,6 +322,11 @@ static const struct pci_device_id ahci_pci_tbl[] = {
|
|
{ PCI_VDEVICE(INTEL, 0x8c87), board_ahci }, /* 9 Series RAID */
|
|
{ PCI_VDEVICE(INTEL, 0x8c8e), board_ahci }, /* 9 Series RAID */
|
|
{ PCI_VDEVICE(INTEL, 0x8c8f), board_ahci }, /* 9 Series RAID */
|
|
+ { PCI_VDEVICE(INTEL, 0xa103), board_ahci }, /* Sunrise Point-H AHCI */
|
|
+ { PCI_VDEVICE(INTEL, 0xa103), board_ahci }, /* Sunrise Point-H RAID */
|
|
+ { PCI_VDEVICE(INTEL, 0xa105), board_ahci }, /* Sunrise Point-H RAID */
|
|
+ { PCI_VDEVICE(INTEL, 0xa107), board_ahci }, /* Sunrise Point-H RAID */
|
|
+ { PCI_VDEVICE(INTEL, 0xa10f), board_ahci }, /* Sunrise Point-H RAID */
|
|
|
|
/* JMicron 360/1/3/5/6, match class to avoid IDE function */
|
|
{ PCI_VENDOR_ID_JMICRON, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
|
|
@@ -476,10 +489,10 @@ static const struct pci_device_id ahci_pci_tbl[] = {
|
|
{ PCI_VDEVICE(ASMEDIA, 0x0612), board_ahci }, /* ASM1062 */
|
|
|
|
/*
|
|
- * Samsung SSDs found on some macbooks. NCQ times out.
|
|
- * https://bugzilla.kernel.org/show_bug.cgi?id=60731
|
|
+ * Samsung SSDs found on some macbooks. NCQ times out if MSI is
|
|
+ * enabled. https://bugzilla.kernel.org/show_bug.cgi?id=60731
|
|
*/
|
|
- { PCI_VDEVICE(SAMSUNG, 0x1600), board_ahci_noncq },
|
|
+ { PCI_VDEVICE(SAMSUNG, 0x1600), board_ahci_nomsi },
|
|
|
|
/* Enmotus */
|
|
{ PCI_DEVICE(0x1c44, 0x8000), board_ahci },
|
|
diff --git a/drivers/ata/sata_rcar.c b/drivers/ata/sata_rcar.c
|
|
index 2b25bd83fc9d..c1ea780fca07 100644
|
|
--- a/drivers/ata/sata_rcar.c
|
|
+++ b/drivers/ata/sata_rcar.c
|
|
@@ -146,6 +146,7 @@
|
|
enum sata_rcar_type {
|
|
RCAR_GEN1_SATA,
|
|
RCAR_GEN2_SATA,
|
|
+ RCAR_R8A7790_ES1_SATA,
|
|
};
|
|
|
|
struct sata_rcar_priv {
|
|
@@ -763,6 +764,9 @@ static void sata_rcar_setup_port(struct ata_host *host)
|
|
ap->udma_mask = ATA_UDMA6;
|
|
ap->flags |= ATA_FLAG_SATA;
|
|
|
|
+ if (priv->type == RCAR_R8A7790_ES1_SATA)
|
|
+ ap->flags |= ATA_FLAG_NO_DIPM;
|
|
+
|
|
ioaddr->cmd_addr = base + SDATA_REG;
|
|
ioaddr->ctl_addr = base + SSDEVCON_REG;
|
|
ioaddr->scr_addr = base + SCRSSTS_REG;
|
|
@@ -792,6 +796,7 @@ static void sata_rcar_init_controller(struct ata_host *host)
|
|
sata_rcar_gen1_phy_init(priv);
|
|
break;
|
|
case RCAR_GEN2_SATA:
|
|
+ case RCAR_R8A7790_ES1_SATA:
|
|
sata_rcar_gen2_phy_init(priv);
|
|
break;
|
|
default:
|
|
@@ -838,6 +843,10 @@ static struct of_device_id sata_rcar_match[] = {
|
|
.data = (void *)RCAR_GEN2_SATA
|
|
},
|
|
{
|
|
+ .compatible = "renesas,sata-r8a7790-es1",
|
|
+ .data = (void *)RCAR_R8A7790_ES1_SATA
|
|
+ },
|
|
+ {
|
|
.compatible = "renesas,sata-r8a7791",
|
|
.data = (void *)RCAR_GEN2_SATA
|
|
},
|
|
@@ -849,6 +858,7 @@ static const struct platform_device_id sata_rcar_id_table[] = {
|
|
{ "sata_rcar", RCAR_GEN1_SATA }, /* Deprecated by "sata-r8a7779" */
|
|
{ "sata-r8a7779", RCAR_GEN1_SATA },
|
|
{ "sata-r8a7790", RCAR_GEN2_SATA },
|
|
+ { "sata-r8a7790-es1", RCAR_R8A7790_ES1_SATA },
|
|
{ "sata-r8a7791", RCAR_GEN2_SATA },
|
|
{ },
|
|
};
|
|
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
|
|
index f6cff3be0ed7..2f9a3d8ecbbf 100644
|
|
--- a/drivers/base/regmap/regmap.c
|
|
+++ b/drivers/base/regmap/regmap.c
|
|
@@ -1557,8 +1557,10 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
|
|
} else {
|
|
void *wval;
|
|
|
|
- if (!val_count)
|
|
- return -EINVAL;
|
|
+ if (!val_count) {
|
|
+ ret = -EINVAL;
|
|
+ goto out;
|
|
+ }
|
|
|
|
wval = kmemdup(val, val_count * val_bytes, GFP_KERNEL);
|
|
if (!wval) {
|
|
diff --git a/drivers/block/sunvdc.c b/drivers/block/sunvdc.c
|
|
index 5814deb6963d..0ebadf93b6c5 100644
|
|
--- a/drivers/block/sunvdc.c
|
|
+++ b/drivers/block/sunvdc.c
|
|
@@ -9,6 +9,7 @@
|
|
#include <linux/blkdev.h>
|
|
#include <linux/hdreg.h>
|
|
#include <linux/genhd.h>
|
|
+#include <linux/cdrom.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/completion.h>
|
|
@@ -22,8 +23,8 @@
|
|
|
|
#define DRV_MODULE_NAME "sunvdc"
|
|
#define PFX DRV_MODULE_NAME ": "
|
|
-#define DRV_MODULE_VERSION "1.0"
|
|
-#define DRV_MODULE_RELDATE "June 25, 2007"
|
|
+#define DRV_MODULE_VERSION "1.1"
|
|
+#define DRV_MODULE_RELDATE "February 13, 2013"
|
|
|
|
static char version[] =
|
|
DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
|
|
@@ -32,7 +33,7 @@ MODULE_DESCRIPTION("Sun LDOM virtual disk client driver");
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_VERSION(DRV_MODULE_VERSION);
|
|
|
|
-#define VDC_TX_RING_SIZE 256
|
|
+#define VDC_TX_RING_SIZE 512
|
|
|
|
#define WAITING_FOR_LINK_UP 0x01
|
|
#define WAITING_FOR_TX_SPACE 0x02
|
|
@@ -65,11 +66,9 @@ struct vdc_port {
|
|
u64 operations;
|
|
u32 vdisk_size;
|
|
u8 vdisk_type;
|
|
+ u8 vdisk_mtype;
|
|
|
|
char disk_name[32];
|
|
-
|
|
- struct vio_disk_geom geom;
|
|
- struct vio_disk_vtoc label;
|
|
};
|
|
|
|
static inline struct vdc_port *to_vdc_port(struct vio_driver_state *vio)
|
|
@@ -79,9 +78,16 @@ static inline struct vdc_port *to_vdc_port(struct vio_driver_state *vio)
|
|
|
|
/* Ordered from largest major to lowest */
|
|
static struct vio_version vdc_versions[] = {
|
|
+ { .major = 1, .minor = 1 },
|
|
{ .major = 1, .minor = 0 },
|
|
};
|
|
|
|
+static inline int vdc_version_supported(struct vdc_port *port,
|
|
+ u16 major, u16 minor)
|
|
+{
|
|
+ return port->vio.ver.major == major && port->vio.ver.minor >= minor;
|
|
+}
|
|
+
|
|
#define VDCBLK_NAME "vdisk"
|
|
static int vdc_major;
|
|
#define PARTITION_SHIFT 3
|
|
@@ -94,18 +100,54 @@ static inline u32 vdc_tx_dring_avail(struct vio_dring_state *dr)
|
|
static int vdc_getgeo(struct block_device *bdev, struct hd_geometry *geo)
|
|
{
|
|
struct gendisk *disk = bdev->bd_disk;
|
|
- struct vdc_port *port = disk->private_data;
|
|
+ sector_t nsect = get_capacity(disk);
|
|
+ sector_t cylinders = nsect;
|
|
|
|
- geo->heads = (u8) port->geom.num_hd;
|
|
- geo->sectors = (u8) port->geom.num_sec;
|
|
- geo->cylinders = port->geom.num_cyl;
|
|
+ geo->heads = 0xff;
|
|
+ geo->sectors = 0x3f;
|
|
+ sector_div(cylinders, geo->heads * geo->sectors);
|
|
+ geo->cylinders = cylinders;
|
|
+ if ((sector_t)(geo->cylinders + 1) * geo->heads * geo->sectors < nsect)
|
|
+ geo->cylinders = 0xffff;
|
|
|
|
return 0;
|
|
}
|
|
|
|
+/* Add ioctl/CDROM_GET_CAPABILITY to support cdrom_id in udev
|
|
+ * when vdisk_mtype is VD_MEDIA_TYPE_CD or VD_MEDIA_TYPE_DVD.
|
|
+ * Needed to be able to install inside an ldom from an iso image.
|
|
+ */
|
|
+static int vdc_ioctl(struct block_device *bdev, fmode_t mode,
|
|
+ unsigned command, unsigned long argument)
|
|
+{
|
|
+ int i;
|
|
+ struct gendisk *disk;
|
|
+
|
|
+ switch (command) {
|
|
+ case CDROMMULTISESSION:
|
|
+ pr_debug(PFX "Multisession CDs not supported\n");
|
|
+ for (i = 0; i < sizeof(struct cdrom_multisession); i++)
|
|
+ if (put_user(0, (char __user *)(argument + i)))
|
|
+ return -EFAULT;
|
|
+ return 0;
|
|
+
|
|
+ case CDROM_GET_CAPABILITY:
|
|
+ disk = bdev->bd_disk;
|
|
+
|
|
+ if (bdev->bd_disk && (disk->flags & GENHD_FL_CD))
|
|
+ return 0;
|
|
+ return -EINVAL;
|
|
+
|
|
+ default:
|
|
+ pr_debug(PFX "ioctl %08x not supported\n", command);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+}
|
|
+
|
|
static const struct block_device_operations vdc_fops = {
|
|
.owner = THIS_MODULE,
|
|
.getgeo = vdc_getgeo,
|
|
+ .ioctl = vdc_ioctl,
|
|
};
|
|
|
|
static void vdc_finish(struct vio_driver_state *vio, int err, int waiting_for)
|
|
@@ -165,9 +207,9 @@ static int vdc_handle_attr(struct vio_driver_state *vio, void *arg)
|
|
struct vio_disk_attr_info *pkt = arg;
|
|
|
|
viodbg(HS, "GOT ATTR stype[0x%x] ops[%llx] disk_size[%llu] disk_type[%x] "
|
|
- "xfer_mode[0x%x] blksz[%u] max_xfer[%llu]\n",
|
|
+ "mtype[0x%x] xfer_mode[0x%x] blksz[%u] max_xfer[%llu]\n",
|
|
pkt->tag.stype, pkt->operations,
|
|
- pkt->vdisk_size, pkt->vdisk_type,
|
|
+ pkt->vdisk_size, pkt->vdisk_type, pkt->vdisk_mtype,
|
|
pkt->xfer_mode, pkt->vdisk_block_size,
|
|
pkt->max_xfer_size);
|
|
|
|
@@ -192,8 +234,11 @@ static int vdc_handle_attr(struct vio_driver_state *vio, void *arg)
|
|
}
|
|
|
|
port->operations = pkt->operations;
|
|
- port->vdisk_size = pkt->vdisk_size;
|
|
port->vdisk_type = pkt->vdisk_type;
|
|
+ if (vdc_version_supported(port, 1, 1)) {
|
|
+ port->vdisk_size = pkt->vdisk_size;
|
|
+ port->vdisk_mtype = pkt->vdisk_mtype;
|
|
+ }
|
|
if (pkt->max_xfer_size < port->max_xfer_size)
|
|
port->max_xfer_size = pkt->max_xfer_size;
|
|
port->vdisk_block_size = pkt->vdisk_block_size;
|
|
@@ -236,7 +281,9 @@ static void vdc_end_one(struct vdc_port *port, struct vio_dring_state *dr,
|
|
|
|
__blk_end_request(req, (desc->status ? -EIO : 0), desc->size);
|
|
|
|
- if (blk_queue_stopped(port->disk->queue))
|
|
+ /* restart blk queue when ring is half emptied */
|
|
+ if (blk_queue_stopped(port->disk->queue) &&
|
|
+ vdc_tx_dring_avail(dr) * 100 / VDC_TX_RING_SIZE >= 50)
|
|
blk_start_queue(port->disk->queue);
|
|
}
|
|
|
|
@@ -388,12 +435,6 @@ static int __send_request(struct request *req)
|
|
for (i = 0; i < nsg; i++)
|
|
len += sg[i].length;
|
|
|
|
- if (unlikely(vdc_tx_dring_avail(dr) < 1)) {
|
|
- blk_stop_queue(port->disk->queue);
|
|
- err = -ENOMEM;
|
|
- goto out;
|
|
- }
|
|
-
|
|
desc = vio_dring_cur(dr);
|
|
|
|
err = ldc_map_sg(port->vio.lp, sg, nsg,
|
|
@@ -433,21 +474,32 @@ static int __send_request(struct request *req)
|
|
port->req_id++;
|
|
dr->prod = (dr->prod + 1) & (VDC_TX_RING_SIZE - 1);
|
|
}
|
|
-out:
|
|
|
|
return err;
|
|
}
|
|
|
|
-static void do_vdc_request(struct request_queue *q)
|
|
+static void do_vdc_request(struct request_queue *rq)
|
|
{
|
|
- while (1) {
|
|
- struct request *req = blk_fetch_request(q);
|
|
+ struct request *req;
|
|
|
|
- if (!req)
|
|
- break;
|
|
+ while ((req = blk_peek_request(rq)) != NULL) {
|
|
+ struct vdc_port *port;
|
|
+ struct vio_dring_state *dr;
|
|
|
|
- if (__send_request(req) < 0)
|
|
- __blk_end_request_all(req, -EIO);
|
|
+ port = req->rq_disk->private_data;
|
|
+ dr = &port->vio.drings[VIO_DRIVER_TX_RING];
|
|
+ if (unlikely(vdc_tx_dring_avail(dr) < 1))
|
|
+ goto wait;
|
|
+
|
|
+ blk_start_request(req);
|
|
+
|
|
+ if (__send_request(req) < 0) {
|
|
+ blk_requeue_request(rq, req);
|
|
+wait:
|
|
+ /* Avoid pointless unplugs. */
|
|
+ blk_stop_queue(rq);
|
|
+ break;
|
|
+ }
|
|
}
|
|
}
|
|
|
|
@@ -656,25 +708,27 @@ static int probe_disk(struct vdc_port *port)
|
|
if (comp.err)
|
|
return comp.err;
|
|
|
|
- err = generic_request(port, VD_OP_GET_VTOC,
|
|
- &port->label, sizeof(port->label));
|
|
- if (err < 0) {
|
|
- printk(KERN_ERR PFX "VD_OP_GET_VTOC returns error %d\n", err);
|
|
- return err;
|
|
- }
|
|
-
|
|
- err = generic_request(port, VD_OP_GET_DISKGEOM,
|
|
- &port->geom, sizeof(port->geom));
|
|
- if (err < 0) {
|
|
- printk(KERN_ERR PFX "VD_OP_GET_DISKGEOM returns "
|
|
- "error %d\n", err);
|
|
- return err;
|
|
+ if (vdc_version_supported(port, 1, 1)) {
|
|
+ /* vdisk_size should be set during the handshake, if it wasn't
|
|
+ * then the underlying disk is reserved by another system
|
|
+ */
|
|
+ if (port->vdisk_size == -1)
|
|
+ return -ENODEV;
|
|
+ } else {
|
|
+ struct vio_disk_geom geom;
|
|
+
|
|
+ err = generic_request(port, VD_OP_GET_DISKGEOM,
|
|
+ &geom, sizeof(geom));
|
|
+ if (err < 0) {
|
|
+ printk(KERN_ERR PFX "VD_OP_GET_DISKGEOM returns "
|
|
+ "error %d\n", err);
|
|
+ return err;
|
|
+ }
|
|
+ port->vdisk_size = ((u64)geom.num_cyl *
|
|
+ (u64)geom.num_hd *
|
|
+ (u64)geom.num_sec);
|
|
}
|
|
|
|
- port->vdisk_size = ((u64)port->geom.num_cyl *
|
|
- (u64)port->geom.num_hd *
|
|
- (u64)port->geom.num_sec);
|
|
-
|
|
q = blk_init_queue(do_vdc_request, &port->vio.lock);
|
|
if (!q) {
|
|
printk(KERN_ERR PFX "%s: Could not allocate queue.\n",
|
|
@@ -691,6 +745,10 @@ static int probe_disk(struct vdc_port *port)
|
|
|
|
port->disk = g;
|
|
|
|
+ /* Each segment in a request is up to an aligned page in size. */
|
|
+ blk_queue_segment_boundary(q, PAGE_SIZE - 1);
|
|
+ blk_queue_max_segment_size(q, PAGE_SIZE);
|
|
+
|
|
blk_queue_max_segments(q, port->ring_cookies);
|
|
blk_queue_max_hw_sectors(q, port->max_xfer_size);
|
|
g->major = vdc_major;
|
|
@@ -704,9 +762,32 @@ static int probe_disk(struct vdc_port *port)
|
|
|
|
set_capacity(g, port->vdisk_size);
|
|
|
|
- printk(KERN_INFO PFX "%s: %u sectors (%u MB)\n",
|
|
+ if (vdc_version_supported(port, 1, 1)) {
|
|
+ switch (port->vdisk_mtype) {
|
|
+ case VD_MEDIA_TYPE_CD:
|
|
+ pr_info(PFX "Virtual CDROM %s\n", port->disk_name);
|
|
+ g->flags |= GENHD_FL_CD;
|
|
+ g->flags |= GENHD_FL_REMOVABLE;
|
|
+ set_disk_ro(g, 1);
|
|
+ break;
|
|
+
|
|
+ case VD_MEDIA_TYPE_DVD:
|
|
+ pr_info(PFX "Virtual DVD %s\n", port->disk_name);
|
|
+ g->flags |= GENHD_FL_CD;
|
|
+ g->flags |= GENHD_FL_REMOVABLE;
|
|
+ set_disk_ro(g, 1);
|
|
+ break;
|
|
+
|
|
+ case VD_MEDIA_TYPE_FIXED:
|
|
+ pr_info(PFX "Virtual Hard disk %s\n", port->disk_name);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ pr_info(PFX "%s: %u sectors (%u MB) protocol %d.%d\n",
|
|
g->disk_name,
|
|
- port->vdisk_size, (port->vdisk_size >> (20 - 9)));
|
|
+ port->vdisk_size, (port->vdisk_size >> (20 - 9)),
|
|
+ port->vio.ver.major, port->vio.ver.minor);
|
|
|
|
add_disk(g);
|
|
|
|
@@ -765,6 +846,7 @@ static int vdc_port_probe(struct vio_dev *vdev, const struct vio_device_id *id)
|
|
else
|
|
snprintf(port->disk_name, sizeof(port->disk_name),
|
|
VDCBLK_NAME "%c", 'a' + ((int)vdev->dev_no % 26));
|
|
+ port->vdisk_size = -1;
|
|
|
|
err = vio_driver_init(&port->vio, vdev, VDEV_DISK,
|
|
vdc_versions, ARRAY_SIZE(vdc_versions),
|
|
diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c
|
|
index 51c557cfd92b..d8ddb8e2adc1 100644
|
|
--- a/drivers/block/zram/zram_drv.c
|
|
+++ b/drivers/block/zram/zram_drv.c
|
|
@@ -447,7 +447,8 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index,
|
|
}
|
|
|
|
if (page_zero_filled(uncmem)) {
|
|
- kunmap_atomic(user_mem);
|
|
+ if (user_mem)
|
|
+ kunmap_atomic(user_mem);
|
|
/* Free memory associated with this sector now. */
|
|
write_lock(&zram->meta->tb_lock);
|
|
zram_free_page(zram, index);
|
|
diff --git a/drivers/char/hw_random/pseries-rng.c b/drivers/char/hw_random/pseries-rng.c
|
|
index ab7ffdec0ec3..f38f2c13e79c 100644
|
|
--- a/drivers/char/hw_random/pseries-rng.c
|
|
+++ b/drivers/char/hw_random/pseries-rng.c
|
|
@@ -25,18 +25,21 @@
|
|
#include <asm/vio.h>
|
|
|
|
|
|
-static int pseries_rng_data_read(struct hwrng *rng, u32 *data)
|
|
+static int pseries_rng_read(struct hwrng *rng, void *data, size_t max, bool wait)
|
|
{
|
|
+ u64 buffer[PLPAR_HCALL_BUFSIZE];
|
|
+ size_t size = max < 8 ? max : 8;
|
|
int rc;
|
|
|
|
- rc = plpar_hcall(H_RANDOM, (unsigned long *)data);
|
|
+ rc = plpar_hcall(H_RANDOM, (unsigned long *)buffer);
|
|
if (rc != H_SUCCESS) {
|
|
pr_err_ratelimited("H_RANDOM call failed %d\n", rc);
|
|
return -EIO;
|
|
}
|
|
+ memcpy(data, buffer, size);
|
|
|
|
/* The hypervisor interface returns 64 bits */
|
|
- return 8;
|
|
+ return size;
|
|
}
|
|
|
|
/**
|
|
@@ -55,7 +58,7 @@ static unsigned long pseries_rng_get_desired_dma(struct vio_dev *vdev)
|
|
|
|
static struct hwrng pseries_rng = {
|
|
.name = KBUILD_MODNAME,
|
|
- .data_read = pseries_rng_data_read,
|
|
+ .read = pseries_rng_read,
|
|
};
|
|
|
|
static int __init pseries_rng_probe(struct vio_dev *dev,
|
|
diff --git a/drivers/crypto/caam/caamhash.c b/drivers/crypto/caam/caamhash.c
|
|
index a4127453baae..d97a03dbf42c 100644
|
|
--- a/drivers/crypto/caam/caamhash.c
|
|
+++ b/drivers/crypto/caam/caamhash.c
|
|
@@ -835,8 +835,9 @@ static int ahash_update_ctx(struct ahash_request *req)
|
|
edesc->sec4_sg + sec4_sg_src_index,
|
|
chained);
|
|
if (*next_buflen) {
|
|
- sg_copy_part(next_buf, req->src, to_hash -
|
|
- *buflen, req->nbytes);
|
|
+ scatterwalk_map_and_copy(next_buf, req->src,
|
|
+ to_hash - *buflen,
|
|
+ *next_buflen, 0);
|
|
state->current_buf = !state->current_buf;
|
|
}
|
|
} else {
|
|
@@ -869,7 +870,8 @@ static int ahash_update_ctx(struct ahash_request *req)
|
|
kfree(edesc);
|
|
}
|
|
} else if (*next_buflen) {
|
|
- sg_copy(buf + *buflen, req->src, req->nbytes);
|
|
+ scatterwalk_map_and_copy(buf + *buflen, req->src, 0,
|
|
+ req->nbytes, 0);
|
|
*buflen = *next_buflen;
|
|
*next_buflen = last_buflen;
|
|
}
|
|
@@ -1216,8 +1218,9 @@ static int ahash_update_no_ctx(struct ahash_request *req)
|
|
src_map_to_sec4_sg(jrdev, req->src, src_nents,
|
|
edesc->sec4_sg + 1, chained);
|
|
if (*next_buflen) {
|
|
- sg_copy_part(next_buf, req->src, to_hash - *buflen,
|
|
- req->nbytes);
|
|
+ scatterwalk_map_and_copy(next_buf, req->src,
|
|
+ to_hash - *buflen,
|
|
+ *next_buflen, 0);
|
|
state->current_buf = !state->current_buf;
|
|
}
|
|
|
|
@@ -1248,7 +1251,8 @@ static int ahash_update_no_ctx(struct ahash_request *req)
|
|
kfree(edesc);
|
|
}
|
|
} else if (*next_buflen) {
|
|
- sg_copy(buf + *buflen, req->src, req->nbytes);
|
|
+ scatterwalk_map_and_copy(buf + *buflen, req->src, 0,
|
|
+ req->nbytes, 0);
|
|
*buflen = *next_buflen;
|
|
*next_buflen = 0;
|
|
}
|
|
@@ -1405,7 +1409,8 @@ static int ahash_update_first(struct ahash_request *req)
|
|
}
|
|
|
|
if (*next_buflen)
|
|
- sg_copy_part(next_buf, req->src, to_hash, req->nbytes);
|
|
+ scatterwalk_map_and_copy(next_buf, req->src, to_hash,
|
|
+ *next_buflen, 0);
|
|
|
|
sh_len = desc_len(sh_desc);
|
|
desc = edesc->hw_desc;
|
|
@@ -1438,7 +1443,8 @@ static int ahash_update_first(struct ahash_request *req)
|
|
state->update = ahash_update_no_ctx;
|
|
state->finup = ahash_finup_no_ctx;
|
|
state->final = ahash_final_no_ctx;
|
|
- sg_copy(next_buf, req->src, req->nbytes);
|
|
+ scatterwalk_map_and_copy(next_buf, req->src, 0,
|
|
+ req->nbytes, 0);
|
|
}
|
|
#ifdef DEBUG
|
|
print_hex_dump(KERN_ERR, "next buf@"__stringify(__LINE__)": ",
|
|
diff --git a/drivers/crypto/caam/key_gen.c b/drivers/crypto/caam/key_gen.c
|
|
index ea2e406610eb..b872eed2957b 100644
|
|
--- a/drivers/crypto/caam/key_gen.c
|
|
+++ b/drivers/crypto/caam/key_gen.c
|
|
@@ -51,23 +51,29 @@ int gen_split_key(struct device *jrdev, u8 *key_out, int split_key_len,
|
|
u32 *desc;
|
|
struct split_key_result result;
|
|
dma_addr_t dma_addr_in, dma_addr_out;
|
|
- int ret = 0;
|
|
+ int ret = -ENOMEM;
|
|
|
|
desc = kmalloc(CAAM_CMD_SZ * 6 + CAAM_PTR_SZ * 2, GFP_KERNEL | GFP_DMA);
|
|
if (!desc) {
|
|
dev_err(jrdev, "unable to allocate key input memory\n");
|
|
- return -ENOMEM;
|
|
+ return ret;
|
|
}
|
|
|
|
- init_job_desc(desc, 0);
|
|
-
|
|
dma_addr_in = dma_map_single(jrdev, (void *)key_in, keylen,
|
|
DMA_TO_DEVICE);
|
|
if (dma_mapping_error(jrdev, dma_addr_in)) {
|
|
dev_err(jrdev, "unable to map key input memory\n");
|
|
- kfree(desc);
|
|
- return -ENOMEM;
|
|
+ goto out_free;
|
|
}
|
|
+
|
|
+ dma_addr_out = dma_map_single(jrdev, key_out, split_key_pad_len,
|
|
+ DMA_FROM_DEVICE);
|
|
+ if (dma_mapping_error(jrdev, dma_addr_out)) {
|
|
+ dev_err(jrdev, "unable to map key output memory\n");
|
|
+ goto out_unmap_in;
|
|
+ }
|
|
+
|
|
+ init_job_desc(desc, 0);
|
|
append_key(desc, dma_addr_in, keylen, CLASS_2 | KEY_DEST_CLASS_REG);
|
|
|
|
/* Sets MDHA up into an HMAC-INIT */
|
|
@@ -84,13 +90,6 @@ int gen_split_key(struct device *jrdev, u8 *key_out, int split_key_len,
|
|
* FIFO_STORE with the explicit split-key content store
|
|
* (0x26 output type)
|
|
*/
|
|
- dma_addr_out = dma_map_single(jrdev, key_out, split_key_pad_len,
|
|
- DMA_FROM_DEVICE);
|
|
- if (dma_mapping_error(jrdev, dma_addr_out)) {
|
|
- dev_err(jrdev, "unable to map key output memory\n");
|
|
- kfree(desc);
|
|
- return -ENOMEM;
|
|
- }
|
|
append_fifo_store(desc, dma_addr_out, split_key_len,
|
|
LDST_CLASS_2_CCB | FIFOST_TYPE_SPLIT_KEK);
|
|
|
|
@@ -118,10 +117,10 @@ int gen_split_key(struct device *jrdev, u8 *key_out, int split_key_len,
|
|
|
|
dma_unmap_single(jrdev, dma_addr_out, split_key_pad_len,
|
|
DMA_FROM_DEVICE);
|
|
+out_unmap_in:
|
|
dma_unmap_single(jrdev, dma_addr_in, keylen, DMA_TO_DEVICE);
|
|
-
|
|
+out_free:
|
|
kfree(desc);
|
|
-
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(gen_split_key);
|
|
diff --git a/drivers/crypto/caam/sg_sw_sec4.h b/drivers/crypto/caam/sg_sw_sec4.h
|
|
index b12ff85f4241..ce28a563effc 100644
|
|
--- a/drivers/crypto/caam/sg_sw_sec4.h
|
|
+++ b/drivers/crypto/caam/sg_sw_sec4.h
|
|
@@ -116,57 +116,3 @@ static int dma_unmap_sg_chained(struct device *dev, struct scatterlist *sg,
|
|
}
|
|
return nents;
|
|
}
|
|
-
|
|
-/* Map SG page in kernel virtual address space and copy */
|
|
-static inline void sg_map_copy(u8 *dest, struct scatterlist *sg,
|
|
- int len, int offset)
|
|
-{
|
|
- u8 *mapped_addr;
|
|
-
|
|
- /*
|
|
- * Page here can be user-space pinned using get_user_pages
|
|
- * Same must be kmapped before use and kunmapped subsequently
|
|
- */
|
|
- mapped_addr = kmap_atomic(sg_page(sg));
|
|
- memcpy(dest, mapped_addr + offset, len);
|
|
- kunmap_atomic(mapped_addr);
|
|
-}
|
|
-
|
|
-/* Copy from len bytes of sg to dest, starting from beginning */
|
|
-static inline void sg_copy(u8 *dest, struct scatterlist *sg, unsigned int len)
|
|
-{
|
|
- struct scatterlist *current_sg = sg;
|
|
- int cpy_index = 0, next_cpy_index = current_sg->length;
|
|
-
|
|
- while (next_cpy_index < len) {
|
|
- sg_map_copy(dest + cpy_index, current_sg, current_sg->length,
|
|
- current_sg->offset);
|
|
- current_sg = scatterwalk_sg_next(current_sg);
|
|
- cpy_index = next_cpy_index;
|
|
- next_cpy_index += current_sg->length;
|
|
- }
|
|
- if (cpy_index < len)
|
|
- sg_map_copy(dest + cpy_index, current_sg, len-cpy_index,
|
|
- current_sg->offset);
|
|
-}
|
|
-
|
|
-/* Copy sg data, from to_skip to end, to dest */
|
|
-static inline void sg_copy_part(u8 *dest, struct scatterlist *sg,
|
|
- int to_skip, unsigned int end)
|
|
-{
|
|
- struct scatterlist *current_sg = sg;
|
|
- int sg_index, cpy_index, offset;
|
|
-
|
|
- sg_index = current_sg->length;
|
|
- while (sg_index <= to_skip) {
|
|
- current_sg = scatterwalk_sg_next(current_sg);
|
|
- sg_index += current_sg->length;
|
|
- }
|
|
- cpy_index = sg_index - to_skip;
|
|
- offset = current_sg->offset + current_sg->length - cpy_index;
|
|
- sg_map_copy(dest, current_sg, cpy_index, offset);
|
|
- if (end - sg_index) {
|
|
- current_sg = scatterwalk_sg_next(current_sg);
|
|
- sg_copy(dest + cpy_index, current_sg, end - sg_index);
|
|
- }
|
|
-}
|
|
diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c
|
|
index d7d5c8af92b9..6d4456898007 100644
|
|
--- a/drivers/firewire/core-cdev.c
|
|
+++ b/drivers/firewire/core-cdev.c
|
|
@@ -1637,8 +1637,7 @@ static int dispatch_ioctl(struct client *client,
|
|
_IOC_SIZE(cmd) > sizeof(buffer))
|
|
return -ENOTTY;
|
|
|
|
- if (_IOC_DIR(cmd) == _IOC_READ)
|
|
- memset(&buffer, 0, _IOC_SIZE(cmd));
|
|
+ memset(&buffer, 0, sizeof(buffer));
|
|
|
|
if (_IOC_DIR(cmd) & _IOC_WRITE)
|
|
if (copy_from_user(&buffer, arg, _IOC_SIZE(cmd)))
|
|
diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c
|
|
index ab5c26575622..ddf70d6c0270 100644
|
|
--- a/drivers/gpu/drm/radeon/cik.c
|
|
+++ b/drivers/gpu/drm/radeon/cik.c
|
|
@@ -3936,8 +3936,8 @@ static int cik_cp_gfx_start(struct radeon_device *rdev)
|
|
/* init the CE partitions. CE only used for gfx on CIK */
|
|
radeon_ring_write(ring, PACKET3(PACKET3_SET_BASE, 2));
|
|
radeon_ring_write(ring, PACKET3_BASE_INDEX(CE_PARTITION_BASE));
|
|
- radeon_ring_write(ring, 0xc000);
|
|
- radeon_ring_write(ring, 0xc000);
|
|
+ radeon_ring_write(ring, 0x8000);
|
|
+ radeon_ring_write(ring, 0x8000);
|
|
|
|
/* setup clear context state */
|
|
radeon_ring_write(ring, PACKET3(PACKET3_PREAMBLE_CNTL, 0));
|
|
@@ -8893,6 +8893,9 @@ void dce8_bandwidth_update(struct radeon_device *rdev)
|
|
u32 num_heads = 0, lb_size;
|
|
int i;
|
|
|
|
+ if (!rdev->mode_info.mode_config_initialized)
|
|
+ return;
|
|
+
|
|
radeon_update_display_priority(rdev);
|
|
|
|
for (i = 0; i < rdev->num_crtc; i++) {
|
|
diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c
|
|
index 4b3c5f7ae63b..7138f3e31b7c 100644
|
|
--- a/drivers/gpu/drm/radeon/evergreen.c
|
|
+++ b/drivers/gpu/drm/radeon/evergreen.c
|
|
@@ -2362,6 +2362,9 @@ void evergreen_bandwidth_update(struct radeon_device *rdev)
|
|
u32 num_heads = 0, lb_size;
|
|
int i;
|
|
|
|
+ if (!rdev->mode_info.mode_config_initialized)
|
|
+ return;
|
|
+
|
|
radeon_update_display_priority(rdev);
|
|
|
|
for (i = 0; i < rdev->num_crtc; i++) {
|
|
@@ -2570,6 +2573,7 @@ void evergreen_mc_stop(struct radeon_device *rdev, struct evergreen_mc_save *sav
|
|
WREG32(EVERGREEN_CRTC_UPDATE_LOCK + crtc_offsets[i], 1);
|
|
tmp |= EVERGREEN_CRTC_BLANK_DATA_EN;
|
|
WREG32(EVERGREEN_CRTC_BLANK_CONTROL + crtc_offsets[i], tmp);
|
|
+ WREG32(EVERGREEN_CRTC_UPDATE_LOCK + crtc_offsets[i], 0);
|
|
}
|
|
} else {
|
|
tmp = RREG32(EVERGREEN_CRTC_CONTROL + crtc_offsets[i]);
|
|
diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c
|
|
index 3cc78bb66042..07620e198a6d 100644
|
|
--- a/drivers/gpu/drm/radeon/r100.c
|
|
+++ b/drivers/gpu/drm/radeon/r100.c
|
|
@@ -3219,6 +3219,9 @@ void r100_bandwidth_update(struct radeon_device *rdev)
|
|
uint32_t pixel_bytes1 = 0;
|
|
uint32_t pixel_bytes2 = 0;
|
|
|
|
+ if (!rdev->mode_info.mode_config_initialized)
|
|
+ return;
|
|
+
|
|
radeon_update_display_priority(rdev);
|
|
|
|
if (rdev->mode_info.crtcs[0]->base.enabled) {
|
|
diff --git a/drivers/gpu/drm/radeon/rs600.c b/drivers/gpu/drm/radeon/rs600.c
|
|
index 95b693c11640..e5619d5e2a30 100644
|
|
--- a/drivers/gpu/drm/radeon/rs600.c
|
|
+++ b/drivers/gpu/drm/radeon/rs600.c
|
|
@@ -890,6 +890,9 @@ void rs600_bandwidth_update(struct radeon_device *rdev)
|
|
u32 d1mode_priority_a_cnt, d2mode_priority_a_cnt;
|
|
/* FIXME: implement full support */
|
|
|
|
+ if (!rdev->mode_info.mode_config_initialized)
|
|
+ return;
|
|
+
|
|
radeon_update_display_priority(rdev);
|
|
|
|
if (rdev->mode_info.crtcs[0]->base.enabled)
|
|
diff --git a/drivers/gpu/drm/radeon/rs690.c b/drivers/gpu/drm/radeon/rs690.c
|
|
index 3462b64369bf..0a2d36e81108 100644
|
|
--- a/drivers/gpu/drm/radeon/rs690.c
|
|
+++ b/drivers/gpu/drm/radeon/rs690.c
|
|
@@ -579,6 +579,9 @@ void rs690_bandwidth_update(struct radeon_device *rdev)
|
|
u32 d1mode_priority_a_cnt, d1mode_priority_b_cnt;
|
|
u32 d2mode_priority_a_cnt, d2mode_priority_b_cnt;
|
|
|
|
+ if (!rdev->mode_info.mode_config_initialized)
|
|
+ return;
|
|
+
|
|
radeon_update_display_priority(rdev);
|
|
|
|
if (rdev->mode_info.crtcs[0]->base.enabled)
|
|
diff --git a/drivers/gpu/drm/radeon/rv515.c b/drivers/gpu/drm/radeon/rv515.c
|
|
index 237dd29d9f1c..b49965a21a2d 100644
|
|
--- a/drivers/gpu/drm/radeon/rv515.c
|
|
+++ b/drivers/gpu/drm/radeon/rv515.c
|
|
@@ -1276,6 +1276,9 @@ void rv515_bandwidth_update(struct radeon_device *rdev)
|
|
struct drm_display_mode *mode0 = NULL;
|
|
struct drm_display_mode *mode1 = NULL;
|
|
|
|
+ if (!rdev->mode_info.mode_config_initialized)
|
|
+ return;
|
|
+
|
|
radeon_update_display_priority(rdev);
|
|
|
|
if (rdev->mode_info.crtcs[0]->base.enabled)
|
|
diff --git a/drivers/gpu/drm/radeon/si.c b/drivers/gpu/drm/radeon/si.c
|
|
index 559564c1dc97..52b64ad285d6 100644
|
|
--- a/drivers/gpu/drm/radeon/si.c
|
|
+++ b/drivers/gpu/drm/radeon/si.c
|
|
@@ -2227,6 +2227,9 @@ void dce6_bandwidth_update(struct radeon_device *rdev)
|
|
u32 num_heads = 0, lb_size;
|
|
int i;
|
|
|
|
+ if (!rdev->mode_info.mode_config_initialized)
|
|
+ return;
|
|
+
|
|
radeon_update_display_priority(rdev);
|
|
|
|
for (i = 0; i < rdev->num_crtc; i++) {
|
|
diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c
|
|
index ea6203ee7bcc..23467a2abd62 100644
|
|
--- a/drivers/infiniband/core/uverbs_cmd.c
|
|
+++ b/drivers/infiniband/core/uverbs_cmd.c
|
|
@@ -2425,6 +2425,8 @@ ssize_t ib_uverbs_create_ah(struct ib_uverbs_file *file,
|
|
attr.grh.sgid_index = cmd.attr.grh.sgid_index;
|
|
attr.grh.hop_limit = cmd.attr.grh.hop_limit;
|
|
attr.grh.traffic_class = cmd.attr.grh.traffic_class;
|
|
+ attr.vlan_id = 0;
|
|
+ memset(&attr.dmac, 0, sizeof(attr.dmac));
|
|
memcpy(attr.grh.dgid.raw, cmd.attr.grh.dgid, 16);
|
|
|
|
ah = ib_create_ah(pd, &attr);
|
|
diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c
|
|
index fb15c64ffb95..4979b00fbf04 100644
|
|
--- a/drivers/input/mouse/alps.c
|
|
+++ b/drivers/input/mouse/alps.c
|
|
@@ -1047,7 +1047,13 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse)
|
|
{
|
|
struct alps_data *priv = psmouse->private;
|
|
|
|
- if ((psmouse->packet[0] & 0xc8) == 0x08) { /* PS/2 packet */
|
|
+ /*
|
|
+ * Check if we are dealing with a bare PS/2 packet, presumably from
|
|
+ * a device connected to the external PS/2 port. Because bare PS/2
|
|
+ * protocol does not have enough constant bits to self-synchronize
|
|
+ * properly we only do this if the device is fully synchronized.
|
|
+ */
|
|
+ if (!psmouse->out_of_sync_cnt && (psmouse->packet[0] & 0xc8) == 0x08) {
|
|
if (psmouse->pktcnt == 3) {
|
|
alps_report_bare_ps2_packet(psmouse, psmouse->packet,
|
|
true);
|
|
@@ -1071,12 +1077,27 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse)
|
|
}
|
|
|
|
/* Bytes 2 - pktsize should have 0 in the highest bit */
|
|
- if ((priv->proto_version < ALPS_PROTO_V5) &&
|
|
+ if (priv->proto_version < ALPS_PROTO_V5 &&
|
|
psmouse->pktcnt >= 2 && psmouse->pktcnt <= psmouse->pktsize &&
|
|
(psmouse->packet[psmouse->pktcnt - 1] & 0x80)) {
|
|
psmouse_dbg(psmouse, "refusing packet[%i] = %x\n",
|
|
psmouse->pktcnt - 1,
|
|
psmouse->packet[psmouse->pktcnt - 1]);
|
|
+
|
|
+ if (priv->proto_version == ALPS_PROTO_V3 &&
|
|
+ psmouse->pktcnt == psmouse->pktsize) {
|
|
+ /*
|
|
+ * Some Dell boxes, such as Latitude E6440 or E7440
|
|
+ * with closed lid, quite often smash last byte of
|
|
+ * otherwise valid packet with 0xff. Given that the
|
|
+ * next packet is very likely to be valid let's
|
|
+ * report PSMOUSE_FULL_PACKET but not process data,
|
|
+ * rather than reporting PSMOUSE_BAD_DATA and
|
|
+ * filling the logs.
|
|
+ */
|
|
+ return PSMOUSE_FULL_PACKET;
|
|
+ }
|
|
+
|
|
return PSMOUSE_BAD_DATA;
|
|
}
|
|
|
|
@@ -2148,6 +2169,9 @@ int alps_init(struct psmouse *psmouse)
|
|
/* We are having trouble resyncing ALPS touchpads so disable it for now */
|
|
psmouse->resync_time = 0;
|
|
|
|
+ /* Allow 2 invalid packets without resetting device */
|
|
+ psmouse->resetafter = psmouse->pktsize * 2;
|
|
+
|
|
return 0;
|
|
|
|
init_fail:
|
|
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
|
|
index a50a2a7a43f7..1e76eb8f06c7 100644
|
|
--- a/drivers/input/mouse/synaptics.c
|
|
+++ b/drivers/input/mouse/synaptics.c
|
|
@@ -132,8 +132,8 @@ static const struct min_max_quirk min_max_pnpid_table[] = {
|
|
1232, 5710, 1156, 4696
|
|
},
|
|
{
|
|
- (const char * const []){"LEN0034", "LEN0036", "LEN2002",
|
|
- "LEN2004", NULL},
|
|
+ (const char * const []){"LEN0034", "LEN0036", "LEN0039",
|
|
+ "LEN2002", "LEN2004", NULL},
|
|
1024, 5112, 2024, 4832
|
|
},
|
|
{
|
|
@@ -160,6 +160,7 @@ static const char * const topbuttonpad_pnp_ids[] = {
|
|
"LEN0036", /* T440 */
|
|
"LEN0037",
|
|
"LEN0038",
|
|
+ "LEN0039", /* T440s */
|
|
"LEN0041",
|
|
"LEN0042", /* Yoga */
|
|
"LEN0045",
|
|
diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c
|
|
index ca1621b49453..a1cebf745b22 100644
|
|
--- a/drivers/md/dm-bufio.c
|
|
+++ b/drivers/md/dm-bufio.c
|
|
@@ -1448,9 +1448,9 @@ static void drop_buffers(struct dm_bufio_client *c)
|
|
|
|
/*
|
|
* Test if the buffer is unused and too old, and commit it.
|
|
- * At if noio is set, we must not do any I/O because we hold
|
|
- * dm_bufio_clients_lock and we would risk deadlock if the I/O gets rerouted to
|
|
- * different bufio client.
|
|
+ * And if GFP_NOFS is used, we must not do any I/O because we hold
|
|
+ * dm_bufio_clients_lock and we would risk deadlock if the I/O gets
|
|
+ * rerouted to different bufio client.
|
|
*/
|
|
static int __cleanup_old_buffer(struct dm_buffer *b, gfp_t gfp,
|
|
unsigned long max_jiffies)
|
|
@@ -1458,7 +1458,7 @@ static int __cleanup_old_buffer(struct dm_buffer *b, gfp_t gfp,
|
|
if (jiffies - b->last_accessed < max_jiffies)
|
|
return 0;
|
|
|
|
- if (!(gfp & __GFP_IO)) {
|
|
+ if (!(gfp & __GFP_FS)) {
|
|
if (test_bit(B_READING, &b->state) ||
|
|
test_bit(B_WRITING, &b->state) ||
|
|
test_bit(B_DIRTY, &b->state))
|
|
@@ -1500,7 +1500,7 @@ dm_bufio_shrink_scan(struct shrinker *shrink, struct shrink_control *sc)
|
|
unsigned long freed;
|
|
|
|
c = container_of(shrink, struct dm_bufio_client, shrinker);
|
|
- if (sc->gfp_mask & __GFP_IO)
|
|
+ if (sc->gfp_mask & __GFP_FS)
|
|
dm_bufio_lock(c);
|
|
else if (!dm_bufio_trylock(c))
|
|
return SHRINK_STOP;
|
|
@@ -1517,7 +1517,7 @@ dm_bufio_shrink_count(struct shrinker *shrink, struct shrink_control *sc)
|
|
unsigned long count;
|
|
|
|
c = container_of(shrink, struct dm_bufio_client, shrinker);
|
|
- if (sc->gfp_mask & __GFP_IO)
|
|
+ if (sc->gfp_mask & __GFP_FS)
|
|
dm_bufio_lock(c);
|
|
else if (!dm_bufio_trylock(c))
|
|
return 0;
|
|
diff --git a/drivers/md/dm-raid.c b/drivers/md/dm-raid.c
|
|
index 4880b69e2e9e..59715389b3cf 100644
|
|
--- a/drivers/md/dm-raid.c
|
|
+++ b/drivers/md/dm-raid.c
|
|
@@ -785,8 +785,7 @@ struct dm_raid_superblock {
|
|
__le32 layout;
|
|
__le32 stripe_sectors;
|
|
|
|
- __u8 pad[452]; /* Round struct to 512 bytes. */
|
|
- /* Always set to 0 when writing. */
|
|
+ /* Remainder of a logical block is zero-filled when writing (see super_sync()). */
|
|
} __packed;
|
|
|
|
static int read_disk_sb(struct md_rdev *rdev, int size)
|
|
@@ -823,7 +822,7 @@ static void super_sync(struct mddev *mddev, struct md_rdev *rdev)
|
|
test_bit(Faulty, &(rs->dev[i].rdev.flags)))
|
|
failed_devices |= (1ULL << i);
|
|
|
|
- memset(sb, 0, sizeof(*sb));
|
|
+ memset(sb + 1, 0, rdev->sb_size - sizeof(*sb));
|
|
|
|
sb->magic = cpu_to_le32(DM_RAID_MAGIC);
|
|
sb->features = cpu_to_le32(0); /* No features yet */
|
|
@@ -858,7 +857,11 @@ static int super_load(struct md_rdev *rdev, struct md_rdev *refdev)
|
|
uint64_t events_sb, events_refsb;
|
|
|
|
rdev->sb_start = 0;
|
|
- rdev->sb_size = sizeof(*sb);
|
|
+ rdev->sb_size = bdev_logical_block_size(rdev->meta_bdev);
|
|
+ if (rdev->sb_size < sizeof(*sb) || rdev->sb_size > PAGE_SIZE) {
|
|
+ DMERR("superblock size of a logical block is no longer valid");
|
|
+ return -EINVAL;
|
|
+ }
|
|
|
|
ret = read_disk_sb(rdev, rdev->sb_size);
|
|
if (ret)
|
|
diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c
|
|
index 359af3a519b5..37f2648c112b 100644
|
|
--- a/drivers/md/dm-thin.c
|
|
+++ b/drivers/md/dm-thin.c
|
|
@@ -1704,6 +1704,14 @@ static int thin_bio_map(struct dm_target *ti, struct bio *bio)
|
|
return DM_MAPIO_SUBMITTED;
|
|
}
|
|
|
|
+ /*
|
|
+ * We must hold the virtual cell before doing the lookup, otherwise
|
|
+ * there's a race with discard.
|
|
+ */
|
|
+ build_virtual_key(tc->td, block, &key);
|
|
+ if (dm_bio_detain(tc->pool->prison, &key, bio, &cell1, &cell_result))
|
|
+ return DM_MAPIO_SUBMITTED;
|
|
+
|
|
r = dm_thin_find_block(td, block, 0, &result);
|
|
|
|
/*
|
|
@@ -1727,13 +1735,10 @@ static int thin_bio_map(struct dm_target *ti, struct bio *bio)
|
|
* shared flag will be set in their case.
|
|
*/
|
|
thin_defer_bio(tc, bio);
|
|
+ cell_defer_no_holder_no_free(tc, &cell1);
|
|
return DM_MAPIO_SUBMITTED;
|
|
}
|
|
|
|
- build_virtual_key(tc->td, block, &key);
|
|
- if (dm_bio_detain(tc->pool->prison, &key, bio, &cell1, &cell_result))
|
|
- return DM_MAPIO_SUBMITTED;
|
|
-
|
|
build_data_key(tc->td, result.block, &key);
|
|
if (dm_bio_detain(tc->pool->prison, &key, bio, &cell2, &cell_result)) {
|
|
cell_defer_no_holder_no_free(tc, &cell1);
|
|
@@ -1754,6 +1759,7 @@ static int thin_bio_map(struct dm_target *ti, struct bio *bio)
|
|
* of doing so.
|
|
*/
|
|
handle_unserviceable_bio(tc->pool, bio);
|
|
+ cell_defer_no_holder_no_free(tc, &cell1);
|
|
return DM_MAPIO_SUBMITTED;
|
|
}
|
|
/* fall through */
|
|
@@ -1764,6 +1770,7 @@ static int thin_bio_map(struct dm_target *ti, struct bio *bio)
|
|
* provide the hint to load the metadata into cache.
|
|
*/
|
|
thin_defer_bio(tc, bio);
|
|
+ cell_defer_no_holder_no_free(tc, &cell1);
|
|
return DM_MAPIO_SUBMITTED;
|
|
|
|
default:
|
|
@@ -1773,6 +1780,7 @@ static int thin_bio_map(struct dm_target *ti, struct bio *bio)
|
|
* pool is switched to fail-io mode.
|
|
*/
|
|
bio_io_error(bio);
|
|
+ cell_defer_no_holder_no_free(tc, &cell1);
|
|
return DM_MAPIO_SUBMITTED;
|
|
}
|
|
}
|
|
diff --git a/drivers/md/md.c b/drivers/md/md.c
|
|
index 73aedcb639c0..40959ee73583 100644
|
|
--- a/drivers/md/md.c
|
|
+++ b/drivers/md/md.c
|
|
@@ -5333,6 +5333,7 @@ static int md_set_readonly(struct mddev *mddev, struct block_device *bdev)
|
|
printk("md: %s still in use.\n",mdname(mddev));
|
|
if (did_freeze) {
|
|
clear_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
|
|
+ set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
|
|
md_wakeup_thread(mddev->thread);
|
|
}
|
|
err = -EBUSY;
|
|
@@ -5347,6 +5348,8 @@ static int md_set_readonly(struct mddev *mddev, struct block_device *bdev)
|
|
mddev->ro = 1;
|
|
set_disk_ro(mddev->gendisk, 1);
|
|
clear_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
|
|
+ set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
|
|
+ md_wakeup_thread(mddev->thread);
|
|
sysfs_notify_dirent_safe(mddev->sysfs_state);
|
|
err = 0;
|
|
}
|
|
@@ -5390,6 +5393,7 @@ static int do_md_stop(struct mddev * mddev, int mode,
|
|
mutex_unlock(&mddev->open_mutex);
|
|
if (did_freeze) {
|
|
clear_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
|
|
+ set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
|
|
md_wakeup_thread(mddev->thread);
|
|
}
|
|
return -EBUSY;
|
|
diff --git a/drivers/md/persistent-data/dm-btree-internal.h b/drivers/md/persistent-data/dm-btree-internal.h
|
|
index 37d367bb9aa8..bf2b80d5c470 100644
|
|
--- a/drivers/md/persistent-data/dm-btree-internal.h
|
|
+++ b/drivers/md/persistent-data/dm-btree-internal.h
|
|
@@ -42,6 +42,12 @@ struct btree_node {
|
|
} __packed;
|
|
|
|
|
|
+/*
|
|
+ * Locks a block using the btree node validator.
|
|
+ */
|
|
+int bn_read_lock(struct dm_btree_info *info, dm_block_t b,
|
|
+ struct dm_block **result);
|
|
+
|
|
void inc_children(struct dm_transaction_manager *tm, struct btree_node *n,
|
|
struct dm_btree_value_type *vt);
|
|
|
|
diff --git a/drivers/md/persistent-data/dm-btree-spine.c b/drivers/md/persistent-data/dm-btree-spine.c
|
|
index cf9fd676ae44..1b5e13ec7f96 100644
|
|
--- a/drivers/md/persistent-data/dm-btree-spine.c
|
|
+++ b/drivers/md/persistent-data/dm-btree-spine.c
|
|
@@ -92,7 +92,7 @@ struct dm_block_validator btree_node_validator = {
|
|
|
|
/*----------------------------------------------------------------*/
|
|
|
|
-static int bn_read_lock(struct dm_btree_info *info, dm_block_t b,
|
|
+int bn_read_lock(struct dm_btree_info *info, dm_block_t b,
|
|
struct dm_block **result)
|
|
{
|
|
return dm_tm_read_lock(info->tm, b, &btree_node_validator, result);
|
|
diff --git a/drivers/md/persistent-data/dm-btree.c b/drivers/md/persistent-data/dm-btree.c
|
|
index 416060c25709..200ac12a1d40 100644
|
|
--- a/drivers/md/persistent-data/dm-btree.c
|
|
+++ b/drivers/md/persistent-data/dm-btree.c
|
|
@@ -847,22 +847,26 @@ EXPORT_SYMBOL_GPL(dm_btree_find_lowest_key);
|
|
* FIXME: We shouldn't use a recursive algorithm when we have limited stack
|
|
* space. Also this only works for single level trees.
|
|
*/
|
|
-static int walk_node(struct ro_spine *s, dm_block_t block,
|
|
+static int walk_node(struct dm_btree_info *info, dm_block_t block,
|
|
int (*fn)(void *context, uint64_t *keys, void *leaf),
|
|
void *context)
|
|
{
|
|
int r;
|
|
unsigned i, nr;
|
|
+ struct dm_block *node;
|
|
struct btree_node *n;
|
|
uint64_t keys;
|
|
|
|
- r = ro_step(s, block);
|
|
- n = ro_node(s);
|
|
+ r = bn_read_lock(info, block, &node);
|
|
+ if (r)
|
|
+ return r;
|
|
+
|
|
+ n = dm_block_data(node);
|
|
|
|
nr = le32_to_cpu(n->header.nr_entries);
|
|
for (i = 0; i < nr; i++) {
|
|
if (le32_to_cpu(n->header.flags) & INTERNAL_NODE) {
|
|
- r = walk_node(s, value64(n, i), fn, context);
|
|
+ r = walk_node(info, value64(n, i), fn, context);
|
|
if (r)
|
|
goto out;
|
|
} else {
|
|
@@ -874,7 +878,7 @@ static int walk_node(struct ro_spine *s, dm_block_t block,
|
|
}
|
|
|
|
out:
|
|
- ro_pop(s);
|
|
+ dm_tm_unlock(info->tm, node);
|
|
return r;
|
|
}
|
|
|
|
@@ -882,15 +886,7 @@ int dm_btree_walk(struct dm_btree_info *info, dm_block_t root,
|
|
int (*fn)(void *context, uint64_t *keys, void *leaf),
|
|
void *context)
|
|
{
|
|
- int r;
|
|
- struct ro_spine spine;
|
|
-
|
|
BUG_ON(info->levels > 1);
|
|
-
|
|
- init_ro_spine(&spine, info);
|
|
- r = walk_node(&spine, root, fn, context);
|
|
- exit_ro_spine(&spine);
|
|
-
|
|
- return r;
|
|
+ return walk_node(info, root, fn, context);
|
|
}
|
|
EXPORT_SYMBOL_GPL(dm_btree_walk);
|
|
diff --git a/drivers/media/usb/ttusb-dec/ttusbdecfe.c b/drivers/media/usb/ttusb-dec/ttusbdecfe.c
|
|
index 5c45c9d0712d..9c29552aedec 100644
|
|
--- a/drivers/media/usb/ttusb-dec/ttusbdecfe.c
|
|
+++ b/drivers/media/usb/ttusb-dec/ttusbdecfe.c
|
|
@@ -156,6 +156,9 @@ static int ttusbdecfe_dvbs_diseqc_send_master_cmd(struct dvb_frontend* fe, struc
|
|
0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00 };
|
|
|
|
+ if (cmd->msg_len > sizeof(b) - 4)
|
|
+ return -EINVAL;
|
|
+
|
|
memcpy(&b[4], cmd->msg, cmd->msg_len);
|
|
|
|
state->config->send_command(fe, 0x72,
|
|
diff --git a/drivers/net/ethernet/smsc/smsc911x.c b/drivers/net/ethernet/smsc/smsc911x.c
|
|
index 6382b7c416f4..e10f5ed26181 100644
|
|
--- a/drivers/net/ethernet/smsc/smsc911x.c
|
|
+++ b/drivers/net/ethernet/smsc/smsc911x.c
|
|
@@ -1341,6 +1341,42 @@ static void smsc911x_rx_multicast_update_workaround(struct smsc911x_data *pdata)
|
|
spin_unlock(&pdata->mac_lock);
|
|
}
|
|
|
|
+static int smsc911x_phy_general_power_up(struct smsc911x_data *pdata)
|
|
+{
|
|
+ int rc = 0;
|
|
+
|
|
+ if (!pdata->phy_dev)
|
|
+ return rc;
|
|
+
|
|
+ /* If the internal PHY is in General Power-Down mode, all, except the
|
|
+ * management interface, is powered-down and stays in that condition as
|
|
+ * long as Phy register bit 0.11 is HIGH.
|
|
+ *
|
|
+ * In that case, clear the bit 0.11, so the PHY powers up and we can
|
|
+ * access to the phy registers.
|
|
+ */
|
|
+ rc = phy_read(pdata->phy_dev, MII_BMCR);
|
|
+ if (rc < 0) {
|
|
+ SMSC_WARN(pdata, drv, "Failed reading PHY control reg");
|
|
+ return rc;
|
|
+ }
|
|
+
|
|
+ /* If the PHY general power-down bit is not set is not necessary to
|
|
+ * disable the general power down-mode.
|
|
+ */
|
|
+ if (rc & BMCR_PDOWN) {
|
|
+ rc = phy_write(pdata->phy_dev, MII_BMCR, rc & ~BMCR_PDOWN);
|
|
+ if (rc < 0) {
|
|
+ SMSC_WARN(pdata, drv, "Failed writing PHY control reg");
|
|
+ return rc;
|
|
+ }
|
|
+
|
|
+ usleep_range(1000, 1500);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static int smsc911x_phy_disable_energy_detect(struct smsc911x_data *pdata)
|
|
{
|
|
int rc = 0;
|
|
@@ -1414,6 +1450,16 @@ static int smsc911x_soft_reset(struct smsc911x_data *pdata)
|
|
int ret;
|
|
|
|
/*
|
|
+ * Make sure to power-up the PHY chip before doing a reset, otherwise
|
|
+ * the reset fails.
|
|
+ */
|
|
+ ret = smsc911x_phy_general_power_up(pdata);
|
|
+ if (ret) {
|
|
+ SMSC_WARN(pdata, drv, "Failed to power-up the PHY chip");
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ /*
|
|
* LAN9210/LAN9211/LAN9220/LAN9221 chips have an internal PHY that
|
|
* are initialized in a Energy Detect Power-Down mode that prevents
|
|
* the MAC chip to be software reseted. So we have to wakeup the PHY
|
|
diff --git a/drivers/net/ethernet/sun/sunvnet.c b/drivers/net/ethernet/sun/sunvnet.c
|
|
index fd411d6e19a2..03ae9def0e0c 100644
|
|
--- a/drivers/net/ethernet/sun/sunvnet.c
|
|
+++ b/drivers/net/ethernet/sun/sunvnet.c
|
|
@@ -656,7 +656,7 @@ static int vnet_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
|
spin_lock_irqsave(&port->vio.lock, flags);
|
|
|
|
dr = &port->vio.drings[VIO_DRIVER_TX_RING];
|
|
- if (unlikely(vnet_tx_dring_avail(dr) < 2)) {
|
|
+ if (unlikely(vnet_tx_dring_avail(dr) < 1)) {
|
|
if (!netif_queue_stopped(dev)) {
|
|
netif_stop_queue(dev);
|
|
|
|
@@ -704,7 +704,7 @@ static int vnet_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
|
dev->stats.tx_bytes += skb->len;
|
|
|
|
dr->prod = (dr->prod + 1) & (VNET_TX_RING_SIZE - 1);
|
|
- if (unlikely(vnet_tx_dring_avail(dr) < 2)) {
|
|
+ if (unlikely(vnet_tx_dring_avail(dr) < 1)) {
|
|
netif_stop_queue(dev);
|
|
if (vnet_tx_dring_avail(dr) > VNET_TX_WAKEUP_THRESH(dr))
|
|
netif_wake_queue(dev);
|
|
diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c
|
|
index f30ceb17d5fc..07c942b6ae01 100644
|
|
--- a/drivers/net/macvtap.c
|
|
+++ b/drivers/net/macvtap.c
|
|
@@ -66,7 +66,7 @@ static struct cdev macvtap_cdev;
|
|
static const struct proto_ops macvtap_socket_ops;
|
|
|
|
#define TUN_OFFLOADS (NETIF_F_HW_CSUM | NETIF_F_TSO_ECN | NETIF_F_TSO | \
|
|
- NETIF_F_TSO6)
|
|
+ NETIF_F_TSO6 | NETIF_F_UFO)
|
|
#define RX_OFFLOADS (NETIF_F_GRO | NETIF_F_LRO)
|
|
#define TAP_FEATURES (NETIF_F_GSO | NETIF_F_SG)
|
|
|
|
@@ -570,8 +570,6 @@ static int macvtap_skb_from_vnet_hdr(struct sk_buff *skb,
|
|
gso_type = SKB_GSO_TCPV6;
|
|
break;
|
|
case VIRTIO_NET_HDR_GSO_UDP:
|
|
- pr_warn_once("macvtap: %s: using disabled UFO feature; please fix this program\n",
|
|
- current->comm);
|
|
gso_type = SKB_GSO_UDP;
|
|
if (skb->protocol == htons(ETH_P_IPV6))
|
|
ipv6_proxy_select_ident(skb);
|
|
@@ -619,6 +617,8 @@ static void macvtap_skb_to_vnet_hdr(const struct sk_buff *skb,
|
|
vnet_hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV4;
|
|
else if (sinfo->gso_type & SKB_GSO_TCPV6)
|
|
vnet_hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV6;
|
|
+ else if (sinfo->gso_type & SKB_GSO_UDP)
|
|
+ vnet_hdr->gso_type = VIRTIO_NET_HDR_GSO_UDP;
|
|
else
|
|
BUG();
|
|
if (sinfo->gso_type & SKB_GSO_TCP_ECN)
|
|
@@ -629,6 +629,8 @@ static void macvtap_skb_to_vnet_hdr(const struct sk_buff *skb,
|
|
if (skb->ip_summed == CHECKSUM_PARTIAL) {
|
|
vnet_hdr->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
|
|
vnet_hdr->csum_start = skb_checksum_start_offset(skb);
|
|
+ if (vlan_tx_tag_present(skb))
|
|
+ vnet_hdr->csum_start += VLAN_HLEN;
|
|
vnet_hdr->csum_offset = skb->csum_offset;
|
|
} else if (skb->ip_summed == CHECKSUM_UNNECESSARY) {
|
|
vnet_hdr->flags = VIRTIO_NET_HDR_F_DATA_VALID;
|
|
@@ -953,6 +955,9 @@ static int set_offload(struct macvtap_queue *q, unsigned long arg)
|
|
if (arg & TUN_F_TSO6)
|
|
feature_mask |= NETIF_F_TSO6;
|
|
}
|
|
+
|
|
+ if (arg & TUN_F_UFO)
|
|
+ feature_mask |= NETIF_F_UFO;
|
|
}
|
|
|
|
/* tun/tap driver inverts the usage for TSO offloads, where
|
|
@@ -963,7 +968,7 @@ static int set_offload(struct macvtap_queue *q, unsigned long arg)
|
|
* When user space turns off TSO, we turn off GSO/LRO so that
|
|
* user-space will not receive TSO frames.
|
|
*/
|
|
- if (feature_mask & (NETIF_F_TSO | NETIF_F_TSO6))
|
|
+ if (feature_mask & (NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_UFO))
|
|
features |= RX_OFFLOADS;
|
|
else
|
|
features &= ~RX_OFFLOADS;
|
|
@@ -1064,7 +1069,7 @@ static long macvtap_ioctl(struct file *file, unsigned int cmd,
|
|
case TUNSETOFFLOAD:
|
|
/* let the user check for future flags */
|
|
if (arg & ~(TUN_F_CSUM | TUN_F_TSO4 | TUN_F_TSO6 |
|
|
- TUN_F_TSO_ECN))
|
|
+ TUN_F_TSO_ECN | TUN_F_UFO))
|
|
return -EINVAL;
|
|
|
|
rtnl_lock();
|
|
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
|
|
index 2c8b1c21c452..ec63314d6480 100644
|
|
--- a/drivers/net/tun.c
|
|
+++ b/drivers/net/tun.c
|
|
@@ -175,7 +175,7 @@ struct tun_struct {
|
|
struct net_device *dev;
|
|
netdev_features_t set_features;
|
|
#define TUN_USER_FEATURES (NETIF_F_HW_CSUM|NETIF_F_TSO_ECN|NETIF_F_TSO| \
|
|
- NETIF_F_TSO6)
|
|
+ NETIF_F_TSO6|NETIF_F_UFO)
|
|
|
|
int vnet_hdr_sz;
|
|
int sndbuf;
|
|
@@ -1153,20 +1153,10 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
|
|
skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6;
|
|
break;
|
|
case VIRTIO_NET_HDR_GSO_UDP:
|
|
- {
|
|
- static bool warned;
|
|
-
|
|
- if (!warned) {
|
|
- warned = true;
|
|
- netdev_warn(tun->dev,
|
|
- "%s: using disabled UFO feature; please fix this program\n",
|
|
- current->comm);
|
|
- }
|
|
skb_shinfo(skb)->gso_type = SKB_GSO_UDP;
|
|
if (skb->protocol == htons(ETH_P_IPV6))
|
|
ipv6_proxy_select_ident(skb);
|
|
break;
|
|
- }
|
|
default:
|
|
tun->dev->stats.rx_frame_errors++;
|
|
kfree_skb(skb);
|
|
@@ -1236,6 +1226,10 @@ static ssize_t tun_put_user(struct tun_struct *tun,
|
|
struct tun_pi pi = { 0, skb->protocol };
|
|
ssize_t total = 0;
|
|
int vlan_offset = 0, copied;
|
|
+ int vlan_hlen = 0;
|
|
+
|
|
+ if (vlan_tx_tag_present(skb))
|
|
+ vlan_hlen = VLAN_HLEN;
|
|
|
|
if (!(tun->flags & TUN_NO_PI)) {
|
|
if ((len -= sizeof(pi)) < 0)
|
|
@@ -1266,6 +1260,8 @@ static ssize_t tun_put_user(struct tun_struct *tun,
|
|
gso.gso_type = VIRTIO_NET_HDR_GSO_TCPV4;
|
|
else if (sinfo->gso_type & SKB_GSO_TCPV6)
|
|
gso.gso_type = VIRTIO_NET_HDR_GSO_TCPV6;
|
|
+ else if (sinfo->gso_type & SKB_GSO_UDP)
|
|
+ gso.gso_type = VIRTIO_NET_HDR_GSO_UDP;
|
|
else {
|
|
pr_err("unexpected GSO type: "
|
|
"0x%x, gso_size %d, hdr_len %d\n",
|
|
@@ -1285,7 +1281,8 @@ static ssize_t tun_put_user(struct tun_struct *tun,
|
|
|
|
if (skb->ip_summed == CHECKSUM_PARTIAL) {
|
|
gso.flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
|
|
- gso.csum_start = skb_checksum_start_offset(skb);
|
|
+ gso.csum_start = skb_checksum_start_offset(skb) +
|
|
+ vlan_hlen;
|
|
gso.csum_offset = skb->csum_offset;
|
|
} else if (skb->ip_summed == CHECKSUM_UNNECESSARY) {
|
|
gso.flags = VIRTIO_NET_HDR_F_DATA_VALID;
|
|
@@ -1298,10 +1295,9 @@ static ssize_t tun_put_user(struct tun_struct *tun,
|
|
}
|
|
|
|
copied = total;
|
|
- total += skb->len;
|
|
- if (!vlan_tx_tag_present(skb)) {
|
|
- len = min_t(int, skb->len, len);
|
|
- } else {
|
|
+ len = min_t(int, skb->len + vlan_hlen, len);
|
|
+ total += skb->len + vlan_hlen;
|
|
+ if (vlan_hlen) {
|
|
int copy, ret;
|
|
struct {
|
|
__be16 h_vlan_proto;
|
|
@@ -1312,8 +1308,6 @@ static ssize_t tun_put_user(struct tun_struct *tun,
|
|
veth.h_vlan_TCI = htons(vlan_tx_tag_get(skb));
|
|
|
|
vlan_offset = offsetof(struct vlan_ethhdr, h_vlan_proto);
|
|
- len = min_t(int, skb->len + VLAN_HLEN, len);
|
|
- total += VLAN_HLEN;
|
|
|
|
copy = min_t(int, vlan_offset, len);
|
|
ret = skb_copy_datagram_const_iovec(skb, 0, iv, copied, copy);
|
|
@@ -1795,6 +1789,11 @@ static int set_offload(struct tun_struct *tun, unsigned long arg)
|
|
features |= NETIF_F_TSO6;
|
|
arg &= ~(TUN_F_TSO4|TUN_F_TSO6);
|
|
}
|
|
+
|
|
+ if (arg & TUN_F_UFO) {
|
|
+ features |= NETIF_F_UFO;
|
|
+ arg &= ~TUN_F_UFO;
|
|
+ }
|
|
}
|
|
|
|
/* This gives the user a way to test for new features in future by
|
|
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
|
|
index 07a3255fd3cc..841b60831df1 100644
|
|
--- a/drivers/net/virtio_net.c
|
|
+++ b/drivers/net/virtio_net.c
|
|
@@ -496,17 +496,8 @@ static void receive_buf(struct receive_queue *rq, void *buf, unsigned int len)
|
|
skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4;
|
|
break;
|
|
case VIRTIO_NET_HDR_GSO_UDP:
|
|
- {
|
|
- static bool warned;
|
|
-
|
|
- if (!warned) {
|
|
- warned = true;
|
|
- netdev_warn(dev,
|
|
- "host using disabled UFO feature; please fix it\n");
|
|
- }
|
|
skb_shinfo(skb)->gso_type = SKB_GSO_UDP;
|
|
break;
|
|
- }
|
|
case VIRTIO_NET_HDR_GSO_TCPV6:
|
|
skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6;
|
|
break;
|
|
@@ -845,6 +836,8 @@ static int xmit_skb(struct send_queue *sq, struct sk_buff *skb)
|
|
hdr->hdr.gso_type = VIRTIO_NET_HDR_GSO_TCPV4;
|
|
else if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6)
|
|
hdr->hdr.gso_type = VIRTIO_NET_HDR_GSO_TCPV6;
|
|
+ else if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP)
|
|
+ hdr->hdr.gso_type = VIRTIO_NET_HDR_GSO_UDP;
|
|
else
|
|
BUG();
|
|
if (skb_shinfo(skb)->gso_type & SKB_GSO_TCP_ECN)
|
|
@@ -1664,7 +1657,7 @@ static int virtnet_probe(struct virtio_device *vdev)
|
|
dev->features |= NETIF_F_HW_CSUM|NETIF_F_SG|NETIF_F_FRAGLIST;
|
|
|
|
if (virtio_has_feature(vdev, VIRTIO_NET_F_GSO)) {
|
|
- dev->hw_features |= NETIF_F_TSO
|
|
+ dev->hw_features |= NETIF_F_TSO | NETIF_F_UFO
|
|
| NETIF_F_TSO_ECN | NETIF_F_TSO6;
|
|
}
|
|
/* Individual feature bits: what can host handle? */
|
|
@@ -1674,9 +1667,11 @@ static int virtnet_probe(struct virtio_device *vdev)
|
|
dev->hw_features |= NETIF_F_TSO6;
|
|
if (virtio_has_feature(vdev, VIRTIO_NET_F_HOST_ECN))
|
|
dev->hw_features |= NETIF_F_TSO_ECN;
|
|
+ if (virtio_has_feature(vdev, VIRTIO_NET_F_HOST_UFO))
|
|
+ dev->hw_features |= NETIF_F_UFO;
|
|
|
|
if (gso)
|
|
- dev->features |= dev->hw_features & NETIF_F_ALL_TSO;
|
|
+ dev->features |= dev->hw_features & (NETIF_F_ALL_TSO|NETIF_F_UFO);
|
|
/* (!csum && gso) case will be fixed by register_netdev() */
|
|
}
|
|
if (virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_CSUM))
|
|
@@ -1716,7 +1711,8 @@ static int virtnet_probe(struct virtio_device *vdev)
|
|
/* If we can receive ANY GSO packets, we must allocate large ones. */
|
|
if (virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_TSO4) ||
|
|
virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_TSO6) ||
|
|
- virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_ECN))
|
|
+ virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_ECN) ||
|
|
+ virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_UFO))
|
|
vi->big_packets = true;
|
|
|
|
if (virtio_has_feature(vdev, VIRTIO_NET_F_MRG_RXBUF))
|
|
@@ -1907,9 +1903,9 @@ static struct virtio_device_id id_table[] = {
|
|
static unsigned int features[] = {
|
|
VIRTIO_NET_F_CSUM, VIRTIO_NET_F_GUEST_CSUM,
|
|
VIRTIO_NET_F_GSO, VIRTIO_NET_F_MAC,
|
|
- VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_TSO6,
|
|
+ VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_HOST_TSO6,
|
|
VIRTIO_NET_F_HOST_ECN, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_TSO6,
|
|
- VIRTIO_NET_F_GUEST_ECN,
|
|
+ VIRTIO_NET_F_GUEST_ECN, VIRTIO_NET_F_GUEST_UFO,
|
|
VIRTIO_NET_F_MRG_RXBUF, VIRTIO_NET_F_STATUS, VIRTIO_NET_F_CTRL_VQ,
|
|
VIRTIO_NET_F_CTRL_RX, VIRTIO_NET_F_CTRL_VLAN,
|
|
VIRTIO_NET_F_GUEST_ANNOUNCE, VIRTIO_NET_F_MQ,
|
|
diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
|
|
index 0704a0402897..5441b49ef89d 100644
|
|
--- a/drivers/net/vxlan.c
|
|
+++ b/drivers/net/vxlan.c
|
|
@@ -279,13 +279,15 @@ static inline struct vxlan_rdst *first_remote_rtnl(struct vxlan_fdb *fdb)
|
|
return list_first_entry(&fdb->remotes, struct vxlan_rdst, list);
|
|
}
|
|
|
|
-/* Find VXLAN socket based on network namespace and UDP port */
|
|
-static struct vxlan_sock *vxlan_find_sock(struct net *net, __be16 port)
|
|
+/* Find VXLAN socket based on network namespace, address family and UDP port */
|
|
+static struct vxlan_sock *vxlan_find_sock(struct net *net,
|
|
+ sa_family_t family, __be16 port)
|
|
{
|
|
struct vxlan_sock *vs;
|
|
|
|
hlist_for_each_entry_rcu(vs, vs_head(net, port), hlist) {
|
|
- if (inet_sk(vs->sock->sk)->inet_sport == port)
|
|
+ if (inet_sk(vs->sock->sk)->inet_sport == port &&
|
|
+ inet_sk(vs->sock->sk)->sk.sk_family == family)
|
|
return vs;
|
|
}
|
|
return NULL;
|
|
@@ -304,11 +306,12 @@ static struct vxlan_dev *vxlan_vs_find_vni(struct vxlan_sock *vs, u32 id)
|
|
}
|
|
|
|
/* Look up VNI in a per net namespace table */
|
|
-static struct vxlan_dev *vxlan_find_vni(struct net *net, u32 id, __be16 port)
|
|
+static struct vxlan_dev *vxlan_find_vni(struct net *net, u32 id,
|
|
+ sa_family_t family, __be16 port)
|
|
{
|
|
struct vxlan_sock *vs;
|
|
|
|
- vs = vxlan_find_sock(net, port);
|
|
+ vs = vxlan_find_sock(net, family, port);
|
|
if (!vs)
|
|
return NULL;
|
|
|
|
@@ -1872,7 +1875,8 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
|
|
struct vxlan_dev *dst_vxlan;
|
|
|
|
ip_rt_put(rt);
|
|
- dst_vxlan = vxlan_find_vni(dev_net(dev), vni, dst_port);
|
|
+ dst_vxlan = vxlan_find_vni(dev_net(dev), vni,
|
|
+ dst->sa.sa_family, dst_port);
|
|
if (!dst_vxlan)
|
|
goto tx_error;
|
|
vxlan_encap_bypass(skb, vxlan, dst_vxlan);
|
|
@@ -1925,7 +1929,8 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
|
|
struct vxlan_dev *dst_vxlan;
|
|
|
|
dst_release(ndst);
|
|
- dst_vxlan = vxlan_find_vni(dev_net(dev), vni, dst_port);
|
|
+ dst_vxlan = vxlan_find_vni(dev_net(dev), vni,
|
|
+ dst->sa.sa_family, dst_port);
|
|
if (!dst_vxlan)
|
|
goto tx_error;
|
|
vxlan_encap_bypass(skb, vxlan, dst_vxlan);
|
|
@@ -2083,6 +2088,7 @@ static int vxlan_init(struct net_device *dev)
|
|
{
|
|
struct vxlan_dev *vxlan = netdev_priv(dev);
|
|
struct vxlan_net *vn = net_generic(dev_net(dev), vxlan_net_id);
|
|
+ bool ipv6 = vxlan->flags & VXLAN_F_IPV6;
|
|
struct vxlan_sock *vs;
|
|
int i;
|
|
|
|
@@ -2098,7 +2104,8 @@ static int vxlan_init(struct net_device *dev)
|
|
|
|
|
|
spin_lock(&vn->sock_lock);
|
|
- vs = vxlan_find_sock(dev_net(dev), vxlan->dst_port);
|
|
+ vs = vxlan_find_sock(dev_net(dev), ipv6 ? AF_INET6 : AF_INET,
|
|
+ vxlan->dst_port);
|
|
if (vs) {
|
|
/* If we have a socket with same port already, reuse it */
|
|
atomic_inc(&vs->refcnt);
|
|
@@ -2566,7 +2573,7 @@ struct vxlan_sock *vxlan_sock_add(struct net *net, __be16 port,
|
|
return vs;
|
|
|
|
spin_lock(&vn->sock_lock);
|
|
- vs = vxlan_find_sock(net, port);
|
|
+ vs = vxlan_find_sock(net, ipv6 ? AF_INET6 : AF_INET, port);
|
|
if (vs) {
|
|
if (vs->rcv == rcv)
|
|
atomic_inc(&vs->refcnt);
|
|
@@ -2712,7 +2719,8 @@ static int vxlan_newlink(struct net *net, struct net_device *dev,
|
|
if (data[IFLA_VXLAN_PORT])
|
|
vxlan->dst_port = nla_get_be16(data[IFLA_VXLAN_PORT]);
|
|
|
|
- if (vxlan_find_vni(net, vni, vxlan->dst_port)) {
|
|
+ if (vxlan_find_vni(net, vni, use_ipv6 ? AF_INET6 : AF_INET,
|
|
+ vxlan->dst_port)) {
|
|
pr_info("duplicate VNI %u\n", vni);
|
|
return -EEXIST;
|
|
}
|
|
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h
|
|
index 1f065cf4a4ba..d090ed79ada0 100644
|
|
--- a/drivers/net/wireless/iwlwifi/iwl-trans.h
|
|
+++ b/drivers/net/wireless/iwlwifi/iwl-trans.h
|
|
@@ -514,6 +514,7 @@ enum iwl_trans_state {
|
|
* Set during transport allocation.
|
|
* @hw_id_str: a string with info about HW ID. Set during transport allocation.
|
|
* @pm_support: set to true in start_hw if link pm is supported
|
|
+ * @ltr_enabled: set to true if the LTR is enabled
|
|
* @dev_cmd_pool: pool for Tx cmd allocation - for internal use only.
|
|
* The user should use iwl_trans_{alloc,free}_tx_cmd.
|
|
* @dev_cmd_headroom: room needed for the transport's private use before the
|
|
@@ -539,6 +540,7 @@ struct iwl_trans {
|
|
u8 rx_mpdu_cmd, rx_mpdu_cmd_hdr_size;
|
|
|
|
bool pm_support;
|
|
+ bool ltr_enabled;
|
|
|
|
/* The following fields are internal only */
|
|
struct kmem_cache *dev_cmd_pool;
|
|
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
|
|
index 884c08725308..fa66471283d9 100644
|
|
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
|
|
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
|
|
@@ -66,13 +66,46 @@
|
|
|
|
/* Power Management Commands, Responses, Notifications */
|
|
|
|
+/**
|
|
+ * enum iwl_ltr_config_flags - masks for LTR config command flags
|
|
+ * @LTR_CFG_FLAG_FEATURE_ENABLE: Feature operational status
|
|
+ * @LTR_CFG_FLAG_HW_DIS_ON_SHADOW_REG_ACCESS: allow LTR change on shadow
|
|
+ * memory access
|
|
+ * @LTR_CFG_FLAG_HW_EN_SHRT_WR_THROUGH: allow LTR msg send on ANY LTR
|
|
+ * reg change
|
|
+ * @LTR_CFG_FLAG_HW_DIS_ON_D0_2_D3: allow LTR msg send on transition from
|
|
+ * D0 to D3
|
|
+ * @LTR_CFG_FLAG_SW_SET_SHORT: fixed static short LTR register
|
|
+ * @LTR_CFG_FLAG_SW_SET_LONG: fixed static short LONG register
|
|
+ * @LTR_CFG_FLAG_DENIE_C10_ON_PD: allow going into C10 on PD
|
|
+ */
|
|
+enum iwl_ltr_config_flags {
|
|
+ LTR_CFG_FLAG_FEATURE_ENABLE = BIT(0),
|
|
+ LTR_CFG_FLAG_HW_DIS_ON_SHADOW_REG_ACCESS = BIT(1),
|
|
+ LTR_CFG_FLAG_HW_EN_SHRT_WR_THROUGH = BIT(2),
|
|
+ LTR_CFG_FLAG_HW_DIS_ON_D0_2_D3 = BIT(3),
|
|
+ LTR_CFG_FLAG_SW_SET_SHORT = BIT(4),
|
|
+ LTR_CFG_FLAG_SW_SET_LONG = BIT(5),
|
|
+ LTR_CFG_FLAG_DENIE_C10_ON_PD = BIT(6),
|
|
+};
|
|
+
|
|
+/**
|
|
+ * struct iwl_ltr_config_cmd - configures the LTR
|
|
+ * @flags: See %enum iwl_ltr_config_flags
|
|
+ */
|
|
+struct iwl_ltr_config_cmd {
|
|
+ __le32 flags;
|
|
+ __le32 static_long;
|
|
+ __le32 static_short;
|
|
+} __packed;
|
|
+
|
|
/* Radio LP RX Energy Threshold measured in dBm */
|
|
#define POWER_LPRX_RSSI_THRESHOLD 75
|
|
#define POWER_LPRX_RSSI_THRESHOLD_MAX 94
|
|
#define POWER_LPRX_RSSI_THRESHOLD_MIN 30
|
|
|
|
/**
|
|
- * enum iwl_scan_flags - masks for power table command flags
|
|
+ * enum iwl_power_flags - masks for power table command flags
|
|
* @POWER_FLAGS_POWER_SAVE_ENA_MSK: '1' Allow to save power by turning off
|
|
* receiver and transmitter. '0' - does not allow.
|
|
* @POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK: '0' Driver disables power management,
|
|
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/iwlwifi/mvm/fw-api.h
|
|
index d0a04779d734..d8948aa9c2d2 100644
|
|
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api.h
|
|
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api.h
|
|
@@ -142,6 +142,7 @@ enum {
|
|
/* Power - legacy power table command */
|
|
POWER_TABLE_CMD = 0x77,
|
|
PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION = 0x78,
|
|
+ LTR_CONFIG = 0xee,
|
|
|
|
/* Thermal Throttling*/
|
|
REPLY_THERMAL_MNG_BACKOFF = 0x7e,
|
|
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw.c b/drivers/net/wireless/iwlwifi/mvm/fw.c
|
|
index c03d39541f9e..2ef344fc0acb 100644
|
|
--- a/drivers/net/wireless/iwlwifi/mvm/fw.c
|
|
+++ b/drivers/net/wireless/iwlwifi/mvm/fw.c
|
|
@@ -439,6 +439,15 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
|
|
goto error;
|
|
}
|
|
|
|
+ if (mvm->trans->ltr_enabled) {
|
|
+ struct iwl_ltr_config_cmd cmd = {
|
|
+ .flags = cpu_to_le32(LTR_CFG_FLAG_FEATURE_ENABLE),
|
|
+ };
|
|
+
|
|
+ WARN_ON(iwl_mvm_send_cmd_pdu(mvm, LTR_CONFIG, 0,
|
|
+ sizeof(cmd), &cmd));
|
|
+ }
|
|
+
|
|
ret = iwl_mvm_power_update_device_mode(mvm);
|
|
if (ret)
|
|
goto error;
|
|
diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c
|
|
index a3d43de342d7..dbff7f0bc6a8 100644
|
|
--- a/drivers/net/wireless/iwlwifi/mvm/ops.c
|
|
+++ b/drivers/net/wireless/iwlwifi/mvm/ops.c
|
|
@@ -313,6 +313,7 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = {
|
|
CMD(REPLY_BEACON_FILTERING_CMD),
|
|
CMD(REPLY_THERMAL_MNG_BACKOFF),
|
|
CMD(MAC_PM_POWER_TABLE),
|
|
+ CMD(LTR_CONFIG),
|
|
CMD(BT_COEX_CI),
|
|
CMD(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION),
|
|
};
|
|
diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c
|
|
index 16be0c07c64a..fb62927ca44d 100644
|
|
--- a/drivers/net/wireless/iwlwifi/pcie/trans.c
|
|
+++ b/drivers/net/wireless/iwlwifi/pcie/trans.c
|
|
@@ -94,6 +94,7 @@ static void iwl_pcie_apm_config(struct iwl_trans *trans)
|
|
{
|
|
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
|
|
u16 lctl;
|
|
+ u16 cap;
|
|
|
|
/*
|
|
* HW bug W/A for instability in PCIe bus L0S->L1 transition.
|
|
@@ -104,16 +105,17 @@ static void iwl_pcie_apm_config(struct iwl_trans *trans)
|
|
* power savings, even without L1.
|
|
*/
|
|
pcie_capability_read_word(trans_pcie->pci_dev, PCI_EXP_LNKCTL, &lctl);
|
|
- if (lctl & PCI_EXP_LNKCTL_ASPM_L1) {
|
|
- /* L1-ASPM enabled; disable(!) L0S */
|
|
+ if (lctl & PCI_EXP_LNKCTL_ASPM_L1)
|
|
iwl_set_bit(trans, CSR_GIO_REG, CSR_GIO_REG_VAL_L0S_ENABLED);
|
|
- dev_info(trans->dev, "L1 Enabled; Disabling L0S\n");
|
|
- } else {
|
|
- /* L1-ASPM disabled; enable(!) L0S */
|
|
+ else
|
|
iwl_clear_bit(trans, CSR_GIO_REG, CSR_GIO_REG_VAL_L0S_ENABLED);
|
|
- dev_info(trans->dev, "L1 Disabled; Enabling L0S\n");
|
|
- }
|
|
trans->pm_support = !(lctl & PCI_EXP_LNKCTL_ASPM_L0S);
|
|
+
|
|
+ pcie_capability_read_word(trans_pcie->pci_dev, PCI_EXP_DEVCTL2, &cap);
|
|
+ trans->ltr_enabled = cap & PCI_EXP_DEVCTL2_LTR_EN;
|
|
+ dev_info(trans->dev, "L1 %sabled - LTR %sabled\n",
|
|
+ (lctl & PCI_EXP_LNKCTL_ASPM_L1) ? "En" : "Dis",
|
|
+ trans->ltr_enabled ? "En" : "Dis");
|
|
}
|
|
|
|
/*
|
|
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
|
|
index 69d4c3179d04..505ff601d9f4 100644
|
|
--- a/drivers/net/wireless/mac80211_hwsim.c
|
|
+++ b/drivers/net/wireless/mac80211_hwsim.c
|
|
@@ -1974,7 +1974,7 @@ static int mac80211_hwsim_create_radio(int channels, const char *reg_alpha2,
|
|
if (err != 0) {
|
|
printk(KERN_DEBUG "mac80211_hwsim: device_bind_driver failed (%d)\n",
|
|
err);
|
|
- goto failed_hw;
|
|
+ goto failed_bind;
|
|
}
|
|
|
|
skb_queue_head_init(&data->pending);
|
|
@@ -2157,6 +2157,8 @@ static int mac80211_hwsim_create_radio(int channels, const char *reg_alpha2,
|
|
return idx;
|
|
|
|
failed_hw:
|
|
+ device_release_driver(data->dev);
|
|
+failed_bind:
|
|
device_unregister(data->dev);
|
|
failed_drvdata:
|
|
ieee80211_free_hw(hw);
|
|
diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c
|
|
index 390e8e33d5e3..25721bf20092 100644
|
|
--- a/drivers/platform/x86/dell-wmi.c
|
|
+++ b/drivers/platform/x86/dell-wmi.c
|
|
@@ -163,18 +163,24 @@ static void dell_wmi_notify(u32 value, void *context)
|
|
const struct key_entry *key;
|
|
int reported_key;
|
|
u16 *buffer_entry = (u16 *)obj->buffer.pointer;
|
|
+ int buffer_size = obj->buffer.length/2;
|
|
|
|
- if (dell_new_hk_type && (buffer_entry[1] != 0x10)) {
|
|
+ if (buffer_size >= 2 && dell_new_hk_type && buffer_entry[1] != 0x10) {
|
|
pr_info("Received unknown WMI event (0x%x)\n",
|
|
buffer_entry[1]);
|
|
kfree(obj);
|
|
return;
|
|
}
|
|
|
|
- if (dell_new_hk_type || buffer_entry[1] == 0x0)
|
|
+ if (buffer_size >= 3 && (dell_new_hk_type || buffer_entry[1] == 0x0))
|
|
reported_key = (int)buffer_entry[2];
|
|
- else
|
|
+ else if (buffer_size >= 2)
|
|
reported_key = (int)buffer_entry[1] & 0xffff;
|
|
+ else {
|
|
+ pr_info("Received unknown WMI event\n");
|
|
+ kfree(obj);
|
|
+ return;
|
|
+ }
|
|
|
|
key = sparse_keymap_entry_from_scancode(dell_wmi_input_dev,
|
|
reported_key);
|
|
diff --git a/drivers/power/bq2415x_charger.c b/drivers/power/bq2415x_charger.c
|
|
index e384844a1ae1..1f49986fc605 100644
|
|
--- a/drivers/power/bq2415x_charger.c
|
|
+++ b/drivers/power/bq2415x_charger.c
|
|
@@ -1579,8 +1579,15 @@ static int bq2415x_probe(struct i2c_client *client,
|
|
if (np) {
|
|
bq->notify_psy = power_supply_get_by_phandle(np, "ti,usb-charger-detection");
|
|
|
|
- if (!bq->notify_psy)
|
|
- return -EPROBE_DEFER;
|
|
+ if (IS_ERR(bq->notify_psy)) {
|
|
+ dev_info(&client->dev,
|
|
+ "no 'ti,usb-charger-detection' property (err=%ld)\n",
|
|
+ PTR_ERR(bq->notify_psy));
|
|
+ bq->notify_psy = NULL;
|
|
+ } else if (!bq->notify_psy) {
|
|
+ ret = -EPROBE_DEFER;
|
|
+ goto error_2;
|
|
+ }
|
|
}
|
|
else if (pdata->notify_device)
|
|
bq->notify_psy = power_supply_get_by_name(pdata->notify_device);
|
|
@@ -1602,27 +1609,27 @@ static int bq2415x_probe(struct i2c_client *client,
|
|
ret = of_property_read_u32(np, "ti,current-limit",
|
|
&bq->init_data.current_limit);
|
|
if (ret)
|
|
- return ret;
|
|
+ goto error_2;
|
|
ret = of_property_read_u32(np, "ti,weak-battery-voltage",
|
|
&bq->init_data.weak_battery_voltage);
|
|
if (ret)
|
|
- return ret;
|
|
+ goto error_2;
|
|
ret = of_property_read_u32(np, "ti,battery-regulation-voltage",
|
|
&bq->init_data.battery_regulation_voltage);
|
|
if (ret)
|
|
- return ret;
|
|
+ goto error_2;
|
|
ret = of_property_read_u32(np, "ti,charge-current",
|
|
&bq->init_data.charge_current);
|
|
if (ret)
|
|
- return ret;
|
|
+ goto error_2;
|
|
ret = of_property_read_u32(np, "ti,termination-current",
|
|
&bq->init_data.termination_current);
|
|
if (ret)
|
|
- return ret;
|
|
+ goto error_2;
|
|
ret = of_property_read_u32(np, "ti,resistor-sense",
|
|
&bq->init_data.resistor_sense);
|
|
if (ret)
|
|
- return ret;
|
|
+ goto error_2;
|
|
} else {
|
|
memcpy(&bq->init_data, pdata, sizeof(bq->init_data));
|
|
}
|
|
diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c
|
|
index ef1f4c928431..03bfac3655ef 100644
|
|
--- a/drivers/power/charger-manager.c
|
|
+++ b/drivers/power/charger-manager.c
|
|
@@ -97,6 +97,7 @@ static struct charger_global_desc *g_desc; /* init with setup_charger_manager */
|
|
static bool is_batt_present(struct charger_manager *cm)
|
|
{
|
|
union power_supply_propval val;
|
|
+ struct power_supply *psy;
|
|
bool present = false;
|
|
int i, ret;
|
|
|
|
@@ -107,16 +108,27 @@ static bool is_batt_present(struct charger_manager *cm)
|
|
case CM_NO_BATTERY:
|
|
break;
|
|
case CM_FUEL_GAUGE:
|
|
- ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
|
|
+ psy = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
|
|
+ if (!psy)
|
|
+ break;
|
|
+
|
|
+ ret = psy->get_property(psy,
|
|
POWER_SUPPLY_PROP_PRESENT, &val);
|
|
if (ret == 0 && val.intval)
|
|
present = true;
|
|
break;
|
|
case CM_CHARGER_STAT:
|
|
- for (i = 0; cm->charger_stat[i]; i++) {
|
|
- ret = cm->charger_stat[i]->get_property(
|
|
- cm->charger_stat[i],
|
|
- POWER_SUPPLY_PROP_PRESENT, &val);
|
|
+ for (i = 0; cm->desc->psy_charger_stat[i]; i++) {
|
|
+ psy = power_supply_get_by_name(
|
|
+ cm->desc->psy_charger_stat[i]);
|
|
+ if (!psy) {
|
|
+ dev_err(cm->dev, "Cannot find power supply \"%s\"\n",
|
|
+ cm->desc->psy_charger_stat[i]);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ ret = psy->get_property(psy, POWER_SUPPLY_PROP_PRESENT,
|
|
+ &val);
|
|
if (ret == 0 && val.intval) {
|
|
present = true;
|
|
break;
|
|
@@ -139,14 +151,20 @@ static bool is_batt_present(struct charger_manager *cm)
|
|
static bool is_ext_pwr_online(struct charger_manager *cm)
|
|
{
|
|
union power_supply_propval val;
|
|
+ struct power_supply *psy;
|
|
bool online = false;
|
|
int i, ret;
|
|
|
|
/* If at least one of them has one, it's yes. */
|
|
- for (i = 0; cm->charger_stat[i]; i++) {
|
|
- ret = cm->charger_stat[i]->get_property(
|
|
- cm->charger_stat[i],
|
|
- POWER_SUPPLY_PROP_ONLINE, &val);
|
|
+ for (i = 0; cm->desc->psy_charger_stat[i]; i++) {
|
|
+ psy = power_supply_get_by_name(cm->desc->psy_charger_stat[i]);
|
|
+ if (!psy) {
|
|
+ dev_err(cm->dev, "Cannot find power supply \"%s\"\n",
|
|
+ cm->desc->psy_charger_stat[i]);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ ret = psy->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &val);
|
|
if (ret == 0 && val.intval) {
|
|
online = true;
|
|
break;
|
|
@@ -167,12 +185,14 @@ static bool is_ext_pwr_online(struct charger_manager *cm)
|
|
static int get_batt_uV(struct charger_manager *cm, int *uV)
|
|
{
|
|
union power_supply_propval val;
|
|
+ struct power_supply *fuel_gauge;
|
|
int ret;
|
|
|
|
- if (!cm->fuel_gauge)
|
|
+ fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
|
|
+ if (!fuel_gauge)
|
|
return -ENODEV;
|
|
|
|
- ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
|
|
+ ret = fuel_gauge->get_property(fuel_gauge,
|
|
POWER_SUPPLY_PROP_VOLTAGE_NOW, &val);
|
|
if (ret)
|
|
return ret;
|
|
@@ -189,6 +209,7 @@ static bool is_charging(struct charger_manager *cm)
|
|
{
|
|
int i, ret;
|
|
bool charging = false;
|
|
+ struct power_supply *psy;
|
|
union power_supply_propval val;
|
|
|
|
/* If there is no battery, it cannot be charged */
|
|
@@ -196,17 +217,22 @@ static bool is_charging(struct charger_manager *cm)
|
|
return false;
|
|
|
|
/* If at least one of the charger is charging, return yes */
|
|
- for (i = 0; cm->charger_stat[i]; i++) {
|
|
+ for (i = 0; cm->desc->psy_charger_stat[i]; i++) {
|
|
/* 1. The charger sholuld not be DISABLED */
|
|
if (cm->emergency_stop)
|
|
continue;
|
|
if (!cm->charger_enabled)
|
|
continue;
|
|
|
|
+ psy = power_supply_get_by_name(cm->desc->psy_charger_stat[i]);
|
|
+ if (!psy) {
|
|
+ dev_err(cm->dev, "Cannot find power supply \"%s\"\n",
|
|
+ cm->desc->psy_charger_stat[i]);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
/* 2. The charger should be online (ext-power) */
|
|
- ret = cm->charger_stat[i]->get_property(
|
|
- cm->charger_stat[i],
|
|
- POWER_SUPPLY_PROP_ONLINE, &val);
|
|
+ ret = psy->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &val);
|
|
if (ret) {
|
|
dev_warn(cm->dev, "Cannot read ONLINE value from %s\n",
|
|
cm->desc->psy_charger_stat[i]);
|
|
@@ -219,9 +245,7 @@ static bool is_charging(struct charger_manager *cm)
|
|
* 3. The charger should not be FULL, DISCHARGING,
|
|
* or NOT_CHARGING.
|
|
*/
|
|
- ret = cm->charger_stat[i]->get_property(
|
|
- cm->charger_stat[i],
|
|
- POWER_SUPPLY_PROP_STATUS, &val);
|
|
+ ret = psy->get_property(psy, POWER_SUPPLY_PROP_STATUS, &val);
|
|
if (ret) {
|
|
dev_warn(cm->dev, "Cannot read STATUS value from %s\n",
|
|
cm->desc->psy_charger_stat[i]);
|
|
@@ -248,6 +272,7 @@ static bool is_full_charged(struct charger_manager *cm)
|
|
{
|
|
struct charger_desc *desc = cm->desc;
|
|
union power_supply_propval val;
|
|
+ struct power_supply *fuel_gauge;
|
|
int ret = 0;
|
|
int uV;
|
|
|
|
@@ -255,11 +280,15 @@ static bool is_full_charged(struct charger_manager *cm)
|
|
if (!is_batt_present(cm))
|
|
return false;
|
|
|
|
- if (cm->fuel_gauge && desc->fullbatt_full_capacity > 0) {
|
|
+ fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
|
|
+ if (!fuel_gauge)
|
|
+ return false;
|
|
+
|
|
+ if (desc->fullbatt_full_capacity > 0) {
|
|
val.intval = 0;
|
|
|
|
/* Not full if capacity of fuel gauge isn't full */
|
|
- ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
|
|
+ ret = fuel_gauge->get_property(fuel_gauge,
|
|
POWER_SUPPLY_PROP_CHARGE_FULL, &val);
|
|
if (!ret && val.intval > desc->fullbatt_full_capacity)
|
|
return true;
|
|
@@ -273,10 +302,10 @@ static bool is_full_charged(struct charger_manager *cm)
|
|
}
|
|
|
|
/* Full, if the capacity is more than fullbatt_soc */
|
|
- if (cm->fuel_gauge && desc->fullbatt_soc > 0) {
|
|
+ if (desc->fullbatt_soc > 0) {
|
|
val.intval = 0;
|
|
|
|
- ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
|
|
+ ret = fuel_gauge->get_property(fuel_gauge,
|
|
POWER_SUPPLY_PROP_CAPACITY, &val);
|
|
if (!ret && val.intval >= desc->fullbatt_soc)
|
|
return true;
|
|
@@ -551,6 +580,20 @@ static int check_charging_duration(struct charger_manager *cm)
|
|
return ret;
|
|
}
|
|
|
|
+static int cm_get_battery_temperature_by_psy(struct charger_manager *cm,
|
|
+ int *temp)
|
|
+{
|
|
+ struct power_supply *fuel_gauge;
|
|
+
|
|
+ fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
|
|
+ if (!fuel_gauge)
|
|
+ return -ENODEV;
|
|
+
|
|
+ return fuel_gauge->get_property(fuel_gauge,
|
|
+ POWER_SUPPLY_PROP_TEMP,
|
|
+ (union power_supply_propval *)temp);
|
|
+}
|
|
+
|
|
static int cm_get_battery_temperature(struct charger_manager *cm,
|
|
int *temp)
|
|
{
|
|
@@ -560,15 +603,18 @@ static int cm_get_battery_temperature(struct charger_manager *cm,
|
|
return -ENODEV;
|
|
|
|
#ifdef CONFIG_THERMAL
|
|
- ret = thermal_zone_get_temp(cm->tzd_batt, (unsigned long *)temp);
|
|
- if (!ret)
|
|
- /* Calibrate temperature unit */
|
|
- *temp /= 100;
|
|
-#else
|
|
- ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
|
|
- POWER_SUPPLY_PROP_TEMP,
|
|
- (union power_supply_propval *)temp);
|
|
+ if (cm->tzd_batt) {
|
|
+ ret = thermal_zone_get_temp(cm->tzd_batt, (unsigned long *)temp);
|
|
+ if (!ret)
|
|
+ /* Calibrate temperature unit */
|
|
+ *temp /= 100;
|
|
+ } else
|
|
#endif
|
|
+ {
|
|
+ /* if-else continued from CONFIG_THERMAL */
|
|
+ ret = cm_get_battery_temperature_by_psy(cm, temp);
|
|
+ }
|
|
+
|
|
return ret;
|
|
}
|
|
|
|
@@ -827,6 +873,7 @@ static int charger_get_property(struct power_supply *psy,
|
|
struct charger_manager *cm = container_of(psy,
|
|
struct charger_manager, charger_psy);
|
|
struct charger_desc *desc = cm->desc;
|
|
+ struct power_supply *fuel_gauge;
|
|
int ret = 0;
|
|
int uV;
|
|
|
|
@@ -857,14 +904,20 @@ static int charger_get_property(struct power_supply *psy,
|
|
ret = get_batt_uV(cm, &val->intval);
|
|
break;
|
|
case POWER_SUPPLY_PROP_CURRENT_NOW:
|
|
- ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
|
|
+ fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
|
|
+ if (!fuel_gauge) {
|
|
+ ret = -ENODEV;
|
|
+ break;
|
|
+ }
|
|
+ ret = fuel_gauge->get_property(fuel_gauge,
|
|
POWER_SUPPLY_PROP_CURRENT_NOW, val);
|
|
break;
|
|
case POWER_SUPPLY_PROP_TEMP:
|
|
case POWER_SUPPLY_PROP_TEMP_AMBIENT:
|
|
return cm_get_battery_temperature(cm, &val->intval);
|
|
case POWER_SUPPLY_PROP_CAPACITY:
|
|
- if (!cm->fuel_gauge) {
|
|
+ fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
|
|
+ if (!fuel_gauge) {
|
|
ret = -ENODEV;
|
|
break;
|
|
}
|
|
@@ -875,7 +928,7 @@ static int charger_get_property(struct power_supply *psy,
|
|
break;
|
|
}
|
|
|
|
- ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
|
|
+ ret = fuel_gauge->get_property(fuel_gauge,
|
|
POWER_SUPPLY_PROP_CAPACITY, val);
|
|
if (ret)
|
|
break;
|
|
@@ -924,7 +977,14 @@ static int charger_get_property(struct power_supply *psy,
|
|
break;
|
|
case POWER_SUPPLY_PROP_CHARGE_NOW:
|
|
if (is_charging(cm)) {
|
|
- ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
|
|
+ fuel_gauge = power_supply_get_by_name(
|
|
+ cm->desc->psy_fuel_gauge);
|
|
+ if (!fuel_gauge) {
|
|
+ ret = -ENODEV;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ ret = fuel_gauge->get_property(fuel_gauge,
|
|
POWER_SUPPLY_PROP_CHARGE_NOW,
|
|
val);
|
|
if (ret) {
|
|
@@ -1485,14 +1545,15 @@ err:
|
|
return ret;
|
|
}
|
|
|
|
-static int cm_init_thermal_data(struct charger_manager *cm)
|
|
+static int cm_init_thermal_data(struct charger_manager *cm,
|
|
+ struct power_supply *fuel_gauge)
|
|
{
|
|
struct charger_desc *desc = cm->desc;
|
|
union power_supply_propval val;
|
|
int ret;
|
|
|
|
/* Verify whether fuel gauge provides battery temperature */
|
|
- ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
|
|
+ ret = fuel_gauge->get_property(fuel_gauge,
|
|
POWER_SUPPLY_PROP_TEMP, &val);
|
|
|
|
if (!ret) {
|
|
@@ -1502,8 +1563,6 @@ static int cm_init_thermal_data(struct charger_manager *cm)
|
|
cm->desc->measure_battery_temp = true;
|
|
}
|
|
#ifdef CONFIG_THERMAL
|
|
- cm->tzd_batt = cm->fuel_gauge->tzd;
|
|
-
|
|
if (ret && desc->thermal_zone) {
|
|
cm->tzd_batt =
|
|
thermal_zone_get_zone_by_name(desc->thermal_zone);
|
|
@@ -1666,6 +1725,7 @@ static int charger_manager_probe(struct platform_device *pdev)
|
|
int ret = 0, i = 0;
|
|
int j = 0;
|
|
union power_supply_propval val;
|
|
+ struct power_supply *fuel_gauge;
|
|
|
|
if (g_desc && !rtc_dev && g_desc->rtc_name) {
|
|
rtc_dev = rtc_class_open(g_desc->rtc_name);
|
|
@@ -1729,23 +1789,20 @@ static int charger_manager_probe(struct platform_device *pdev)
|
|
while (desc->psy_charger_stat[i])
|
|
i++;
|
|
|
|
- cm->charger_stat = devm_kzalloc(&pdev->dev,
|
|
- sizeof(struct power_supply *) * i, GFP_KERNEL);
|
|
- if (!cm->charger_stat)
|
|
- return -ENOMEM;
|
|
-
|
|
+ /* Check if charger's supplies are present at probe */
|
|
for (i = 0; desc->psy_charger_stat[i]; i++) {
|
|
- cm->charger_stat[i] = power_supply_get_by_name(
|
|
- desc->psy_charger_stat[i]);
|
|
- if (!cm->charger_stat[i]) {
|
|
+ struct power_supply *psy;
|
|
+
|
|
+ psy = power_supply_get_by_name(desc->psy_charger_stat[i]);
|
|
+ if (!psy) {
|
|
dev_err(&pdev->dev, "Cannot find power supply \"%s\"\n",
|
|
desc->psy_charger_stat[i]);
|
|
return -ENODEV;
|
|
}
|
|
}
|
|
|
|
- cm->fuel_gauge = power_supply_get_by_name(desc->psy_fuel_gauge);
|
|
- if (!cm->fuel_gauge) {
|
|
+ fuel_gauge = power_supply_get_by_name(desc->psy_fuel_gauge);
|
|
+ if (!fuel_gauge) {
|
|
dev_err(&pdev->dev, "Cannot find power supply \"%s\"\n",
|
|
desc->psy_fuel_gauge);
|
|
return -ENODEV;
|
|
@@ -1788,13 +1845,13 @@ static int charger_manager_probe(struct platform_device *pdev)
|
|
cm->charger_psy.num_properties = psy_default.num_properties;
|
|
|
|
/* Find which optional psy-properties are available */
|
|
- if (!cm->fuel_gauge->get_property(cm->fuel_gauge,
|
|
+ if (!fuel_gauge->get_property(fuel_gauge,
|
|
POWER_SUPPLY_PROP_CHARGE_NOW, &val)) {
|
|
cm->charger_psy.properties[cm->charger_psy.num_properties] =
|
|
POWER_SUPPLY_PROP_CHARGE_NOW;
|
|
cm->charger_psy.num_properties++;
|
|
}
|
|
- if (!cm->fuel_gauge->get_property(cm->fuel_gauge,
|
|
+ if (!fuel_gauge->get_property(fuel_gauge,
|
|
POWER_SUPPLY_PROP_CURRENT_NOW,
|
|
&val)) {
|
|
cm->charger_psy.properties[cm->charger_psy.num_properties] =
|
|
@@ -1802,7 +1859,7 @@ static int charger_manager_probe(struct platform_device *pdev)
|
|
cm->charger_psy.num_properties++;
|
|
}
|
|
|
|
- ret = cm_init_thermal_data(cm);
|
|
+ ret = cm_init_thermal_data(cm, fuel_gauge);
|
|
if (ret) {
|
|
dev_err(&pdev->dev, "Failed to initialize thermal data\n");
|
|
cm->desc->measure_battery_temp = false;
|
|
@@ -2059,8 +2116,8 @@ static bool find_power_supply(struct charger_manager *cm,
|
|
int i;
|
|
bool found = false;
|
|
|
|
- for (i = 0; cm->charger_stat[i]; i++) {
|
|
- if (psy == cm->charger_stat[i]) {
|
|
+ for (i = 0; cm->desc->psy_charger_stat[i]; i++) {
|
|
+ if (!strcmp(psy->name, cm->desc->psy_charger_stat[i])) {
|
|
found = true;
|
|
break;
|
|
}
|
|
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
|
|
index edb4d46fa874..96b6664bb1cf 100644
|
|
--- a/drivers/scsi/scsi_error.c
|
|
+++ b/drivers/scsi/scsi_error.c
|
|
@@ -1984,8 +1984,10 @@ static void scsi_restart_operations(struct Scsi_Host *shost)
|
|
* is no point trying to lock the door of an off-line device.
|
|
*/
|
|
shost_for_each_device(sdev, shost) {
|
|
- if (scsi_device_online(sdev) && sdev->locked)
|
|
+ if (scsi_device_online(sdev) && sdev->was_reset && sdev->locked) {
|
|
scsi_eh_lock_door(sdev);
|
|
+ sdev->was_reset = 0;
|
|
+ }
|
|
}
|
|
|
|
/*
|
|
diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c
|
|
index b01fb6c527e3..d43c544d3b68 100644
|
|
--- a/fs/btrfs/compression.c
|
|
+++ b/fs/btrfs/compression.c
|
|
@@ -472,7 +472,7 @@ static noinline int add_ra_bio_pages(struct inode *inode,
|
|
rcu_read_lock();
|
|
page = radix_tree_lookup(&mapping->page_tree, pg_index);
|
|
rcu_read_unlock();
|
|
- if (page) {
|
|
+ if (page && !radix_tree_exceptional_entry(page)) {
|
|
misses++;
|
|
if (misses > 4)
|
|
break;
|
|
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
|
|
index a9a881ed8cbe..f6d00df99a8c 100644
|
|
--- a/fs/btrfs/file.c
|
|
+++ b/fs/btrfs/file.c
|
|
@@ -425,13 +425,8 @@ static noinline int btrfs_copy_from_user(loff_t pos, int num_pages,
|
|
struct page *page = prepared_pages[pg];
|
|
/*
|
|
* Copy data from userspace to the current page
|
|
- *
|
|
- * Disable pagefault to avoid recursive lock since
|
|
- * the pages are already locked
|
|
*/
|
|
- pagefault_disable();
|
|
copied = iov_iter_copy_from_user_atomic(page, i, offset, count);
|
|
- pagefault_enable();
|
|
|
|
/* Flush processor's dcache for this page */
|
|
flush_dcache_page(page);
|
|
diff --git a/fs/cramfs/inode.c b/fs/cramfs/inode.c
|
|
index 06610cf94d57..a1f801c14fbc 100644
|
|
--- a/fs/cramfs/inode.c
|
|
+++ b/fs/cramfs/inode.c
|
|
@@ -195,8 +195,7 @@ static void *cramfs_read(struct super_block *sb, unsigned int offset, unsigned i
|
|
struct page *page = NULL;
|
|
|
|
if (blocknr + i < devsize) {
|
|
- page = read_mapping_page_async(mapping, blocknr + i,
|
|
- NULL);
|
|
+ page = read_mapping_page(mapping, blocknr + i, NULL);
|
|
/* synchronous error? */
|
|
if (IS_ERR(page))
|
|
page = NULL;
|
|
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
|
|
index 77bcc303c3ae..a91d3b4d32f3 100644
|
|
--- a/fs/fuse/file.c
|
|
+++ b/fs/fuse/file.c
|
|
@@ -1003,9 +1003,7 @@ static ssize_t fuse_fill_write_pages(struct fuse_req *req,
|
|
if (mapping_writably_mapped(mapping))
|
|
flush_dcache_page(page);
|
|
|
|
- pagefault_disable();
|
|
tmp = iov_iter_copy_from_user_atomic(page, ii, offset, bytes);
|
|
- pagefault_enable();
|
|
flush_dcache_page(page);
|
|
|
|
mark_page_accessed(page);
|
|
diff --git a/fs/gfs2/meta_io.c b/fs/gfs2/meta_io.c
|
|
index c7f24690ed05..b82a9c99e18b 100644
|
|
--- a/fs/gfs2/meta_io.c
|
|
+++ b/fs/gfs2/meta_io.c
|
|
@@ -97,6 +97,11 @@ const struct address_space_operations gfs2_meta_aops = {
|
|
.releasepage = gfs2_releasepage,
|
|
};
|
|
|
|
+const struct address_space_operations gfs2_rgrp_aops = {
|
|
+ .writepage = gfs2_aspace_writepage,
|
|
+ .releasepage = gfs2_releasepage,
|
|
+};
|
|
+
|
|
/**
|
|
* gfs2_getbuf - Get a buffer with a given address space
|
|
* @gl: the glock
|
|
diff --git a/fs/gfs2/meta_io.h b/fs/gfs2/meta_io.h
|
|
index 4823b934208a..ac5d8027d335 100644
|
|
--- a/fs/gfs2/meta_io.h
|
|
+++ b/fs/gfs2/meta_io.h
|
|
@@ -38,12 +38,15 @@ static inline void gfs2_buffer_copy_tail(struct buffer_head *to_bh,
|
|
}
|
|
|
|
extern const struct address_space_operations gfs2_meta_aops;
|
|
+extern const struct address_space_operations gfs2_rgrp_aops;
|
|
|
|
static inline struct gfs2_sbd *gfs2_mapping2sbd(struct address_space *mapping)
|
|
{
|
|
struct inode *inode = mapping->host;
|
|
if (mapping->a_ops == &gfs2_meta_aops)
|
|
return (((struct gfs2_glock *)mapping) - 1)->gl_sbd;
|
|
+ else if (mapping->a_ops == &gfs2_rgrp_aops)
|
|
+ return container_of(mapping, struct gfs2_sbd, sd_aspace);
|
|
else
|
|
return inode->i_sb->s_fs_info;
|
|
}
|
|
diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c
|
|
index c6872d09561a..f6c9d83aa39b 100644
|
|
--- a/fs/gfs2/ops_fstype.c
|
|
+++ b/fs/gfs2/ops_fstype.c
|
|
@@ -104,7 +104,7 @@ static struct gfs2_sbd *init_sbd(struct super_block *sb)
|
|
mapping = &sdp->sd_aspace;
|
|
|
|
address_space_init_once(mapping);
|
|
- mapping->a_ops = &gfs2_meta_aops;
|
|
+ mapping->a_ops = &gfs2_rgrp_aops;
|
|
mapping->host = sb->s_bdev->bd_inode;
|
|
mapping->flags = 0;
|
|
mapping_set_gfp_mask(mapping, GFP_NOFS);
|
|
diff --git a/fs/ioprio.c b/fs/ioprio.c
|
|
index e50170ca7c33..31666c92b46a 100644
|
|
--- a/fs/ioprio.c
|
|
+++ b/fs/ioprio.c
|
|
@@ -157,14 +157,16 @@ out:
|
|
|
|
int ioprio_best(unsigned short aprio, unsigned short bprio)
|
|
{
|
|
- unsigned short aclass = IOPRIO_PRIO_CLASS(aprio);
|
|
- unsigned short bclass = IOPRIO_PRIO_CLASS(bprio);
|
|
+ unsigned short aclass;
|
|
+ unsigned short bclass;
|
|
|
|
- if (aclass == IOPRIO_CLASS_NONE)
|
|
- aclass = IOPRIO_CLASS_BE;
|
|
- if (bclass == IOPRIO_CLASS_NONE)
|
|
- bclass = IOPRIO_CLASS_BE;
|
|
+ if (!ioprio_valid(aprio))
|
|
+ aprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, IOPRIO_NORM);
|
|
+ if (!ioprio_valid(bprio))
|
|
+ bprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, IOPRIO_NORM);
|
|
|
|
+ aclass = IOPRIO_PRIO_CLASS(aprio);
|
|
+ bclass = IOPRIO_PRIO_CLASS(bprio);
|
|
if (aclass == bclass)
|
|
return min(aprio, bprio);
|
|
if (aclass > bclass)
|
|
diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c
|
|
index a69e426435dd..5b234db85854 100644
|
|
--- a/fs/jffs2/fs.c
|
|
+++ b/fs/jffs2/fs.c
|
|
@@ -687,7 +687,7 @@ unsigned char *jffs2_gc_fetch_page(struct jffs2_sb_info *c,
|
|
struct inode *inode = OFNI_EDONI_2SFFJ(f);
|
|
struct page *pg;
|
|
|
|
- pg = read_cache_page_async(inode->i_mapping, offset >> PAGE_CACHE_SHIFT,
|
|
+ pg = read_cache_page(inode->i_mapping, offset >> PAGE_CACHE_SHIFT,
|
|
(void *)jffs2_do_readpage_unlock, inode);
|
|
if (IS_ERR(pg))
|
|
return (void *)pg;
|
|
diff --git a/fs/nfs/blocklayout/blocklayout.c b/fs/nfs/blocklayout/blocklayout.c
|
|
index 56ff823ca82e..65d849bdf77a 100644
|
|
--- a/fs/nfs/blocklayout/blocklayout.c
|
|
+++ b/fs/nfs/blocklayout/blocklayout.c
|
|
@@ -1213,7 +1213,7 @@ static u64 pnfs_num_cont_bytes(struct inode *inode, pgoff_t idx)
|
|
end = DIV_ROUND_UP(i_size_read(inode), PAGE_CACHE_SIZE);
|
|
if (end != NFS_I(inode)->npages) {
|
|
rcu_read_lock();
|
|
- end = radix_tree_next_hole(&mapping->page_tree, idx + 1, ULONG_MAX);
|
|
+ end = page_cache_next_hole(mapping, idx + 1, ULONG_MAX);
|
|
rcu_read_unlock();
|
|
}
|
|
|
|
diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c
|
|
index 5d8ccecf5f5c..3ed1be9aade3 100644
|
|
--- a/fs/nfs/delegation.c
|
|
+++ b/fs/nfs/delegation.c
|
|
@@ -109,6 +109,8 @@ again:
|
|
continue;
|
|
if (!test_bit(NFS_DELEGATED_STATE, &state->flags))
|
|
continue;
|
|
+ if (!nfs4_valid_open_stateid(state))
|
|
+ continue;
|
|
if (!nfs4_stateid_match(&state->stateid, stateid))
|
|
continue;
|
|
get_nfs_open_context(ctx);
|
|
@@ -177,7 +179,11 @@ static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *
|
|
{
|
|
int res = 0;
|
|
|
|
- res = nfs4_proc_delegreturn(inode, delegation->cred, &delegation->stateid, issync);
|
|
+ if (!test_bit(NFS_DELEGATION_REVOKED, &delegation->flags))
|
|
+ res = nfs4_proc_delegreturn(inode,
|
|
+ delegation->cred,
|
|
+ &delegation->stateid,
|
|
+ issync);
|
|
nfs_free_delegation(delegation);
|
|
return res;
|
|
}
|
|
@@ -364,11 +370,13 @@ static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation
|
|
{
|
|
struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
|
|
struct nfs_inode *nfsi = NFS_I(inode);
|
|
- int err;
|
|
+ int err = 0;
|
|
|
|
if (delegation == NULL)
|
|
return 0;
|
|
do {
|
|
+ if (test_bit(NFS_DELEGATION_REVOKED, &delegation->flags))
|
|
+ break;
|
|
err = nfs_delegation_claim_opens(inode, &delegation->stateid);
|
|
if (!issync || err != -EAGAIN)
|
|
break;
|
|
@@ -589,10 +597,23 @@ static void nfs_client_mark_return_unused_delegation_types(struct nfs_client *cl
|
|
rcu_read_unlock();
|
|
}
|
|
|
|
+static void nfs_revoke_delegation(struct inode *inode)
|
|
+{
|
|
+ struct nfs_delegation *delegation;
|
|
+ rcu_read_lock();
|
|
+ delegation = rcu_dereference(NFS_I(inode)->delegation);
|
|
+ if (delegation != NULL) {
|
|
+ set_bit(NFS_DELEGATION_REVOKED, &delegation->flags);
|
|
+ nfs_mark_return_delegation(NFS_SERVER(inode), delegation);
|
|
+ }
|
|
+ rcu_read_unlock();
|
|
+}
|
|
+
|
|
void nfs_remove_bad_delegation(struct inode *inode)
|
|
{
|
|
struct nfs_delegation *delegation;
|
|
|
|
+ nfs_revoke_delegation(inode);
|
|
delegation = nfs_inode_detach_delegation(inode);
|
|
if (delegation) {
|
|
nfs_inode_find_state_and_recover(inode, &delegation->stateid);
|
|
diff --git a/fs/nfs/delegation.h b/fs/nfs/delegation.h
|
|
index 9a79c7a99d6d..e02b090ab9da 100644
|
|
--- a/fs/nfs/delegation.h
|
|
+++ b/fs/nfs/delegation.h
|
|
@@ -31,6 +31,7 @@ enum {
|
|
NFS_DELEGATION_RETURN_IF_CLOSED,
|
|
NFS_DELEGATION_REFERENCED,
|
|
NFS_DELEGATION_RETURNING,
|
|
+ NFS_DELEGATION_REVOKED,
|
|
};
|
|
|
|
int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res);
|
|
diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c
|
|
index b8797ae6831f..de2543d3c283 100644
|
|
--- a/fs/nfs/direct.c
|
|
+++ b/fs/nfs/direct.c
|
|
@@ -178,6 +178,7 @@ static void nfs_direct_req_free(struct kref *kref)
|
|
{
|
|
struct nfs_direct_req *dreq = container_of(kref, struct nfs_direct_req, kref);
|
|
|
|
+ nfs_free_pnfs_ds_cinfo(&dreq->ds_cinfo);
|
|
if (dreq->l_ctx != NULL)
|
|
nfs_put_lock_context(dreq->l_ctx);
|
|
if (dreq->ctx != NULL)
|
|
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
|
|
index 15f9d98627a4..6659ce545f15 100644
|
|
--- a/fs/nfs/inode.c
|
|
+++ b/fs/nfs/inode.c
|
|
@@ -592,7 +592,7 @@ int nfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
|
|
{
|
|
struct inode *inode = dentry->d_inode;
|
|
int need_atime = NFS_I(inode)->cache_validity & NFS_INO_INVALID_ATIME;
|
|
- int err;
|
|
+ int err = 0;
|
|
|
|
trace_nfs_getattr_enter(inode);
|
|
/* Flush out writes to the server in order to update c/mtime. */
|
|
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
|
|
index da657b7804a5..bd01803d0656 100644
|
|
--- a/fs/nfs/nfs4proc.c
|
|
+++ b/fs/nfs/nfs4proc.c
|
|
@@ -1587,7 +1587,7 @@ static int nfs4_handle_delegation_recall_error(struct nfs_server *server, struct
|
|
nfs_inode_find_state_and_recover(state->inode,
|
|
stateid);
|
|
nfs4_schedule_stateid_recovery(server, state);
|
|
- return 0;
|
|
+ return -EAGAIN;
|
|
case -NFS4ERR_DELAY:
|
|
case -NFS4ERR_GRACE:
|
|
set_bit(NFS_DELEGATED_STATE, &state->flags);
|
|
@@ -2034,46 +2034,60 @@ static int nfs4_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *sta
|
|
return ret;
|
|
}
|
|
|
|
+static void nfs_finish_clear_delegation_stateid(struct nfs4_state *state)
|
|
+{
|
|
+ nfs_remove_bad_delegation(state->inode);
|
|
+ write_seqlock(&state->seqlock);
|
|
+ nfs4_stateid_copy(&state->stateid, &state->open_stateid);
|
|
+ write_sequnlock(&state->seqlock);
|
|
+ clear_bit(NFS_DELEGATED_STATE, &state->flags);
|
|
+}
|
|
+
|
|
+static void nfs40_clear_delegation_stateid(struct nfs4_state *state)
|
|
+{
|
|
+ if (rcu_access_pointer(NFS_I(state->inode)->delegation) != NULL)
|
|
+ nfs_finish_clear_delegation_stateid(state);
|
|
+}
|
|
+
|
|
+static int nfs40_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *state)
|
|
+{
|
|
+ /* NFSv4.0 doesn't allow for delegation recovery on open expire */
|
|
+ nfs40_clear_delegation_stateid(state);
|
|
+ return nfs4_open_expired(sp, state);
|
|
+}
|
|
+
|
|
#if defined(CONFIG_NFS_V4_1)
|
|
-static void nfs41_clear_delegation_stateid(struct nfs4_state *state)
|
|
+static void nfs41_check_delegation_stateid(struct nfs4_state *state)
|
|
{
|
|
struct nfs_server *server = NFS_SERVER(state->inode);
|
|
- nfs4_stateid *stateid = &state->stateid;
|
|
+ nfs4_stateid stateid;
|
|
struct nfs_delegation *delegation;
|
|
- struct rpc_cred *cred = NULL;
|
|
- int status = -NFS4ERR_BAD_STATEID;
|
|
-
|
|
- /* If a state reset has been done, test_stateid is unneeded */
|
|
- if (test_bit(NFS_DELEGATED_STATE, &state->flags) == 0)
|
|
- return;
|
|
+ struct rpc_cred *cred;
|
|
+ int status;
|
|
|
|
/* Get the delegation credential for use by test/free_stateid */
|
|
rcu_read_lock();
|
|
delegation = rcu_dereference(NFS_I(state->inode)->delegation);
|
|
- if (delegation != NULL &&
|
|
- nfs4_stateid_match(&delegation->stateid, stateid)) {
|
|
- cred = get_rpccred(delegation->cred);
|
|
- rcu_read_unlock();
|
|
- status = nfs41_test_stateid(server, stateid, cred);
|
|
- trace_nfs4_test_delegation_stateid(state, NULL, status);
|
|
- } else
|
|
+ if (delegation == NULL) {
|
|
rcu_read_unlock();
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ nfs4_stateid_copy(&stateid, &delegation->stateid);
|
|
+ cred = get_rpccred(delegation->cred);
|
|
+ rcu_read_unlock();
|
|
+ status = nfs41_test_stateid(server, &stateid, cred);
|
|
+ trace_nfs4_test_delegation_stateid(state, NULL, status);
|
|
|
|
if (status != NFS_OK) {
|
|
/* Free the stateid unless the server explicitly
|
|
* informs us the stateid is unrecognized. */
|
|
if (status != -NFS4ERR_BAD_STATEID)
|
|
- nfs41_free_stateid(server, stateid, cred);
|
|
- nfs_remove_bad_delegation(state->inode);
|
|
-
|
|
- write_seqlock(&state->seqlock);
|
|
- nfs4_stateid_copy(&state->stateid, &state->open_stateid);
|
|
- write_sequnlock(&state->seqlock);
|
|
- clear_bit(NFS_DELEGATED_STATE, &state->flags);
|
|
+ nfs41_free_stateid(server, &stateid, cred);
|
|
+ nfs_finish_clear_delegation_stateid(state);
|
|
}
|
|
|
|
- if (cred != NULL)
|
|
- put_rpccred(cred);
|
|
+ put_rpccred(cred);
|
|
}
|
|
|
|
/**
|
|
@@ -2117,7 +2131,7 @@ static int nfs41_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *st
|
|
{
|
|
int status;
|
|
|
|
- nfs41_clear_delegation_stateid(state);
|
|
+ nfs41_check_delegation_stateid(state);
|
|
status = nfs41_check_open_stateid(state);
|
|
if (status != NFS_OK)
|
|
status = nfs4_open_expired(sp, state);
|
|
@@ -8255,7 +8269,7 @@ static const struct nfs4_state_recovery_ops nfs41_reboot_recovery_ops = {
|
|
static const struct nfs4_state_recovery_ops nfs40_nograce_recovery_ops = {
|
|
.owner_flag_bit = NFS_OWNER_RECLAIM_NOGRACE,
|
|
.state_flag_bit = NFS_STATE_RECLAIM_NOGRACE,
|
|
- .recover_open = nfs4_open_expired,
|
|
+ .recover_open = nfs40_open_expired,
|
|
.recover_lock = nfs4_lock_expired,
|
|
.establish_clid = nfs4_init_clientid,
|
|
};
|
|
diff --git a/fs/super.c b/fs/super.c
|
|
index 88a6bc6e3cc9..440ef51cd696 100644
|
|
--- a/fs/super.c
|
|
+++ b/fs/super.c
|
|
@@ -114,9 +114,14 @@ static unsigned long super_cache_count(struct shrinker *shrink,
|
|
|
|
sb = container_of(shrink, struct super_block, s_shrink);
|
|
|
|
- if (!grab_super_passive(sb))
|
|
- return 0;
|
|
-
|
|
+ /*
|
|
+ * Don't call grab_super_passive as it is a potential
|
|
+ * scalability bottleneck. The counts could get updated
|
|
+ * between super_cache_count and super_cache_scan anyway.
|
|
+ * Call to super_cache_count with shrinker_rwsem held
|
|
+ * ensures the safety of call to list_lru_count_node() and
|
|
+ * s_op->nr_cached_objects().
|
|
+ */
|
|
if (sb->s_op && sb->s_op->nr_cached_objects)
|
|
total_objects = sb->s_op->nr_cached_objects(sb,
|
|
sc->nid);
|
|
@@ -127,7 +132,6 @@ static unsigned long super_cache_count(struct shrinker *shrink,
|
|
sc->nid);
|
|
|
|
total_objects = vfs_pressure_ratio(total_objects);
|
|
- drop_super(sb);
|
|
return total_objects;
|
|
}
|
|
|
|
@@ -278,10 +282,8 @@ void deactivate_locked_super(struct super_block *s)
|
|
struct file_system_type *fs = s->s_type;
|
|
if (atomic_dec_and_test(&s->s_active)) {
|
|
cleancache_invalidate_fs(s);
|
|
- fs->kill_sb(s);
|
|
-
|
|
- /* caches are now gone, we can safely kill the shrinker now */
|
|
unregister_shrinker(&s->s_shrink);
|
|
+ fs->kill_sb(s);
|
|
|
|
put_filesystem(fs);
|
|
put_super(s);
|
|
diff --git a/include/dt-bindings/pinctrl/dra.h b/include/dt-bindings/pinctrl/dra.h
|
|
index 3d33794e4f3e..7448edff4723 100644
|
|
--- a/include/dt-bindings/pinctrl/dra.h
|
|
+++ b/include/dt-bindings/pinctrl/dra.h
|
|
@@ -40,8 +40,8 @@
|
|
|
|
/* Active pin states */
|
|
#define PIN_OUTPUT (0 | PULL_DIS)
|
|
-#define PIN_OUTPUT_PULLUP (PIN_OUTPUT | PULL_ENA | PULL_UP)
|
|
-#define PIN_OUTPUT_PULLDOWN (PIN_OUTPUT | PULL_ENA)
|
|
+#define PIN_OUTPUT_PULLUP (PULL_UP)
|
|
+#define PIN_OUTPUT_PULLDOWN (0)
|
|
#define PIN_INPUT (INPUT_EN | PULL_DIS)
|
|
#define PIN_INPUT_SLEW (INPUT_EN | SLEWCONTROL)
|
|
#define PIN_INPUT_PULLUP (PULL_ENA | INPUT_EN | PULL_UP)
|
|
diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h
|
|
index 67301a405712..879065d8d208 100644
|
|
--- a/include/linux/clocksource.h
|
|
+++ b/include/linux/clocksource.h
|
|
@@ -289,7 +289,7 @@ extern struct clocksource* clocksource_get_next(void);
|
|
extern void clocksource_change_rating(struct clocksource *cs, int rating);
|
|
extern void clocksource_suspend(void);
|
|
extern void clocksource_resume(void);
|
|
-extern struct clocksource * __init __weak clocksource_default_clock(void);
|
|
+extern struct clocksource * __init clocksource_default_clock(void);
|
|
extern void clocksource_mark_unstable(struct clocksource *cs);
|
|
|
|
extern u64
|
|
diff --git a/include/linux/compaction.h b/include/linux/compaction.h
|
|
index 7e1c76e3cd68..01e3132820da 100644
|
|
--- a/include/linux/compaction.h
|
|
+++ b/include/linux/compaction.h
|
|
@@ -22,7 +22,7 @@ extern int sysctl_extfrag_handler(struct ctl_table *table, int write,
|
|
extern int fragmentation_index(struct zone *zone, unsigned int order);
|
|
extern unsigned long try_to_compact_pages(struct zonelist *zonelist,
|
|
int order, gfp_t gfp_mask, nodemask_t *mask,
|
|
- bool sync, bool *contended);
|
|
+ enum migrate_mode mode, bool *contended);
|
|
extern void compact_pgdat(pg_data_t *pgdat, int order);
|
|
extern void reset_isolation_suitable(pg_data_t *pgdat);
|
|
extern unsigned long compaction_suitable(struct zone *zone, int order);
|
|
@@ -91,7 +91,7 @@ static inline bool compaction_restarting(struct zone *zone, int order)
|
|
#else
|
|
static inline unsigned long try_to_compact_pages(struct zonelist *zonelist,
|
|
int order, gfp_t gfp_mask, nodemask_t *nodemask,
|
|
- bool sync, bool *contended)
|
|
+ enum migrate_mode mode, bool *contended)
|
|
{
|
|
return COMPACT_CONTINUE;
|
|
}
|
|
diff --git a/include/linux/crash_dump.h b/include/linux/crash_dump.h
|
|
index 7032518f8542..60023e5d3169 100644
|
|
--- a/include/linux/crash_dump.h
|
|
+++ b/include/linux/crash_dump.h
|
|
@@ -14,14 +14,13 @@
|
|
extern unsigned long long elfcorehdr_addr;
|
|
extern unsigned long long elfcorehdr_size;
|
|
|
|
-extern int __weak elfcorehdr_alloc(unsigned long long *addr,
|
|
- unsigned long long *size);
|
|
-extern void __weak elfcorehdr_free(unsigned long long addr);
|
|
-extern ssize_t __weak elfcorehdr_read(char *buf, size_t count, u64 *ppos);
|
|
-extern ssize_t __weak elfcorehdr_read_notes(char *buf, size_t count, u64 *ppos);
|
|
-extern int __weak remap_oldmem_pfn_range(struct vm_area_struct *vma,
|
|
- unsigned long from, unsigned long pfn,
|
|
- unsigned long size, pgprot_t prot);
|
|
+extern int elfcorehdr_alloc(unsigned long long *addr, unsigned long long *size);
|
|
+extern void elfcorehdr_free(unsigned long long addr);
|
|
+extern ssize_t elfcorehdr_read(char *buf, size_t count, u64 *ppos);
|
|
+extern ssize_t elfcorehdr_read_notes(char *buf, size_t count, u64 *ppos);
|
|
+extern int remap_oldmem_pfn_range(struct vm_area_struct *vma,
|
|
+ unsigned long from, unsigned long pfn,
|
|
+ unsigned long size, pgprot_t prot);
|
|
|
|
extern ssize_t copy_oldmem_page(unsigned long, char *, size_t,
|
|
unsigned long, int);
|
|
diff --git a/include/linux/kgdb.h b/include/linux/kgdb.h
|
|
index 6b06d378f3df..e465bb15912d 100644
|
|
--- a/include/linux/kgdb.h
|
|
+++ b/include/linux/kgdb.h
|
|
@@ -283,7 +283,7 @@ struct kgdb_io {
|
|
|
|
extern struct kgdb_arch arch_kgdb_ops;
|
|
|
|
-extern unsigned long __weak kgdb_arch_pc(int exception, struct pt_regs *regs);
|
|
+extern unsigned long kgdb_arch_pc(int exception, struct pt_regs *regs);
|
|
|
|
#ifdef CONFIG_SERIAL_KGDB_NMI
|
|
extern int kgdb_register_nmi_console(void);
|
|
diff --git a/include/linux/memory.h b/include/linux/memory.h
|
|
index bb7384e3c3d8..8b8d8d12348e 100644
|
|
--- a/include/linux/memory.h
|
|
+++ b/include/linux/memory.h
|
|
@@ -35,7 +35,7 @@ struct memory_block {
|
|
};
|
|
|
|
int arch_get_memory_phys_device(unsigned long start_pfn);
|
|
-unsigned long __weak memory_block_size_bytes(void);
|
|
+unsigned long memory_block_size_bytes(void);
|
|
|
|
/* These states are exposed to userspace as text strings in sysfs */
|
|
#define MEM_ONLINE (1<<0) /* exposed to userspace */
|
|
diff --git a/include/linux/migrate.h b/include/linux/migrate.h
|
|
index 84a31ad0b791..a2901c414664 100644
|
|
--- a/include/linux/migrate.h
|
|
+++ b/include/linux/migrate.h
|
|
@@ -5,7 +5,9 @@
|
|
#include <linux/mempolicy.h>
|
|
#include <linux/migrate_mode.h>
|
|
|
|
-typedef struct page *new_page_t(struct page *, unsigned long private, int **);
|
|
+typedef struct page *new_page_t(struct page *page, unsigned long private,
|
|
+ int **reason);
|
|
+typedef void free_page_t(struct page *page, unsigned long private);
|
|
|
|
/*
|
|
* Return values from addresss_space_operations.migratepage():
|
|
@@ -38,7 +40,7 @@ enum migrate_reason {
|
|
extern void putback_movable_pages(struct list_head *l);
|
|
extern int migrate_page(struct address_space *,
|
|
struct page *, struct page *, enum migrate_mode);
|
|
-extern int migrate_pages(struct list_head *l, new_page_t x,
|
|
+extern int migrate_pages(struct list_head *l, new_page_t new, free_page_t free,
|
|
unsigned long private, enum migrate_mode mode, int reason);
|
|
|
|
extern int migrate_prep(void);
|
|
@@ -56,8 +58,9 @@ extern int migrate_page_move_mapping(struct address_space *mapping,
|
|
#else
|
|
|
|
static inline void putback_movable_pages(struct list_head *l) {}
|
|
-static inline int migrate_pages(struct list_head *l, new_page_t x,
|
|
- unsigned long private, enum migrate_mode mode, int reason)
|
|
+static inline int migrate_pages(struct list_head *l, new_page_t new,
|
|
+ free_page_t free, unsigned long private, enum migrate_mode mode,
|
|
+ int reason)
|
|
{ return -ENOSYS; }
|
|
|
|
static inline int migrate_prep(void) { return -ENOSYS; }
|
|
diff --git a/include/linux/mm.h b/include/linux/mm.h
|
|
index 0a0b024ec7e8..d5039daf1e1c 100644
|
|
--- a/include/linux/mm.h
|
|
+++ b/include/linux/mm.h
|
|
@@ -1041,6 +1041,14 @@ extern void show_free_areas(unsigned int flags);
|
|
extern bool skip_free_areas_node(unsigned int flags, int nid);
|
|
|
|
int shmem_zero_setup(struct vm_area_struct *);
|
|
+#ifdef CONFIG_SHMEM
|
|
+bool shmem_mapping(struct address_space *mapping);
|
|
+#else
|
|
+static inline bool shmem_mapping(struct address_space *mapping)
|
|
+{
|
|
+ return false;
|
|
+}
|
|
+#endif
|
|
|
|
extern int can_do_mlock(void);
|
|
extern int user_shm_lock(size_t, struct user_struct *);
|
|
@@ -1848,9 +1856,6 @@ void page_cache_async_readahead(struct address_space *mapping,
|
|
unsigned long size);
|
|
|
|
unsigned long max_sane_readahead(unsigned long nr);
|
|
-unsigned long ra_submit(struct file_ra_state *ra,
|
|
- struct address_space *mapping,
|
|
- struct file *filp);
|
|
|
|
/* Generic expand stack which grows the stack according to GROWS{UP,DOWN} */
|
|
extern int expand_stack(struct vm_area_struct *vma, unsigned long address);
|
|
diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
|
|
index e6800f0c0d7b..18843532a0c9 100644
|
|
--- a/include/linux/mmzone.h
|
|
+++ b/include/linux/mmzone.h
|
|
@@ -361,9 +361,10 @@ struct zone {
|
|
/* Set to true when the PG_migrate_skip bits should be cleared */
|
|
bool compact_blockskip_flush;
|
|
|
|
- /* pfns where compaction scanners should start */
|
|
+ /* pfn where compaction free scanner should start */
|
|
unsigned long compact_cached_free_pfn;
|
|
- unsigned long compact_cached_migrate_pfn;
|
|
+ /* pfn where async and sync compaction migration scanner should start */
|
|
+ unsigned long compact_cached_migrate_pfn[2];
|
|
#endif
|
|
#ifdef CONFIG_MEMORY_HOTPLUG
|
|
/* see spanned/present_pages for more description */
|
|
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
|
|
index 5624e4e2763c..53988cb3c05a 100644
|
|
--- a/include/linux/nfs_xdr.h
|
|
+++ b/include/linux/nfs_xdr.h
|
|
@@ -1247,11 +1247,22 @@ struct nfs41_free_stateid_res {
|
|
unsigned int status;
|
|
};
|
|
|
|
+static inline void
|
|
+nfs_free_pnfs_ds_cinfo(struct pnfs_ds_commit_info *cinfo)
|
|
+{
|
|
+ kfree(cinfo->buckets);
|
|
+}
|
|
+
|
|
#else
|
|
|
|
struct pnfs_ds_commit_info {
|
|
};
|
|
|
|
+static inline void
|
|
+nfs_free_pnfs_ds_cinfo(struct pnfs_ds_commit_info *cinfo)
|
|
+{
|
|
+}
|
|
+
|
|
#endif /* CONFIG_NFS_V4_1 */
|
|
|
|
struct nfs_page;
|
|
diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h
|
|
index 1710d1b060ba..09c1b03867d9 100644
|
|
--- a/include/linux/pagemap.h
|
|
+++ b/include/linux/pagemap.h
|
|
@@ -243,12 +243,20 @@ static inline struct page *page_cache_alloc_readahead(struct address_space *x)
|
|
|
|
typedef int filler_t(void *, struct page *);
|
|
|
|
-extern struct page * find_get_page(struct address_space *mapping,
|
|
- pgoff_t index);
|
|
-extern struct page * find_lock_page(struct address_space *mapping,
|
|
- pgoff_t index);
|
|
-extern struct page * find_or_create_page(struct address_space *mapping,
|
|
- pgoff_t index, gfp_t gfp_mask);
|
|
+pgoff_t page_cache_next_hole(struct address_space *mapping,
|
|
+ pgoff_t index, unsigned long max_scan);
|
|
+pgoff_t page_cache_prev_hole(struct address_space *mapping,
|
|
+ pgoff_t index, unsigned long max_scan);
|
|
+
|
|
+struct page *find_get_entry(struct address_space *mapping, pgoff_t offset);
|
|
+struct page *find_get_page(struct address_space *mapping, pgoff_t offset);
|
|
+struct page *find_lock_entry(struct address_space *mapping, pgoff_t offset);
|
|
+struct page *find_lock_page(struct address_space *mapping, pgoff_t offset);
|
|
+struct page *find_or_create_page(struct address_space *mapping, pgoff_t index,
|
|
+ gfp_t gfp_mask);
|
|
+unsigned find_get_entries(struct address_space *mapping, pgoff_t start,
|
|
+ unsigned int nr_entries, struct page **entries,
|
|
+ pgoff_t *indices);
|
|
unsigned find_get_pages(struct address_space *mapping, pgoff_t start,
|
|
unsigned int nr_pages, struct page **pages);
|
|
unsigned find_get_pages_contig(struct address_space *mapping, pgoff_t start,
|
|
@@ -270,8 +278,6 @@ static inline struct page *grab_cache_page(struct address_space *mapping,
|
|
|
|
extern struct page * grab_cache_page_nowait(struct address_space *mapping,
|
|
pgoff_t index);
|
|
-extern struct page * read_cache_page_async(struct address_space *mapping,
|
|
- pgoff_t index, filler_t *filler, void *data);
|
|
extern struct page * read_cache_page(struct address_space *mapping,
|
|
pgoff_t index, filler_t *filler, void *data);
|
|
extern struct page * read_cache_page_gfp(struct address_space *mapping,
|
|
@@ -279,14 +285,6 @@ extern struct page * read_cache_page_gfp(struct address_space *mapping,
|
|
extern int read_cache_pages(struct address_space *mapping,
|
|
struct list_head *pages, filler_t *filler, void *data);
|
|
|
|
-static inline struct page *read_mapping_page_async(
|
|
- struct address_space *mapping,
|
|
- pgoff_t index, void *data)
|
|
-{
|
|
- filler_t *filler = (filler_t *)mapping->a_ops->readpage;
|
|
- return read_cache_page_async(mapping, index, filler, data);
|
|
-}
|
|
-
|
|
static inline struct page *read_mapping_page(struct address_space *mapping,
|
|
pgoff_t index, void *data)
|
|
{
|
|
diff --git a/include/linux/pagevec.h b/include/linux/pagevec.h
|
|
index e4dbfab37729..b45d391b4540 100644
|
|
--- a/include/linux/pagevec.h
|
|
+++ b/include/linux/pagevec.h
|
|
@@ -22,6 +22,11 @@ struct pagevec {
|
|
|
|
void __pagevec_release(struct pagevec *pvec);
|
|
void __pagevec_lru_add(struct pagevec *pvec);
|
|
+unsigned pagevec_lookup_entries(struct pagevec *pvec,
|
|
+ struct address_space *mapping,
|
|
+ pgoff_t start, unsigned nr_entries,
|
|
+ pgoff_t *indices);
|
|
+void pagevec_remove_exceptionals(struct pagevec *pvec);
|
|
unsigned pagevec_lookup(struct pagevec *pvec, struct address_space *mapping,
|
|
pgoff_t start, unsigned nr_pages);
|
|
unsigned pagevec_lookup_tag(struct pagevec *pvec,
|
|
diff --git a/include/linux/power/charger-manager.h b/include/linux/power/charger-manager.h
|
|
index 07e7945a1ff2..e97fc656a058 100644
|
|
--- a/include/linux/power/charger-manager.h
|
|
+++ b/include/linux/power/charger-manager.h
|
|
@@ -253,9 +253,6 @@ struct charger_manager {
|
|
struct device *dev;
|
|
struct charger_desc *desc;
|
|
|
|
- struct power_supply *fuel_gauge;
|
|
- struct power_supply **charger_stat;
|
|
-
|
|
#ifdef CONFIG_THERMAL
|
|
struct thermal_zone_device *tzd_batt;
|
|
#endif
|
|
diff --git a/include/linux/radix-tree.h b/include/linux/radix-tree.h
|
|
index 403940787be1..e8be53ecfc45 100644
|
|
--- a/include/linux/radix-tree.h
|
|
+++ b/include/linux/radix-tree.h
|
|
@@ -219,6 +219,7 @@ static inline void radix_tree_replace_slot(void **pslot, void *item)
|
|
int radix_tree_insert(struct radix_tree_root *, unsigned long, void *);
|
|
void *radix_tree_lookup(struct radix_tree_root *, unsigned long);
|
|
void **radix_tree_lookup_slot(struct radix_tree_root *, unsigned long);
|
|
+void *radix_tree_delete_item(struct radix_tree_root *, unsigned long, void *);
|
|
void *radix_tree_delete(struct radix_tree_root *, unsigned long);
|
|
unsigned int
|
|
radix_tree_gang_lookup(struct radix_tree_root *root, void **results,
|
|
@@ -226,10 +227,6 @@ radix_tree_gang_lookup(struct radix_tree_root *root, void **results,
|
|
unsigned int radix_tree_gang_lookup_slot(struct radix_tree_root *root,
|
|
void ***results, unsigned long *indices,
|
|
unsigned long first_index, unsigned int max_items);
|
|
-unsigned long radix_tree_next_hole(struct radix_tree_root *root,
|
|
- unsigned long index, unsigned long max_scan);
|
|
-unsigned long radix_tree_prev_hole(struct radix_tree_root *root,
|
|
- unsigned long index, unsigned long max_scan);
|
|
int radix_tree_preload(gfp_t gfp_mask);
|
|
int radix_tree_maybe_preload(gfp_t gfp_mask);
|
|
void radix_tree_init(void);
|
|
diff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h
|
|
index 9d55438bc4ad..4d1771c2d29f 100644
|
|
--- a/include/linux/shmem_fs.h
|
|
+++ b/include/linux/shmem_fs.h
|
|
@@ -51,6 +51,7 @@ extern struct file *shmem_kernel_file_setup(const char *name, loff_t size,
|
|
unsigned long flags);
|
|
extern int shmem_zero_setup(struct vm_area_struct *);
|
|
extern int shmem_lock(struct file *file, int lock, struct user_struct *user);
|
|
+extern bool shmem_mapping(struct address_space *mapping);
|
|
extern void shmem_unlock_mapping(struct address_space *mapping);
|
|
extern struct page *shmem_read_mapping_page_gfp(struct address_space *mapping,
|
|
pgoff_t index, gfp_t gfp_mask);
|
|
diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h
|
|
index a3353f45ef94..ba41e01ebc54 100644
|
|
--- a/include/net/sctp/sctp.h
|
|
+++ b/include/net/sctp/sctp.h
|
|
@@ -433,6 +433,11 @@ static inline void sctp_assoc_pending_pmtu(struct sock *sk, struct sctp_associat
|
|
asoc->pmtu_pending = 0;
|
|
}
|
|
|
|
+static inline bool sctp_chunk_pending(const struct sctp_chunk *chunk)
|
|
+{
|
|
+ return !list_empty(&chunk->list);
|
|
+}
|
|
+
|
|
/* Walk through a list of TLV parameters. Don't trust the
|
|
* individual parameter lengths and instead depend on
|
|
* the chunk length to indicate when to stop. Make sure
|
|
diff --git a/include/net/sctp/sm.h b/include/net/sctp/sm.h
|
|
index 7f4eeb340a54..72a31db47ded 100644
|
|
--- a/include/net/sctp/sm.h
|
|
+++ b/include/net/sctp/sm.h
|
|
@@ -248,9 +248,9 @@ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *,
|
|
int, __be16);
|
|
struct sctp_chunk *sctp_make_asconf_set_prim(struct sctp_association *asoc,
|
|
union sctp_addr *addr);
|
|
-int sctp_verify_asconf(const struct sctp_association *asoc,
|
|
- struct sctp_paramhdr *param_hdr, void *chunk_end,
|
|
- struct sctp_paramhdr **errp);
|
|
+bool sctp_verify_asconf(const struct sctp_association *asoc,
|
|
+ struct sctp_chunk *chunk, bool addr_param_needed,
|
|
+ struct sctp_paramhdr **errp);
|
|
struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc,
|
|
struct sctp_chunk *asconf);
|
|
int sctp_process_asconf_ack(struct sctp_association *asoc,
|
|
diff --git a/include/trace/events/compaction.h b/include/trace/events/compaction.h
|
|
index 06f544ef2f6f..c6814b917bdf 100644
|
|
--- a/include/trace/events/compaction.h
|
|
+++ b/include/trace/events/compaction.h
|
|
@@ -5,6 +5,7 @@
|
|
#define _TRACE_COMPACTION_H
|
|
|
|
#include <linux/types.h>
|
|
+#include <linux/list.h>
|
|
#include <linux/tracepoint.h>
|
|
#include <trace/events/gfpflags.h>
|
|
|
|
@@ -47,10 +48,11 @@ DEFINE_EVENT(mm_compaction_isolate_template, mm_compaction_isolate_freepages,
|
|
|
|
TRACE_EVENT(mm_compaction_migratepages,
|
|
|
|
- TP_PROTO(unsigned long nr_migrated,
|
|
- unsigned long nr_failed),
|
|
+ TP_PROTO(unsigned long nr_all,
|
|
+ int migrate_rc,
|
|
+ struct list_head *migratepages),
|
|
|
|
- TP_ARGS(nr_migrated, nr_failed),
|
|
+ TP_ARGS(nr_all, migrate_rc, migratepages),
|
|
|
|
TP_STRUCT__entry(
|
|
__field(unsigned long, nr_migrated)
|
|
@@ -58,7 +60,22 @@ TRACE_EVENT(mm_compaction_migratepages,
|
|
),
|
|
|
|
TP_fast_assign(
|
|
- __entry->nr_migrated = nr_migrated;
|
|
+ unsigned long nr_failed = 0;
|
|
+ struct list_head *page_lru;
|
|
+
|
|
+ /*
|
|
+ * migrate_pages() returns either a non-negative number
|
|
+ * with the number of pages that failed migration, or an
|
|
+ * error code, in which case we need to count the remaining
|
|
+ * pages manually
|
|
+ */
|
|
+ if (migrate_rc >= 0)
|
|
+ nr_failed = migrate_rc;
|
|
+ else
|
|
+ list_for_each(page_lru, migratepages)
|
|
+ nr_failed++;
|
|
+
|
|
+ __entry->nr_migrated = nr_all - nr_failed;
|
|
__entry->nr_failed = nr_failed;
|
|
),
|
|
|
|
diff --git a/include/uapi/linux/netfilter/xt_bpf.h b/include/uapi/linux/netfilter/xt_bpf.h
|
|
index 5dda450eb55b..2ec9fbcd06f9 100644
|
|
--- a/include/uapi/linux/netfilter/xt_bpf.h
|
|
+++ b/include/uapi/linux/netfilter/xt_bpf.h
|
|
@@ -6,6 +6,8 @@
|
|
|
|
#define XT_BPF_MAX_NUM_INSTR 64
|
|
|
|
+struct sk_filter;
|
|
+
|
|
struct xt_bpf_info {
|
|
__u16 bpf_program_num_elem;
|
|
struct sock_filter bpf_program[XT_BPF_MAX_NUM_INSTR];
|
|
diff --git a/ipc/ipc_sysctl.c b/ipc/ipc_sysctl.c
|
|
index 17028648cfeb..cadddc8388f3 100644
|
|
--- a/ipc/ipc_sysctl.c
|
|
+++ b/ipc/ipc_sysctl.c
|
|
@@ -123,7 +123,6 @@ static int proc_ipcauto_dointvec_minmax(ctl_table *table, int write,
|
|
void __user *buffer, size_t *lenp, loff_t *ppos)
|
|
{
|
|
struct ctl_table ipc_table;
|
|
- size_t lenp_bef = *lenp;
|
|
int oldval;
|
|
int rc;
|
|
|
|
@@ -133,7 +132,7 @@ static int proc_ipcauto_dointvec_minmax(ctl_table *table, int write,
|
|
|
|
rc = proc_dointvec_minmax(&ipc_table, write, buffer, lenp, ppos);
|
|
|
|
- if (write && !rc && lenp_bef == *lenp) {
|
|
+ if (write && !rc) {
|
|
int newval = *((int *)(ipc_table.data));
|
|
/*
|
|
* The file "auto_msgmni" has correctly been set.
|
|
diff --git a/kernel/audit.c b/kernel/audit.c
|
|
index 2c0ecd1753de..b45b2daa9f92 100644
|
|
--- a/kernel/audit.c
|
|
+++ b/kernel/audit.c
|
|
@@ -687,7 +687,7 @@ static int audit_get_feature(struct sk_buff *skb)
|
|
|
|
seq = nlmsg_hdr(skb)->nlmsg_seq;
|
|
|
|
- audit_send_reply(skb, seq, AUDIT_GET, 0, 0, &af, sizeof(af));
|
|
+ audit_send_reply(skb, seq, AUDIT_GET_FEATURE, 0, 0, &af, sizeof(af));
|
|
|
|
return 0;
|
|
}
|
|
@@ -702,7 +702,7 @@ static void audit_log_feature_change(int which, u32 old_feature, u32 new_feature
|
|
|
|
ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_FEATURE_CHANGE);
|
|
audit_log_task_info(ab, current);
|
|
- audit_log_format(ab, "feature=%s old=%u new=%u old_lock=%u new_lock=%u res=%d",
|
|
+ audit_log_format(ab, " feature=%s old=%u new=%u old_lock=%u new_lock=%u res=%d",
|
|
audit_feature_names[which], !!old_feature, !!new_feature,
|
|
!!old_lock, !!new_lock, res);
|
|
audit_log_end(ab);
|
|
diff --git a/kernel/audit_tree.c b/kernel/audit_tree.c
|
|
index 135944a7b28a..a79db03990db 100644
|
|
--- a/kernel/audit_tree.c
|
|
+++ b/kernel/audit_tree.c
|
|
@@ -154,6 +154,7 @@ static struct audit_chunk *alloc_chunk(int count)
|
|
chunk->owners[i].index = i;
|
|
}
|
|
fsnotify_init_mark(&chunk->mark, audit_tree_destroy_watch);
|
|
+ chunk->mark.mask = FS_IN_IGNORED;
|
|
return chunk;
|
|
}
|
|
|
|
diff --git a/kernel/events/core.c b/kernel/events/core.c
|
|
index 4ced342f1ba9..4bbb27adf23d 100644
|
|
--- a/kernel/events/core.c
|
|
+++ b/kernel/events/core.c
|
|
@@ -39,6 +39,7 @@
|
|
#include <linux/hw_breakpoint.h>
|
|
#include <linux/mm_types.h>
|
|
#include <linux/cgroup.h>
|
|
+#include <linux/compat.h>
|
|
|
|
#include "internal.h"
|
|
|
|
@@ -3693,6 +3694,26 @@ static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|
return 0;
|
|
}
|
|
|
|
+#ifdef CONFIG_COMPAT
|
|
+static long perf_compat_ioctl(struct file *file, unsigned int cmd,
|
|
+ unsigned long arg)
|
|
+{
|
|
+ switch (_IOC_NR(cmd)) {
|
|
+ case _IOC_NR(PERF_EVENT_IOC_SET_FILTER):
|
|
+ case _IOC_NR(PERF_EVENT_IOC_ID):
|
|
+ /* Fix up pointer size (usually 4 -> 8 in 32-on-64-bit case */
|
|
+ if (_IOC_SIZE(cmd) == sizeof(compat_uptr_t)) {
|
|
+ cmd &= ~IOCSIZE_MASK;
|
|
+ cmd |= sizeof(void *) << IOCSIZE_SHIFT;
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ return perf_ioctl(file, cmd, arg);
|
|
+}
|
|
+#else
|
|
+# define perf_compat_ioctl NULL
|
|
+#endif
|
|
+
|
|
int perf_event_task_enable(void)
|
|
{
|
|
struct perf_event *event;
|
|
@@ -4185,7 +4206,7 @@ static const struct file_operations perf_fops = {
|
|
.read = perf_read,
|
|
.poll = perf_poll,
|
|
.unlocked_ioctl = perf_ioctl,
|
|
- .compat_ioctl = perf_ioctl,
|
|
+ .compat_ioctl = perf_compat_ioctl,
|
|
.mmap = perf_mmap,
|
|
.fasync = perf_fasync,
|
|
};
|
|
diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c
|
|
index b3d116cd072d..6705d947ef14 100644
|
|
--- a/kernel/rcu/tree.c
|
|
+++ b/kernel/rcu/tree.c
|
|
@@ -1228,6 +1228,22 @@ static int rcu_future_gp_cleanup(struct rcu_state *rsp, struct rcu_node *rnp)
|
|
}
|
|
|
|
/*
|
|
+ * Awaken the grace-period kthread for the specified flavor of RCU.
|
|
+ * Don't do a self-awaken, and don't bother awakening when there is
|
|
+ * nothing for the grace-period kthread to do (as in several CPUs
|
|
+ * raced to awaken, and we lost), and finally don't try to awaken
|
|
+ * a kthread that has not yet been created.
|
|
+ */
|
|
+static void rcu_gp_kthread_wake(struct rcu_state *rsp)
|
|
+{
|
|
+ if (current == rsp->gp_kthread ||
|
|
+ !ACCESS_ONCE(rsp->gp_flags) ||
|
|
+ !rsp->gp_kthread)
|
|
+ return;
|
|
+ wake_up(&rsp->gp_wq);
|
|
+}
|
|
+
|
|
+/*
|
|
* If there is room, assign a ->completed number to any callbacks on
|
|
* this CPU that have not already been assigned. Also accelerate any
|
|
* callbacks that were previously assigned a ->completed number that has
|
|
@@ -1670,7 +1686,7 @@ static void rsp_wakeup(struct irq_work *work)
|
|
struct rcu_state *rsp = container_of(work, struct rcu_state, wakeup_work);
|
|
|
|
/* Wake up rcu_gp_kthread() to start the grace period. */
|
|
- wake_up(&rsp->gp_wq);
|
|
+ rcu_gp_kthread_wake(rsp);
|
|
}
|
|
|
|
/*
|
|
@@ -1746,7 +1762,7 @@ static void rcu_report_qs_rsp(struct rcu_state *rsp, unsigned long flags)
|
|
{
|
|
WARN_ON_ONCE(!rcu_gp_in_progress(rsp));
|
|
raw_spin_unlock_irqrestore(&rcu_get_root(rsp)->lock, flags);
|
|
- wake_up(&rsp->gp_wq); /* Memory barrier implied by wake_up() path. */
|
|
+ rcu_gp_kthread_wake(rsp);
|
|
}
|
|
|
|
/*
|
|
@@ -2322,7 +2338,7 @@ static void force_quiescent_state(struct rcu_state *rsp)
|
|
}
|
|
rsp->gp_flags |= RCU_GP_FLAG_FQS;
|
|
raw_spin_unlock_irqrestore(&rnp_old->lock, flags);
|
|
- wake_up(&rsp->gp_wq); /* Memory barrier implied by wake_up() path. */
|
|
+ rcu_gp_kthread_wake(rsp);
|
|
}
|
|
|
|
/*
|
|
diff --git a/lib/radix-tree.c b/lib/radix-tree.c
|
|
index bd4a8dfdf0b8..7e30d2a7f346 100644
|
|
--- a/lib/radix-tree.c
|
|
+++ b/lib/radix-tree.c
|
|
@@ -946,81 +946,6 @@ next:
|
|
}
|
|
EXPORT_SYMBOL(radix_tree_range_tag_if_tagged);
|
|
|
|
-
|
|
-/**
|
|
- * radix_tree_next_hole - find the next hole (not-present entry)
|
|
- * @root: tree root
|
|
- * @index: index key
|
|
- * @max_scan: maximum range to search
|
|
- *
|
|
- * Search the set [index, min(index+max_scan-1, MAX_INDEX)] for the lowest
|
|
- * indexed hole.
|
|
- *
|
|
- * Returns: the index of the hole if found, otherwise returns an index
|
|
- * outside of the set specified (in which case 'return - index >= max_scan'
|
|
- * will be true). In rare cases of index wrap-around, 0 will be returned.
|
|
- *
|
|
- * radix_tree_next_hole may be called under rcu_read_lock. However, like
|
|
- * radix_tree_gang_lookup, this will not atomically search a snapshot of
|
|
- * the tree at a single point in time. For example, if a hole is created
|
|
- * at index 5, then subsequently a hole is created at index 10,
|
|
- * radix_tree_next_hole covering both indexes may return 10 if called
|
|
- * under rcu_read_lock.
|
|
- */
|
|
-unsigned long radix_tree_next_hole(struct radix_tree_root *root,
|
|
- unsigned long index, unsigned long max_scan)
|
|
-{
|
|
- unsigned long i;
|
|
-
|
|
- for (i = 0; i < max_scan; i++) {
|
|
- if (!radix_tree_lookup(root, index))
|
|
- break;
|
|
- index++;
|
|
- if (index == 0)
|
|
- break;
|
|
- }
|
|
-
|
|
- return index;
|
|
-}
|
|
-EXPORT_SYMBOL(radix_tree_next_hole);
|
|
-
|
|
-/**
|
|
- * radix_tree_prev_hole - find the prev hole (not-present entry)
|
|
- * @root: tree root
|
|
- * @index: index key
|
|
- * @max_scan: maximum range to search
|
|
- *
|
|
- * Search backwards in the range [max(index-max_scan+1, 0), index]
|
|
- * for the first hole.
|
|
- *
|
|
- * Returns: the index of the hole if found, otherwise returns an index
|
|
- * outside of the set specified (in which case 'index - return >= max_scan'
|
|
- * will be true). In rare cases of wrap-around, ULONG_MAX will be returned.
|
|
- *
|
|
- * radix_tree_next_hole may be called under rcu_read_lock. However, like
|
|
- * radix_tree_gang_lookup, this will not atomically search a snapshot of
|
|
- * the tree at a single point in time. For example, if a hole is created
|
|
- * at index 10, then subsequently a hole is created at index 5,
|
|
- * radix_tree_prev_hole covering both indexes may return 5 if called under
|
|
- * rcu_read_lock.
|
|
- */
|
|
-unsigned long radix_tree_prev_hole(struct radix_tree_root *root,
|
|
- unsigned long index, unsigned long max_scan)
|
|
-{
|
|
- unsigned long i;
|
|
-
|
|
- for (i = 0; i < max_scan; i++) {
|
|
- if (!radix_tree_lookup(root, index))
|
|
- break;
|
|
- index--;
|
|
- if (index == ULONG_MAX)
|
|
- break;
|
|
- }
|
|
-
|
|
- return index;
|
|
-}
|
|
-EXPORT_SYMBOL(radix_tree_prev_hole);
|
|
-
|
|
/**
|
|
* radix_tree_gang_lookup - perform multiple lookup on a radix tree
|
|
* @root: radix tree root
|
|
@@ -1337,15 +1262,18 @@ static inline void radix_tree_shrink(struct radix_tree_root *root)
|
|
}
|
|
|
|
/**
|
|
- * radix_tree_delete - delete an item from a radix tree
|
|
+ * radix_tree_delete_item - delete an item from a radix tree
|
|
* @root: radix tree root
|
|
* @index: index key
|
|
+ * @item: expected item
|
|
*
|
|
- * Remove the item at @index from the radix tree rooted at @root.
|
|
+ * Remove @item at @index from the radix tree rooted at @root.
|
|
*
|
|
- * Returns the address of the deleted item, or NULL if it was not present.
|
|
+ * Returns the address of the deleted item, or NULL if it was not present
|
|
+ * or the entry at the given @index was not @item.
|
|
*/
|
|
-void *radix_tree_delete(struct radix_tree_root *root, unsigned long index)
|
|
+void *radix_tree_delete_item(struct radix_tree_root *root,
|
|
+ unsigned long index, void *item)
|
|
{
|
|
struct radix_tree_node *node = NULL;
|
|
struct radix_tree_node *slot = NULL;
|
|
@@ -1380,6 +1308,11 @@ void *radix_tree_delete(struct radix_tree_root *root, unsigned long index)
|
|
if (slot == NULL)
|
|
goto out;
|
|
|
|
+ if (item && slot != item) {
|
|
+ slot = NULL;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
/*
|
|
* Clear all tags associated with the item to be deleted.
|
|
* This way of doing it would be inefficient, but seldom is any set.
|
|
@@ -1424,6 +1357,21 @@ void *radix_tree_delete(struct radix_tree_root *root, unsigned long index)
|
|
out:
|
|
return slot;
|
|
}
|
|
+EXPORT_SYMBOL(radix_tree_delete_item);
|
|
+
|
|
+/**
|
|
+ * radix_tree_delete - delete an item from a radix tree
|
|
+ * @root: radix tree root
|
|
+ * @index: index key
|
|
+ *
|
|
+ * Remove the item at @index from the radix tree rooted at @root.
|
|
+ *
|
|
+ * Returns the address of the deleted item, or NULL if it was not present.
|
|
+ */
|
|
+void *radix_tree_delete(struct radix_tree_root *root, unsigned long index)
|
|
+{
|
|
+ return radix_tree_delete_item(root, index, NULL);
|
|
+}
|
|
EXPORT_SYMBOL(radix_tree_delete);
|
|
|
|
/**
|
|
diff --git a/mm/compaction.c b/mm/compaction.c
|
|
index 5e38e5706f62..4229fc22a477 100644
|
|
--- a/mm/compaction.c
|
|
+++ b/mm/compaction.c
|
|
@@ -89,7 +89,8 @@ static void __reset_isolation_suitable(struct zone *zone)
|
|
unsigned long end_pfn = zone_end_pfn(zone);
|
|
unsigned long pfn;
|
|
|
|
- zone->compact_cached_migrate_pfn = start_pfn;
|
|
+ zone->compact_cached_migrate_pfn[0] = start_pfn;
|
|
+ zone->compact_cached_migrate_pfn[1] = start_pfn;
|
|
zone->compact_cached_free_pfn = end_pfn;
|
|
zone->compact_blockskip_flush = false;
|
|
|
|
@@ -131,9 +132,10 @@ void reset_isolation_suitable(pg_data_t *pgdat)
|
|
*/
|
|
static void update_pageblock_skip(struct compact_control *cc,
|
|
struct page *page, unsigned long nr_isolated,
|
|
- bool migrate_scanner)
|
|
+ bool set_unsuitable, bool migrate_scanner)
|
|
{
|
|
struct zone *zone = cc->zone;
|
|
+ unsigned long pfn;
|
|
|
|
if (cc->ignore_skip_hint)
|
|
return;
|
|
@@ -141,20 +143,32 @@ static void update_pageblock_skip(struct compact_control *cc,
|
|
if (!page)
|
|
return;
|
|
|
|
- if (!nr_isolated) {
|
|
- unsigned long pfn = page_to_pfn(page);
|
|
+ if (nr_isolated)
|
|
+ return;
|
|
+
|
|
+ /*
|
|
+ * Only skip pageblocks when all forms of compaction will be known to
|
|
+ * fail in the near future.
|
|
+ */
|
|
+ if (set_unsuitable)
|
|
set_pageblock_skip(page);
|
|
|
|
- /* Update where compaction should restart */
|
|
- if (migrate_scanner) {
|
|
- if (!cc->finished_update_migrate &&
|
|
- pfn > zone->compact_cached_migrate_pfn)
|
|
- zone->compact_cached_migrate_pfn = pfn;
|
|
- } else {
|
|
- if (!cc->finished_update_free &&
|
|
- pfn < zone->compact_cached_free_pfn)
|
|
- zone->compact_cached_free_pfn = pfn;
|
|
- }
|
|
+ pfn = page_to_pfn(page);
|
|
+
|
|
+ /* Update where async and sync compaction should restart */
|
|
+ if (migrate_scanner) {
|
|
+ if (cc->finished_update_migrate)
|
|
+ return;
|
|
+ if (pfn > zone->compact_cached_migrate_pfn[0])
|
|
+ zone->compact_cached_migrate_pfn[0] = pfn;
|
|
+ if (cc->mode != MIGRATE_ASYNC &&
|
|
+ pfn > zone->compact_cached_migrate_pfn[1])
|
|
+ zone->compact_cached_migrate_pfn[1] = pfn;
|
|
+ } else {
|
|
+ if (cc->finished_update_free)
|
|
+ return;
|
|
+ if (pfn < zone->compact_cached_free_pfn)
|
|
+ zone->compact_cached_free_pfn = pfn;
|
|
}
|
|
}
|
|
#else
|
|
@@ -166,7 +180,7 @@ static inline bool isolation_suitable(struct compact_control *cc,
|
|
|
|
static void update_pageblock_skip(struct compact_control *cc,
|
|
struct page *page, unsigned long nr_isolated,
|
|
- bool migrate_scanner)
|
|
+ bool set_unsuitable, bool migrate_scanner)
|
|
{
|
|
}
|
|
#endif /* CONFIG_COMPACTION */
|
|
@@ -195,7 +209,7 @@ static bool compact_checklock_irqsave(spinlock_t *lock, unsigned long *flags,
|
|
}
|
|
|
|
/* async aborts if taking too long or contended */
|
|
- if (!cc->sync) {
|
|
+ if (cc->mode == MIGRATE_ASYNC) {
|
|
cc->contended = true;
|
|
return false;
|
|
}
|
|
@@ -208,10 +222,28 @@ static bool compact_checklock_irqsave(spinlock_t *lock, unsigned long *flags,
|
|
return true;
|
|
}
|
|
|
|
-static inline bool compact_trylock_irqsave(spinlock_t *lock,
|
|
- unsigned long *flags, struct compact_control *cc)
|
|
+/*
|
|
+ * Aside from avoiding lock contention, compaction also periodically checks
|
|
+ * need_resched() and either schedules in sync compaction or aborts async
|
|
+ * compaction. This is similar to what compact_checklock_irqsave() does, but
|
|
+ * is used where no lock is concerned.
|
|
+ *
|
|
+ * Returns false when no scheduling was needed, or sync compaction scheduled.
|
|
+ * Returns true when async compaction should abort.
|
|
+ */
|
|
+static inline bool compact_should_abort(struct compact_control *cc)
|
|
{
|
|
- return compact_checklock_irqsave(lock, flags, false, cc);
|
|
+ /* async compaction aborts if contended */
|
|
+ if (need_resched()) {
|
|
+ if (cc->mode == MIGRATE_ASYNC) {
|
|
+ cc->contended = true;
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ cond_resched();
|
|
+ }
|
|
+
|
|
+ return false;
|
|
}
|
|
|
|
/* Returns true if the page is within a block suitable for migration to */
|
|
@@ -329,7 +361,8 @@ isolate_fail:
|
|
|
|
/* Update the pageblock-skip if the whole pageblock was scanned */
|
|
if (blockpfn == end_pfn)
|
|
- update_pageblock_skip(cc, valid_page, total_isolated, false);
|
|
+ update_pageblock_skip(cc, valid_page, total_isolated, true,
|
|
+ false);
|
|
|
|
count_compact_events(COMPACTFREE_SCANNED, nr_scanned);
|
|
if (total_isolated)
|
|
@@ -464,8 +497,9 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
|
|
unsigned long flags;
|
|
bool locked = false;
|
|
struct page *page = NULL, *valid_page = NULL;
|
|
- bool skipped_async_unsuitable = false;
|
|
- const isolate_mode_t mode = (!cc->sync ? ISOLATE_ASYNC_MIGRATE : 0) |
|
|
+ bool set_unsuitable = true;
|
|
+ const isolate_mode_t mode = (cc->mode == MIGRATE_ASYNC ?
|
|
+ ISOLATE_ASYNC_MIGRATE : 0) |
|
|
(unevictable ? ISOLATE_UNEVICTABLE : 0);
|
|
|
|
/*
|
|
@@ -475,7 +509,7 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
|
|
*/
|
|
while (unlikely(too_many_isolated(zone))) {
|
|
/* async migration should just abort */
|
|
- if (!cc->sync)
|
|
+ if (cc->mode == MIGRATE_ASYNC)
|
|
return 0;
|
|
|
|
congestion_wait(BLK_RW_ASYNC, HZ/10);
|
|
@@ -484,8 +518,10 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
|
|
return 0;
|
|
}
|
|
|
|
+ if (compact_should_abort(cc))
|
|
+ return 0;
|
|
+
|
|
/* Time to isolate some pages for migration */
|
|
- cond_resched();
|
|
for (; low_pfn < end_pfn; low_pfn++) {
|
|
/* give a chance to irqs before checking need_resched() */
|
|
if (locked && !(low_pfn % SWAP_CLUSTER_MAX)) {
|
|
@@ -540,9 +576,9 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
|
|
* the minimum amount of work satisfies the allocation
|
|
*/
|
|
mt = get_pageblock_migratetype(page);
|
|
- if (!cc->sync && !migrate_async_suitable(mt)) {
|
|
- cc->finished_update_migrate = true;
|
|
- skipped_async_unsuitable = true;
|
|
+ if (cc->mode == MIGRATE_ASYNC &&
|
|
+ !migrate_async_suitable(mt)) {
|
|
+ set_unsuitable = false;
|
|
goto next_pageblock;
|
|
}
|
|
}
|
|
@@ -646,11 +682,10 @@ next_pageblock:
|
|
/*
|
|
* Update the pageblock-skip information and cached scanner pfn,
|
|
* if the whole pageblock was scanned without isolating any page.
|
|
- * This is not done when pageblock was skipped due to being unsuitable
|
|
- * for async compaction, so that eventual sync compaction can try.
|
|
*/
|
|
- if (low_pfn == end_pfn && !skipped_async_unsuitable)
|
|
- update_pageblock_skip(cc, valid_page, nr_isolated, true);
|
|
+ if (low_pfn == end_pfn)
|
|
+ update_pageblock_skip(cc, valid_page, nr_isolated,
|
|
+ set_unsuitable, true);
|
|
|
|
trace_mm_compaction_isolate_migratepages(nr_scanned, nr_isolated);
|
|
|
|
@@ -671,7 +706,9 @@ static void isolate_freepages(struct zone *zone,
|
|
struct compact_control *cc)
|
|
{
|
|
struct page *page;
|
|
- unsigned long high_pfn, low_pfn, pfn, z_end_pfn;
|
|
+ unsigned long block_start_pfn; /* start of current pageblock */
|
|
+ unsigned long block_end_pfn; /* end of current pageblock */
|
|
+ unsigned long low_pfn; /* lowest pfn scanner is able to scan */
|
|
int nr_freepages = cc->nr_freepages;
|
|
struct list_head *freelist = &cc->freepages;
|
|
|
|
@@ -679,41 +716,38 @@ static void isolate_freepages(struct zone *zone,
|
|
* Initialise the free scanner. The starting point is where we last
|
|
* successfully isolated from, zone-cached value, or the end of the
|
|
* zone when isolating for the first time. We need this aligned to
|
|
- * the pageblock boundary, because we do pfn -= pageblock_nr_pages
|
|
- * in the for loop.
|
|
+ * the pageblock boundary, because we do
|
|
+ * block_start_pfn -= pageblock_nr_pages in the for loop.
|
|
+ * For ending point, take care when isolating in last pageblock of a
|
|
+ * a zone which ends in the middle of a pageblock.
|
|
* The low boundary is the end of the pageblock the migration scanner
|
|
* is using.
|
|
*/
|
|
- pfn = cc->free_pfn & ~(pageblock_nr_pages-1);
|
|
+ block_start_pfn = cc->free_pfn & ~(pageblock_nr_pages-1);
|
|
+ block_end_pfn = min(block_start_pfn + pageblock_nr_pages,
|
|
+ zone_end_pfn(zone));
|
|
low_pfn = ALIGN(cc->migrate_pfn + 1, pageblock_nr_pages);
|
|
|
|
/*
|
|
- * Take care that if the migration scanner is at the end of the zone
|
|
- * that the free scanner does not accidentally move to the next zone
|
|
- * in the next isolation cycle.
|
|
- */
|
|
- high_pfn = min(low_pfn, pfn);
|
|
-
|
|
- z_end_pfn = zone_end_pfn(zone);
|
|
-
|
|
- /*
|
|
* Isolate free pages until enough are available to migrate the
|
|
* pages on cc->migratepages. We stop searching if the migrate
|
|
* and free page scanners meet or enough free pages are isolated.
|
|
*/
|
|
- for (; pfn >= low_pfn && cc->nr_migratepages > nr_freepages;
|
|
- pfn -= pageblock_nr_pages) {
|
|
+ for (; block_start_pfn >= low_pfn && cc->nr_migratepages > nr_freepages;
|
|
+ block_end_pfn = block_start_pfn,
|
|
+ block_start_pfn -= pageblock_nr_pages) {
|
|
unsigned long isolated;
|
|
- unsigned long end_pfn;
|
|
|
|
/*
|
|
* This can iterate a massively long zone without finding any
|
|
* suitable migration targets, so periodically check if we need
|
|
- * to schedule.
|
|
+ * to schedule, or even abort async compaction.
|
|
*/
|
|
- cond_resched();
|
|
+ if (!(block_start_pfn % (SWAP_CLUSTER_MAX * pageblock_nr_pages))
|
|
+ && compact_should_abort(cc))
|
|
+ break;
|
|
|
|
- if (!pfn_valid(pfn))
|
|
+ if (!pfn_valid(block_start_pfn))
|
|
continue;
|
|
|
|
/*
|
|
@@ -723,7 +757,7 @@ static void isolate_freepages(struct zone *zone,
|
|
* i.e. it's possible that all pages within a zones range of
|
|
* pages do not belong to a single zone.
|
|
*/
|
|
- page = pfn_to_page(pfn);
|
|
+ page = pfn_to_page(block_start_pfn);
|
|
if (page_zone(page) != zone)
|
|
continue;
|
|
|
|
@@ -736,26 +770,26 @@ static void isolate_freepages(struct zone *zone,
|
|
continue;
|
|
|
|
/* Found a block suitable for isolating free pages from */
|
|
- isolated = 0;
|
|
+ cc->free_pfn = block_start_pfn;
|
|
+ isolated = isolate_freepages_block(cc, block_start_pfn,
|
|
+ block_end_pfn, freelist, false);
|
|
+ nr_freepages += isolated;
|
|
|
|
/*
|
|
- * Take care when isolating in last pageblock of a zone which
|
|
- * ends in the middle of a pageblock.
|
|
+ * Set a flag that we successfully isolated in this pageblock.
|
|
+ * In the next loop iteration, zone->compact_cached_free_pfn
|
|
+ * will not be updated and thus it will effectively contain the
|
|
+ * highest pageblock we isolated pages from.
|
|
*/
|
|
- end_pfn = min(pfn + pageblock_nr_pages, z_end_pfn);
|
|
- isolated = isolate_freepages_block(cc, pfn, end_pfn,
|
|
- freelist, false);
|
|
- nr_freepages += isolated;
|
|
+ if (isolated)
|
|
+ cc->finished_update_free = true;
|
|
|
|
/*
|
|
- * Record the highest PFN we isolated pages from. When next
|
|
- * looking for free pages, the search will restart here as
|
|
- * page migration may have returned some pages to the allocator
|
|
+ * isolate_freepages_block() might have aborted due to async
|
|
+ * compaction being contended
|
|
*/
|
|
- if (isolated) {
|
|
- cc->finished_update_free = true;
|
|
- high_pfn = max(high_pfn, pfn);
|
|
- }
|
|
+ if (cc->contended)
|
|
+ break;
|
|
}
|
|
|
|
/* split_free_page does not map the pages */
|
|
@@ -765,10 +799,9 @@ static void isolate_freepages(struct zone *zone,
|
|
* If we crossed the migrate scanner, we want to keep it that way
|
|
* so that compact_finished() may detect this
|
|
*/
|
|
- if (pfn < low_pfn)
|
|
- cc->free_pfn = max(pfn, zone->zone_start_pfn);
|
|
- else
|
|
- cc->free_pfn = high_pfn;
|
|
+ if (block_start_pfn < low_pfn)
|
|
+ cc->free_pfn = cc->migrate_pfn;
|
|
+
|
|
cc->nr_freepages = nr_freepages;
|
|
}
|
|
|
|
@@ -783,9 +816,13 @@ static struct page *compaction_alloc(struct page *migratepage,
|
|
struct compact_control *cc = (struct compact_control *)data;
|
|
struct page *freepage;
|
|
|
|
- /* Isolate free pages if necessary */
|
|
+ /*
|
|
+ * Isolate free pages if necessary, and if we are not aborting due to
|
|
+ * contention.
|
|
+ */
|
|
if (list_empty(&cc->freepages)) {
|
|
- isolate_freepages(cc->zone, cc);
|
|
+ if (!cc->contended)
|
|
+ isolate_freepages(cc->zone, cc);
|
|
|
|
if (list_empty(&cc->freepages))
|
|
return NULL;
|
|
@@ -799,23 +836,16 @@ static struct page *compaction_alloc(struct page *migratepage,
|
|
}
|
|
|
|
/*
|
|
- * We cannot control nr_migratepages and nr_freepages fully when migration is
|
|
- * running as migrate_pages() has no knowledge of compact_control. When
|
|
- * migration is complete, we count the number of pages on the lists by hand.
|
|
+ * This is a migrate-callback that "frees" freepages back to the isolated
|
|
+ * freelist. All pages on the freelist are from the same zone, so there is no
|
|
+ * special handling needed for NUMA.
|
|
*/
|
|
-static void update_nr_listpages(struct compact_control *cc)
|
|
+static void compaction_free(struct page *page, unsigned long data)
|
|
{
|
|
- int nr_migratepages = 0;
|
|
- int nr_freepages = 0;
|
|
- struct page *page;
|
|
-
|
|
- list_for_each_entry(page, &cc->migratepages, lru)
|
|
- nr_migratepages++;
|
|
- list_for_each_entry(page, &cc->freepages, lru)
|
|
- nr_freepages++;
|
|
+ struct compact_control *cc = (struct compact_control *)data;
|
|
|
|
- cc->nr_migratepages = nr_migratepages;
|
|
- cc->nr_freepages = nr_freepages;
|
|
+ list_add(&page->lru, &cc->freepages);
|
|
+ cc->nr_freepages++;
|
|
}
|
|
|
|
/* possible outcome of isolate_migratepages */
|
|
@@ -862,13 +892,14 @@ static int compact_finished(struct zone *zone,
|
|
unsigned int order;
|
|
unsigned long watermark;
|
|
|
|
- if (fatal_signal_pending(current))
|
|
+ if (cc->contended || fatal_signal_pending(current))
|
|
return COMPACT_PARTIAL;
|
|
|
|
/* Compaction run completes if the migrate and free scanner meet */
|
|
if (cc->free_pfn <= cc->migrate_pfn) {
|
|
/* Let the next compaction start anew. */
|
|
- zone->compact_cached_migrate_pfn = zone->zone_start_pfn;
|
|
+ zone->compact_cached_migrate_pfn[0] = zone->zone_start_pfn;
|
|
+ zone->compact_cached_migrate_pfn[1] = zone->zone_start_pfn;
|
|
zone->compact_cached_free_pfn = zone_end_pfn(zone);
|
|
|
|
/*
|
|
@@ -968,6 +999,7 @@ static int compact_zone(struct zone *zone, struct compact_control *cc)
|
|
int ret;
|
|
unsigned long start_pfn = zone->zone_start_pfn;
|
|
unsigned long end_pfn = zone_end_pfn(zone);
|
|
+ const bool sync = cc->mode != MIGRATE_ASYNC;
|
|
|
|
ret = compaction_suitable(zone, cc->order);
|
|
switch (ret) {
|
|
@@ -993,7 +1025,7 @@ static int compact_zone(struct zone *zone, struct compact_control *cc)
|
|
* information on where the scanners should start but check that it
|
|
* is initialised by ensuring the values are within zone boundaries.
|
|
*/
|
|
- cc->migrate_pfn = zone->compact_cached_migrate_pfn;
|
|
+ cc->migrate_pfn = zone->compact_cached_migrate_pfn[sync];
|
|
cc->free_pfn = zone->compact_cached_free_pfn;
|
|
if (cc->free_pfn < start_pfn || cc->free_pfn > end_pfn) {
|
|
cc->free_pfn = end_pfn & ~(pageblock_nr_pages-1);
|
|
@@ -1001,7 +1033,8 @@ static int compact_zone(struct zone *zone, struct compact_control *cc)
|
|
}
|
|
if (cc->migrate_pfn < start_pfn || cc->migrate_pfn > end_pfn) {
|
|
cc->migrate_pfn = start_pfn;
|
|
- zone->compact_cached_migrate_pfn = cc->migrate_pfn;
|
|
+ zone->compact_cached_migrate_pfn[0] = cc->migrate_pfn;
|
|
+ zone->compact_cached_migrate_pfn[1] = cc->migrate_pfn;
|
|
}
|
|
|
|
trace_mm_compaction_begin(start_pfn, cc->migrate_pfn, cc->free_pfn, end_pfn);
|
|
@@ -1009,7 +1042,6 @@ static int compact_zone(struct zone *zone, struct compact_control *cc)
|
|
migrate_prep_local();
|
|
|
|
while ((ret = compact_finished(zone, cc)) == COMPACT_CONTINUE) {
|
|
- unsigned long nr_migrate, nr_remaining;
|
|
int err;
|
|
|
|
switch (isolate_migratepages(zone, cc)) {
|
|
@@ -1024,21 +1056,20 @@ static int compact_zone(struct zone *zone, struct compact_control *cc)
|
|
;
|
|
}
|
|
|
|
- nr_migrate = cc->nr_migratepages;
|
|
+ if (!cc->nr_migratepages)
|
|
+ continue;
|
|
+
|
|
err = migrate_pages(&cc->migratepages, compaction_alloc,
|
|
- (unsigned long)cc,
|
|
- cc->sync ? MIGRATE_SYNC_LIGHT : MIGRATE_ASYNC,
|
|
+ compaction_free, (unsigned long)cc, cc->mode,
|
|
MR_COMPACTION);
|
|
- update_nr_listpages(cc);
|
|
- nr_remaining = cc->nr_migratepages;
|
|
|
|
- trace_mm_compaction_migratepages(nr_migrate - nr_remaining,
|
|
- nr_remaining);
|
|
+ trace_mm_compaction_migratepages(cc->nr_migratepages, err,
|
|
+ &cc->migratepages);
|
|
|
|
- /* Release isolated pages not migrated */
|
|
+ /* All pages were either migrated or will be released */
|
|
+ cc->nr_migratepages = 0;
|
|
if (err) {
|
|
putback_movable_pages(&cc->migratepages);
|
|
- cc->nr_migratepages = 0;
|
|
/*
|
|
* migrate_pages() may return -ENOMEM when scanners meet
|
|
* and we want compact_finished() to detect it
|
|
@@ -1060,9 +1091,8 @@ out:
|
|
return ret;
|
|
}
|
|
|
|
-static unsigned long compact_zone_order(struct zone *zone,
|
|
- int order, gfp_t gfp_mask,
|
|
- bool sync, bool *contended)
|
|
+static unsigned long compact_zone_order(struct zone *zone, int order,
|
|
+ gfp_t gfp_mask, enum migrate_mode mode, bool *contended)
|
|
{
|
|
unsigned long ret;
|
|
struct compact_control cc = {
|
|
@@ -1071,7 +1101,7 @@ static unsigned long compact_zone_order(struct zone *zone,
|
|
.order = order,
|
|
.migratetype = allocflags_to_migratetype(gfp_mask),
|
|
.zone = zone,
|
|
- .sync = sync,
|
|
+ .mode = mode,
|
|
};
|
|
INIT_LIST_HEAD(&cc.freepages);
|
|
INIT_LIST_HEAD(&cc.migratepages);
|
|
@@ -1093,7 +1123,7 @@ int sysctl_extfrag_threshold = 500;
|
|
* @order: The order of the current allocation
|
|
* @gfp_mask: The GFP mask of the current allocation
|
|
* @nodemask: The allowed nodes to allocate from
|
|
- * @sync: Whether migration is synchronous or not
|
|
+ * @mode: The migration mode for async, sync light, or sync migration
|
|
* @contended: Return value that is true if compaction was aborted due to lock contention
|
|
* @page: Optionally capture a free page of the requested order during compaction
|
|
*
|
|
@@ -1101,7 +1131,7 @@ int sysctl_extfrag_threshold = 500;
|
|
*/
|
|
unsigned long try_to_compact_pages(struct zonelist *zonelist,
|
|
int order, gfp_t gfp_mask, nodemask_t *nodemask,
|
|
- bool sync, bool *contended)
|
|
+ enum migrate_mode mode, bool *contended)
|
|
{
|
|
enum zone_type high_zoneidx = gfp_zone(gfp_mask);
|
|
int may_enter_fs = gfp_mask & __GFP_FS;
|
|
@@ -1126,7 +1156,7 @@ unsigned long try_to_compact_pages(struct zonelist *zonelist,
|
|
nodemask) {
|
|
int status;
|
|
|
|
- status = compact_zone_order(zone, order, gfp_mask, sync,
|
|
+ status = compact_zone_order(zone, order, gfp_mask, mode,
|
|
contended);
|
|
rc = max(status, rc);
|
|
|
|
@@ -1165,9 +1195,6 @@ static void __compact_pgdat(pg_data_t *pgdat, struct compact_control *cc)
|
|
if (zone_watermark_ok(zone, cc->order,
|
|
low_wmark_pages(zone), 0, 0))
|
|
compaction_defer_reset(zone, cc->order, false);
|
|
- /* Currently async compaction is never deferred. */
|
|
- else if (cc->sync)
|
|
- defer_compaction(zone, cc->order);
|
|
}
|
|
|
|
VM_BUG_ON(!list_empty(&cc->freepages));
|
|
@@ -1179,7 +1206,7 @@ void compact_pgdat(pg_data_t *pgdat, int order)
|
|
{
|
|
struct compact_control cc = {
|
|
.order = order,
|
|
- .sync = false,
|
|
+ .mode = MIGRATE_ASYNC,
|
|
};
|
|
|
|
if (!order)
|
|
@@ -1192,7 +1219,7 @@ static void compact_node(int nid)
|
|
{
|
|
struct compact_control cc = {
|
|
.order = -1,
|
|
- .sync = true,
|
|
+ .mode = MIGRATE_SYNC,
|
|
.ignore_skip_hint = true,
|
|
};
|
|
|
|
diff --git a/mm/filemap.c b/mm/filemap.c
|
|
index c2cc7c95eff1..bdaa21555abe 100644
|
|
--- a/mm/filemap.c
|
|
+++ b/mm/filemap.c
|
|
@@ -448,6 +448,29 @@ int replace_page_cache_page(struct page *old, struct page *new, gfp_t gfp_mask)
|
|
}
|
|
EXPORT_SYMBOL_GPL(replace_page_cache_page);
|
|
|
|
+static int page_cache_tree_insert(struct address_space *mapping,
|
|
+ struct page *page)
|
|
+{
|
|
+ void **slot;
|
|
+ int error;
|
|
+
|
|
+ slot = radix_tree_lookup_slot(&mapping->page_tree, page->index);
|
|
+ if (slot) {
|
|
+ void *p;
|
|
+
|
|
+ p = radix_tree_deref_slot_protected(slot, &mapping->tree_lock);
|
|
+ if (!radix_tree_exceptional_entry(p))
|
|
+ return -EEXIST;
|
|
+ radix_tree_replace_slot(slot, page);
|
|
+ mapping->nrpages++;
|
|
+ return 0;
|
|
+ }
|
|
+ error = radix_tree_insert(&mapping->page_tree, page->index, page);
|
|
+ if (!error)
|
|
+ mapping->nrpages++;
|
|
+ return error;
|
|
+}
|
|
+
|
|
/**
|
|
* add_to_page_cache_locked - add a locked page to the pagecache
|
|
* @page: page to add
|
|
@@ -482,11 +505,10 @@ int add_to_page_cache_locked(struct page *page, struct address_space *mapping,
|
|
page->index = offset;
|
|
|
|
spin_lock_irq(&mapping->tree_lock);
|
|
- error = radix_tree_insert(&mapping->page_tree, offset, page);
|
|
+ error = page_cache_tree_insert(mapping, page);
|
|
radix_tree_preload_end();
|
|
if (unlikely(error))
|
|
goto err_insert;
|
|
- mapping->nrpages++;
|
|
__inc_zone_page_state(page, NR_FILE_PAGES);
|
|
spin_unlock_irq(&mapping->tree_lock);
|
|
trace_mm_filemap_add_to_page_cache(page);
|
|
@@ -688,14 +710,101 @@ int __lock_page_or_retry(struct page *page, struct mm_struct *mm,
|
|
}
|
|
|
|
/**
|
|
- * find_get_page - find and get a page reference
|
|
+ * page_cache_next_hole - find the next hole (not-present entry)
|
|
+ * @mapping: mapping
|
|
+ * @index: index
|
|
+ * @max_scan: maximum range to search
|
|
+ *
|
|
+ * Search the set [index, min(index+max_scan-1, MAX_INDEX)] for the
|
|
+ * lowest indexed hole.
|
|
+ *
|
|
+ * Returns: the index of the hole if found, otherwise returns an index
|
|
+ * outside of the set specified (in which case 'return - index >=
|
|
+ * max_scan' will be true). In rare cases of index wrap-around, 0 will
|
|
+ * be returned.
|
|
+ *
|
|
+ * page_cache_next_hole may be called under rcu_read_lock. However,
|
|
+ * like radix_tree_gang_lookup, this will not atomically search a
|
|
+ * snapshot of the tree at a single point in time. For example, if a
|
|
+ * hole is created at index 5, then subsequently a hole is created at
|
|
+ * index 10, page_cache_next_hole covering both indexes may return 10
|
|
+ * if called under rcu_read_lock.
|
|
+ */
|
|
+pgoff_t page_cache_next_hole(struct address_space *mapping,
|
|
+ pgoff_t index, unsigned long max_scan)
|
|
+{
|
|
+ unsigned long i;
|
|
+
|
|
+ for (i = 0; i < max_scan; i++) {
|
|
+ struct page *page;
|
|
+
|
|
+ page = radix_tree_lookup(&mapping->page_tree, index);
|
|
+ if (!page || radix_tree_exceptional_entry(page))
|
|
+ break;
|
|
+ index++;
|
|
+ if (index == 0)
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return index;
|
|
+}
|
|
+EXPORT_SYMBOL(page_cache_next_hole);
|
|
+
|
|
+/**
|
|
+ * page_cache_prev_hole - find the prev hole (not-present entry)
|
|
+ * @mapping: mapping
|
|
+ * @index: index
|
|
+ * @max_scan: maximum range to search
|
|
+ *
|
|
+ * Search backwards in the range [max(index-max_scan+1, 0), index] for
|
|
+ * the first hole.
|
|
+ *
|
|
+ * Returns: the index of the hole if found, otherwise returns an index
|
|
+ * outside of the set specified (in which case 'index - return >=
|
|
+ * max_scan' will be true). In rare cases of wrap-around, ULONG_MAX
|
|
+ * will be returned.
|
|
+ *
|
|
+ * page_cache_prev_hole may be called under rcu_read_lock. However,
|
|
+ * like radix_tree_gang_lookup, this will not atomically search a
|
|
+ * snapshot of the tree at a single point in time. For example, if a
|
|
+ * hole is created at index 10, then subsequently a hole is created at
|
|
+ * index 5, page_cache_prev_hole covering both indexes may return 5 if
|
|
+ * called under rcu_read_lock.
|
|
+ */
|
|
+pgoff_t page_cache_prev_hole(struct address_space *mapping,
|
|
+ pgoff_t index, unsigned long max_scan)
|
|
+{
|
|
+ unsigned long i;
|
|
+
|
|
+ for (i = 0; i < max_scan; i++) {
|
|
+ struct page *page;
|
|
+
|
|
+ page = radix_tree_lookup(&mapping->page_tree, index);
|
|
+ if (!page || radix_tree_exceptional_entry(page))
|
|
+ break;
|
|
+ index--;
|
|
+ if (index == ULONG_MAX)
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return index;
|
|
+}
|
|
+EXPORT_SYMBOL(page_cache_prev_hole);
|
|
+
|
|
+/**
|
|
+ * find_get_entry - find and get a page cache entry
|
|
* @mapping: the address_space to search
|
|
- * @offset: the page index
|
|
+ * @offset: the page cache index
|
|
*
|
|
- * Is there a pagecache struct page at the given (mapping, offset) tuple?
|
|
- * If yes, increment its refcount and return it; if no, return NULL.
|
|
+ * Looks up the page cache slot at @mapping & @offset. If there is a
|
|
+ * page cache page, it is returned with an increased refcount.
|
|
+ *
|
|
+ * If the slot holds a shadow entry of a previously evicted page, it
|
|
+ * is returned.
|
|
+ *
|
|
+ * Otherwise, %NULL is returned.
|
|
*/
|
|
-struct page *find_get_page(struct address_space *mapping, pgoff_t offset)
|
|
+struct page *find_get_entry(struct address_space *mapping, pgoff_t offset)
|
|
{
|
|
void **pagep;
|
|
struct page *page;
|
|
@@ -736,24 +845,50 @@ out:
|
|
|
|
return page;
|
|
}
|
|
-EXPORT_SYMBOL(find_get_page);
|
|
+EXPORT_SYMBOL(find_get_entry);
|
|
|
|
/**
|
|
- * find_lock_page - locate, pin and lock a pagecache page
|
|
+ * find_get_page - find and get a page reference
|
|
* @mapping: the address_space to search
|
|
* @offset: the page index
|
|
*
|
|
- * Locates the desired pagecache page, locks it, increments its reference
|
|
- * count and returns its address.
|
|
+ * Looks up the page cache slot at @mapping & @offset. If there is a
|
|
+ * page cache page, it is returned with an increased refcount.
|
|
*
|
|
- * Returns zero if the page was not present. find_lock_page() may sleep.
|
|
+ * Otherwise, %NULL is returned.
|
|
*/
|
|
-struct page *find_lock_page(struct address_space *mapping, pgoff_t offset)
|
|
+struct page *find_get_page(struct address_space *mapping, pgoff_t offset)
|
|
+{
|
|
+ struct page *page = find_get_entry(mapping, offset);
|
|
+
|
|
+ if (radix_tree_exceptional_entry(page))
|
|
+ page = NULL;
|
|
+ return page;
|
|
+}
|
|
+EXPORT_SYMBOL(find_get_page);
|
|
+
|
|
+/**
|
|
+ * find_lock_entry - locate, pin and lock a page cache entry
|
|
+ * @mapping: the address_space to search
|
|
+ * @offset: the page cache index
|
|
+ *
|
|
+ * Looks up the page cache slot at @mapping & @offset. If there is a
|
|
+ * page cache page, it is returned locked and with an increased
|
|
+ * refcount.
|
|
+ *
|
|
+ * If the slot holds a shadow entry of a previously evicted page, it
|
|
+ * is returned.
|
|
+ *
|
|
+ * Otherwise, %NULL is returned.
|
|
+ *
|
|
+ * find_lock_entry() may sleep.
|
|
+ */
|
|
+struct page *find_lock_entry(struct address_space *mapping, pgoff_t offset)
|
|
{
|
|
struct page *page;
|
|
|
|
repeat:
|
|
- page = find_get_page(mapping, offset);
|
|
+ page = find_get_entry(mapping, offset);
|
|
if (page && !radix_tree_exception(page)) {
|
|
lock_page(page);
|
|
/* Has the page been truncated? */
|
|
@@ -766,6 +901,29 @@ repeat:
|
|
}
|
|
return page;
|
|
}
|
|
+EXPORT_SYMBOL(find_lock_entry);
|
|
+
|
|
+/**
|
|
+ * find_lock_page - locate, pin and lock a pagecache page
|
|
+ * @mapping: the address_space to search
|
|
+ * @offset: the page index
|
|
+ *
|
|
+ * Looks up the page cache slot at @mapping & @offset. If there is a
|
|
+ * page cache page, it is returned locked and with an increased
|
|
+ * refcount.
|
|
+ *
|
|
+ * Otherwise, %NULL is returned.
|
|
+ *
|
|
+ * find_lock_page() may sleep.
|
|
+ */
|
|
+struct page *find_lock_page(struct address_space *mapping, pgoff_t offset)
|
|
+{
|
|
+ struct page *page = find_lock_entry(mapping, offset);
|
|
+
|
|
+ if (radix_tree_exceptional_entry(page))
|
|
+ page = NULL;
|
|
+ return page;
|
|
+}
|
|
EXPORT_SYMBOL(find_lock_page);
|
|
|
|
/**
|
|
@@ -774,16 +932,18 @@ EXPORT_SYMBOL(find_lock_page);
|
|
* @index: the page's index into the mapping
|
|
* @gfp_mask: page allocation mode
|
|
*
|
|
- * Locates a page in the pagecache. If the page is not present, a new page
|
|
- * is allocated using @gfp_mask and is added to the pagecache and to the VM's
|
|
- * LRU list. The returned page is locked and has its reference count
|
|
- * incremented.
|
|
+ * Looks up the page cache slot at @mapping & @offset. If there is a
|
|
+ * page cache page, it is returned locked and with an increased
|
|
+ * refcount.
|
|
+ *
|
|
+ * If the page is not present, a new page is allocated using @gfp_mask
|
|
+ * and added to the page cache and the VM's LRU list. The page is
|
|
+ * returned locked and with an increased refcount.
|
|
*
|
|
- * find_or_create_page() may sleep, even if @gfp_flags specifies an atomic
|
|
- * allocation!
|
|
+ * On memory exhaustion, %NULL is returned.
|
|
*
|
|
- * find_or_create_page() returns the desired page's address, or zero on
|
|
- * memory exhaustion.
|
|
+ * find_or_create_page() may sleep, even if @gfp_flags specifies an
|
|
+ * atomic allocation!
|
|
*/
|
|
struct page *find_or_create_page(struct address_space *mapping,
|
|
pgoff_t index, gfp_t gfp_mask)
|
|
@@ -816,6 +976,76 @@ repeat:
|
|
EXPORT_SYMBOL(find_or_create_page);
|
|
|
|
/**
|
|
+ * find_get_entries - gang pagecache lookup
|
|
+ * @mapping: The address_space to search
|
|
+ * @start: The starting page cache index
|
|
+ * @nr_entries: The maximum number of entries
|
|
+ * @entries: Where the resulting entries are placed
|
|
+ * @indices: The cache indices corresponding to the entries in @entries
|
|
+ *
|
|
+ * find_get_entries() will search for and return a group of up to
|
|
+ * @nr_entries entries in the mapping. The entries are placed at
|
|
+ * @entries. find_get_entries() takes a reference against any actual
|
|
+ * pages it returns.
|
|
+ *
|
|
+ * The search returns a group of mapping-contiguous page cache entries
|
|
+ * with ascending indexes. There may be holes in the indices due to
|
|
+ * not-present pages.
|
|
+ *
|
|
+ * Any shadow entries of evicted pages are included in the returned
|
|
+ * array.
|
|
+ *
|
|
+ * find_get_entries() returns the number of pages and shadow entries
|
|
+ * which were found.
|
|
+ */
|
|
+unsigned find_get_entries(struct address_space *mapping,
|
|
+ pgoff_t start, unsigned int nr_entries,
|
|
+ struct page **entries, pgoff_t *indices)
|
|
+{
|
|
+ void **slot;
|
|
+ unsigned int ret = 0;
|
|
+ struct radix_tree_iter iter;
|
|
+
|
|
+ if (!nr_entries)
|
|
+ return 0;
|
|
+
|
|
+ rcu_read_lock();
|
|
+restart:
|
|
+ radix_tree_for_each_slot(slot, &mapping->page_tree, &iter, start) {
|
|
+ struct page *page;
|
|
+repeat:
|
|
+ page = radix_tree_deref_slot(slot);
|
|
+ if (unlikely(!page))
|
|
+ continue;
|
|
+ if (radix_tree_exception(page)) {
|
|
+ if (radix_tree_deref_retry(page))
|
|
+ goto restart;
|
|
+ /*
|
|
+ * Otherwise, we must be storing a swap entry
|
|
+ * here as an exceptional entry: so return it
|
|
+ * without attempting to raise page count.
|
|
+ */
|
|
+ goto export;
|
|
+ }
|
|
+ if (!page_cache_get_speculative(page))
|
|
+ goto repeat;
|
|
+
|
|
+ /* Has the page moved? */
|
|
+ if (unlikely(page != *slot)) {
|
|
+ page_cache_release(page);
|
|
+ goto repeat;
|
|
+ }
|
|
+export:
|
|
+ indices[ret] = iter.index;
|
|
+ entries[ret] = page;
|
|
+ if (++ret == nr_entries)
|
|
+ break;
|
|
+ }
|
|
+ rcu_read_unlock();
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/**
|
|
* find_get_pages - gang pagecache lookup
|
|
* @mapping: The address_space to search
|
|
* @start: The starting page index
|
|
@@ -1797,6 +2027,18 @@ int generic_file_readonly_mmap(struct file * file, struct vm_area_struct * vma)
|
|
EXPORT_SYMBOL(generic_file_mmap);
|
|
EXPORT_SYMBOL(generic_file_readonly_mmap);
|
|
|
|
+static struct page *wait_on_page_read(struct page *page)
|
|
+{
|
|
+ if (!IS_ERR(page)) {
|
|
+ wait_on_page_locked(page);
|
|
+ if (!PageUptodate(page)) {
|
|
+ page_cache_release(page);
|
|
+ page = ERR_PTR(-EIO);
|
|
+ }
|
|
+ }
|
|
+ return page;
|
|
+}
|
|
+
|
|
static struct page *__read_cache_page(struct address_space *mapping,
|
|
pgoff_t index,
|
|
int (*filler)(void *, struct page *),
|
|
@@ -1823,6 +2065,8 @@ repeat:
|
|
if (err < 0) {
|
|
page_cache_release(page);
|
|
page = ERR_PTR(err);
|
|
+ } else {
|
|
+ page = wait_on_page_read(page);
|
|
}
|
|
}
|
|
return page;
|
|
@@ -1859,6 +2103,10 @@ retry:
|
|
if (err < 0) {
|
|
page_cache_release(page);
|
|
return ERR_PTR(err);
|
|
+ } else {
|
|
+ page = wait_on_page_read(page);
|
|
+ if (IS_ERR(page))
|
|
+ return page;
|
|
}
|
|
out:
|
|
mark_page_accessed(page);
|
|
@@ -1866,40 +2114,25 @@ out:
|
|
}
|
|
|
|
/**
|
|
- * read_cache_page_async - read into page cache, fill it if needed
|
|
+ * read_cache_page - read into page cache, fill it if needed
|
|
* @mapping: the page's address_space
|
|
* @index: the page index
|
|
* @filler: function to perform the read
|
|
* @data: first arg to filler(data, page) function, often left as NULL
|
|
*
|
|
- * Same as read_cache_page, but don't wait for page to become unlocked
|
|
- * after submitting it to the filler.
|
|
- *
|
|
* Read into the page cache. If a page already exists, and PageUptodate() is
|
|
- * not set, try to fill the page but don't wait for it to become unlocked.
|
|
+ * not set, try to fill the page and wait for it to become unlocked.
|
|
*
|
|
* If the page does not get brought uptodate, return -EIO.
|
|
*/
|
|
-struct page *read_cache_page_async(struct address_space *mapping,
|
|
+struct page *read_cache_page(struct address_space *mapping,
|
|
pgoff_t index,
|
|
int (*filler)(void *, struct page *),
|
|
void *data)
|
|
{
|
|
return do_read_cache_page(mapping, index, filler, data, mapping_gfp_mask(mapping));
|
|
}
|
|
-EXPORT_SYMBOL(read_cache_page_async);
|
|
-
|
|
-static struct page *wait_on_page_read(struct page *page)
|
|
-{
|
|
- if (!IS_ERR(page)) {
|
|
- wait_on_page_locked(page);
|
|
- if (!PageUptodate(page)) {
|
|
- page_cache_release(page);
|
|
- page = ERR_PTR(-EIO);
|
|
- }
|
|
- }
|
|
- return page;
|
|
-}
|
|
+EXPORT_SYMBOL(read_cache_page);
|
|
|
|
/**
|
|
* read_cache_page_gfp - read into page cache, using specified page allocation flags.
|
|
@@ -1918,31 +2151,10 @@ struct page *read_cache_page_gfp(struct address_space *mapping,
|
|
{
|
|
filler_t *filler = (filler_t *)mapping->a_ops->readpage;
|
|
|
|
- return wait_on_page_read(do_read_cache_page(mapping, index, filler, NULL, gfp));
|
|
+ return do_read_cache_page(mapping, index, filler, NULL, gfp);
|
|
}
|
|
EXPORT_SYMBOL(read_cache_page_gfp);
|
|
|
|
-/**
|
|
- * read_cache_page - read into page cache, fill it if needed
|
|
- * @mapping: the page's address_space
|
|
- * @index: the page index
|
|
- * @filler: function to perform the read
|
|
- * @data: first arg to filler(data, page) function, often left as NULL
|
|
- *
|
|
- * Read into the page cache. If a page already exists, and PageUptodate() is
|
|
- * not set, try to fill the page then wait for it to become unlocked.
|
|
- *
|
|
- * If the page does not get brought uptodate, return -EIO.
|
|
- */
|
|
-struct page *read_cache_page(struct address_space *mapping,
|
|
- pgoff_t index,
|
|
- int (*filler)(void *, struct page *),
|
|
- void *data)
|
|
-{
|
|
- return wait_on_page_read(read_cache_page_async(mapping, index, filler, data));
|
|
-}
|
|
-EXPORT_SYMBOL(read_cache_page);
|
|
-
|
|
static size_t __iovec_copy_from_user_inatomic(char *vaddr,
|
|
const struct iovec *iov, size_t base, size_t bytes)
|
|
{
|
|
@@ -1976,7 +2188,6 @@ size_t iov_iter_copy_from_user_atomic(struct page *page,
|
|
char *kaddr;
|
|
size_t copied;
|
|
|
|
- BUG_ON(!in_atomic());
|
|
kaddr = kmap_atomic(page);
|
|
if (likely(i->nr_segs == 1)) {
|
|
int left;
|
|
@@ -2350,9 +2561,7 @@ again:
|
|
if (mapping_writably_mapped(mapping))
|
|
flush_dcache_page(page);
|
|
|
|
- pagefault_disable();
|
|
copied = iov_iter_copy_from_user_atomic(page, i, offset, bytes);
|
|
- pagefault_enable();
|
|
flush_dcache_page(page);
|
|
|
|
mark_page_accessed(page);
|
|
diff --git a/mm/internal.h b/mm/internal.h
|
|
index 3e910000fda4..1a8a0d4b687a 100644
|
|
--- a/mm/internal.h
|
|
+++ b/mm/internal.h
|
|
@@ -11,6 +11,7 @@
|
|
#ifndef __MM_INTERNAL_H
|
|
#define __MM_INTERNAL_H
|
|
|
|
+#include <linux/fs.h>
|
|
#include <linux/mm.h>
|
|
|
|
void free_pgtables(struct mmu_gather *tlb, struct vm_area_struct *start_vma,
|
|
@@ -21,6 +22,20 @@ static inline void set_page_count(struct page *page, int v)
|
|
atomic_set(&page->_count, v);
|
|
}
|
|
|
|
+extern int __do_page_cache_readahead(struct address_space *mapping,
|
|
+ struct file *filp, pgoff_t offset, unsigned long nr_to_read,
|
|
+ unsigned long lookahead_size);
|
|
+
|
|
+/*
|
|
+ * Submit IO for the read-ahead request in file_ra_state.
|
|
+ */
|
|
+static inline unsigned long ra_submit(struct file_ra_state *ra,
|
|
+ struct address_space *mapping, struct file *filp)
|
|
+{
|
|
+ return __do_page_cache_readahead(mapping, filp,
|
|
+ ra->start, ra->size, ra->async_size);
|
|
+}
|
|
+
|
|
/*
|
|
* Turn a non-refcounted page (->_count == 0) into refcounted with
|
|
* a count of one.
|
|
@@ -119,7 +134,7 @@ struct compact_control {
|
|
unsigned long nr_migratepages; /* Number of pages to migrate */
|
|
unsigned long free_pfn; /* isolate_freepages search base */
|
|
unsigned long migrate_pfn; /* isolate_migratepages search base */
|
|
- bool sync; /* Synchronous migration */
|
|
+ enum migrate_mode mode; /* Async or sync migration mode */
|
|
bool ignore_skip_hint; /* Scan blocks even if marked skip */
|
|
bool finished_update_free; /* True when the zone cached pfns are
|
|
* no longer being updated
|
|
@@ -129,7 +144,10 @@ struct compact_control {
|
|
int order; /* order a direct compactor needs */
|
|
int migratetype; /* MOVABLE, RECLAIMABLE etc */
|
|
struct zone *zone;
|
|
- bool contended; /* True if a lock was contended */
|
|
+ bool contended; /* True if a lock was contended, or
|
|
+ * need_resched() true during async
|
|
+ * compaction
|
|
+ */
|
|
};
|
|
|
|
unsigned long
|
|
diff --git a/mm/madvise.c b/mm/madvise.c
|
|
index 539eeb96b323..a402f8fdc68e 100644
|
|
--- a/mm/madvise.c
|
|
+++ b/mm/madvise.c
|
|
@@ -195,7 +195,7 @@ static void force_shm_swapin_readahead(struct vm_area_struct *vma,
|
|
for (; start < end; start += PAGE_SIZE) {
|
|
index = ((start - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff;
|
|
|
|
- page = find_get_page(mapping, index);
|
|
+ page = find_get_entry(mapping, index);
|
|
if (!radix_tree_exceptional_entry(page)) {
|
|
if (page)
|
|
page_cache_release(page);
|
|
diff --git a/mm/memory-failure.c b/mm/memory-failure.c
|
|
index 33365e9ce6a7..a98c7fce470a 100644
|
|
--- a/mm/memory-failure.c
|
|
+++ b/mm/memory-failure.c
|
|
@@ -1540,7 +1540,7 @@ static int soft_offline_huge_page(struct page *page, int flags)
|
|
|
|
/* Keep page count to indicate a given hugepage is isolated. */
|
|
list_move(&hpage->lru, &pagelist);
|
|
- ret = migrate_pages(&pagelist, new_page, MPOL_MF_MOVE_ALL,
|
|
+ ret = migrate_pages(&pagelist, new_page, NULL, MPOL_MF_MOVE_ALL,
|
|
MIGRATE_SYNC, MR_MEMORY_FAILURE);
|
|
if (ret) {
|
|
pr_info("soft offline: %#lx: migration failed %d, type %lx\n",
|
|
@@ -1621,7 +1621,7 @@ static int __soft_offline_page(struct page *page, int flags)
|
|
inc_zone_page_state(page, NR_ISOLATED_ANON +
|
|
page_is_file_cache(page));
|
|
list_add(&page->lru, &pagelist);
|
|
- ret = migrate_pages(&pagelist, new_page, MPOL_MF_MOVE_ALL,
|
|
+ ret = migrate_pages(&pagelist, new_page, NULL, MPOL_MF_MOVE_ALL,
|
|
MIGRATE_SYNC, MR_MEMORY_FAILURE);
|
|
if (ret) {
|
|
if (!list_empty(&pagelist)) {
|
|
diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
|
|
index a650db29606f..f6f23833de44 100644
|
|
--- a/mm/memory_hotplug.c
|
|
+++ b/mm/memory_hotplug.c
|
|
@@ -1332,7 +1332,7 @@ do_migrate_range(unsigned long start_pfn, unsigned long end_pfn)
|
|
* alloc_migrate_target should be improooooved!!
|
|
* migrate_pages returns # of failed pages.
|
|
*/
|
|
- ret = migrate_pages(&source, alloc_migrate_target, 0,
|
|
+ ret = migrate_pages(&source, alloc_migrate_target, NULL, 0,
|
|
MIGRATE_SYNC, MR_MEMORY_HOTPLUG);
|
|
if (ret)
|
|
putback_movable_pages(&source);
|
|
diff --git a/mm/mempolicy.c b/mm/mempolicy.c
|
|
index 796c7e6cf93b..e8fff0fa1202 100644
|
|
--- a/mm/mempolicy.c
|
|
+++ b/mm/mempolicy.c
|
|
@@ -1060,7 +1060,7 @@ static int migrate_to_node(struct mm_struct *mm, int source, int dest,
|
|
flags | MPOL_MF_DISCONTIG_OK, &pagelist);
|
|
|
|
if (!list_empty(&pagelist)) {
|
|
- err = migrate_pages(&pagelist, new_node_page, dest,
|
|
+ err = migrate_pages(&pagelist, new_node_page, NULL, dest,
|
|
MIGRATE_SYNC, MR_SYSCALL);
|
|
if (err)
|
|
putback_movable_pages(&pagelist);
|
|
@@ -1306,7 +1306,7 @@ static long do_mbind(unsigned long start, unsigned long len,
|
|
|
|
if (!list_empty(&pagelist)) {
|
|
WARN_ON_ONCE(flags & MPOL_MF_LAZY);
|
|
- nr_failed = migrate_pages(&pagelist, new_page,
|
|
+ nr_failed = migrate_pages(&pagelist, new_page, NULL,
|
|
start, MIGRATE_SYNC, MR_MEMPOLICY_MBIND);
|
|
if (nr_failed)
|
|
putback_movable_pages(&pagelist);
|
|
diff --git a/mm/migrate.c b/mm/migrate.c
|
|
index 13f47fbe3550..3acac4a62c4b 100644
|
|
--- a/mm/migrate.c
|
|
+++ b/mm/migrate.c
|
|
@@ -941,8 +941,9 @@ out:
|
|
* Obtain the lock on page, remove all ptes and migrate the page
|
|
* to the newly allocated page in newpage.
|
|
*/
|
|
-static int unmap_and_move(new_page_t get_new_page, unsigned long private,
|
|
- struct page *page, int force, enum migrate_mode mode)
|
|
+static int unmap_and_move(new_page_t get_new_page, free_page_t put_new_page,
|
|
+ unsigned long private, struct page *page, int force,
|
|
+ enum migrate_mode mode)
|
|
{
|
|
int rc = 0;
|
|
int *result = NULL;
|
|
@@ -986,11 +987,18 @@ out:
|
|
page_is_file_cache(page));
|
|
putback_lru_page(page);
|
|
}
|
|
+
|
|
/*
|
|
- * Move the new page to the LRU. If migration was not successful
|
|
- * then this will free the page.
|
|
+ * If migration was not successful and there's a freeing callback, use
|
|
+ * it. Otherwise, putback_lru_page() will drop the reference grabbed
|
|
+ * during isolation.
|
|
*/
|
|
- putback_lru_page(newpage);
|
|
+ if (rc != MIGRATEPAGE_SUCCESS && put_new_page) {
|
|
+ ClearPageSwapBacked(newpage);
|
|
+ put_new_page(newpage, private);
|
|
+ } else
|
|
+ putback_lru_page(newpage);
|
|
+
|
|
if (result) {
|
|
if (rc)
|
|
*result = rc;
|
|
@@ -1019,8 +1027,9 @@ out:
|
|
* will wait in the page fault for migration to complete.
|
|
*/
|
|
static int unmap_and_move_huge_page(new_page_t get_new_page,
|
|
- unsigned long private, struct page *hpage,
|
|
- int force, enum migrate_mode mode)
|
|
+ free_page_t put_new_page, unsigned long private,
|
|
+ struct page *hpage, int force,
|
|
+ enum migrate_mode mode)
|
|
{
|
|
int rc = 0;
|
|
int *result = NULL;
|
|
@@ -1059,20 +1068,30 @@ static int unmap_and_move_huge_page(new_page_t get_new_page,
|
|
if (!page_mapped(hpage))
|
|
rc = move_to_new_page(new_hpage, hpage, 1, mode);
|
|
|
|
- if (rc)
|
|
+ if (rc != MIGRATEPAGE_SUCCESS)
|
|
remove_migration_ptes(hpage, hpage);
|
|
|
|
if (anon_vma)
|
|
put_anon_vma(anon_vma);
|
|
|
|
- if (!rc)
|
|
+ if (rc == MIGRATEPAGE_SUCCESS)
|
|
hugetlb_cgroup_migrate(hpage, new_hpage);
|
|
|
|
unlock_page(hpage);
|
|
out:
|
|
if (rc != -EAGAIN)
|
|
putback_active_hugepage(hpage);
|
|
- put_page(new_hpage);
|
|
+
|
|
+ /*
|
|
+ * If migration was not successful and there's a freeing callback, use
|
|
+ * it. Otherwise, put_page() will drop the reference grabbed during
|
|
+ * isolation.
|
|
+ */
|
|
+ if (rc != MIGRATEPAGE_SUCCESS && put_new_page)
|
|
+ put_new_page(new_hpage, private);
|
|
+ else
|
|
+ put_page(new_hpage);
|
|
+
|
|
if (result) {
|
|
if (rc)
|
|
*result = rc;
|
|
@@ -1089,6 +1108,8 @@ out:
|
|
* @from: The list of pages to be migrated.
|
|
* @get_new_page: The function used to allocate free pages to be used
|
|
* as the target of the page migration.
|
|
+ * @put_new_page: The function used to free target pages if migration
|
|
+ * fails, or NULL if no special handling is necessary.
|
|
* @private: Private data to be passed on to get_new_page()
|
|
* @mode: The migration mode that specifies the constraints for
|
|
* page migration, if any.
|
|
@@ -1102,7 +1123,8 @@ out:
|
|
* Returns the number of pages that were not migrated, or an error code.
|
|
*/
|
|
int migrate_pages(struct list_head *from, new_page_t get_new_page,
|
|
- unsigned long private, enum migrate_mode mode, int reason)
|
|
+ free_page_t put_new_page, unsigned long private,
|
|
+ enum migrate_mode mode, int reason)
|
|
{
|
|
int retry = 1;
|
|
int nr_failed = 0;
|
|
@@ -1124,10 +1146,11 @@ int migrate_pages(struct list_head *from, new_page_t get_new_page,
|
|
|
|
if (PageHuge(page))
|
|
rc = unmap_and_move_huge_page(get_new_page,
|
|
- private, page, pass > 2, mode);
|
|
+ put_new_page, private, page,
|
|
+ pass > 2, mode);
|
|
else
|
|
- rc = unmap_and_move(get_new_page, private,
|
|
- page, pass > 2, mode);
|
|
+ rc = unmap_and_move(get_new_page, put_new_page,
|
|
+ private, page, pass > 2, mode);
|
|
|
|
switch(rc) {
|
|
case -ENOMEM:
|
|
@@ -1276,7 +1299,7 @@ set_status:
|
|
|
|
err = 0;
|
|
if (!list_empty(&pagelist)) {
|
|
- err = migrate_pages(&pagelist, new_page_node,
|
|
+ err = migrate_pages(&pagelist, new_page_node, NULL,
|
|
(unsigned long)pm, MIGRATE_SYNC, MR_SYSCALL);
|
|
if (err)
|
|
putback_movable_pages(&pagelist);
|
|
@@ -1732,7 +1755,8 @@ int migrate_misplaced_page(struct page *page, struct vm_area_struct *vma,
|
|
|
|
list_add(&page->lru, &migratepages);
|
|
nr_remaining = migrate_pages(&migratepages, alloc_misplaced_dst_page,
|
|
- node, MIGRATE_ASYNC, MR_NUMA_MISPLACED);
|
|
+ NULL, node, MIGRATE_ASYNC,
|
|
+ MR_NUMA_MISPLACED);
|
|
if (nr_remaining) {
|
|
if (!list_empty(&migratepages)) {
|
|
list_del(&page->lru);
|
|
diff --git a/mm/mincore.c b/mm/mincore.c
|
|
index 101623378fbf..725c80961048 100644
|
|
--- a/mm/mincore.c
|
|
+++ b/mm/mincore.c
|
|
@@ -70,13 +70,21 @@ static unsigned char mincore_page(struct address_space *mapping, pgoff_t pgoff)
|
|
* any other file mapping (ie. marked !present and faulted in with
|
|
* tmpfs's .fault). So swapped out tmpfs mappings are tested here.
|
|
*/
|
|
- page = find_get_page(mapping, pgoff);
|
|
#ifdef CONFIG_SWAP
|
|
- /* shmem/tmpfs may return swap: account for swapcache page too. */
|
|
- if (radix_tree_exceptional_entry(page)) {
|
|
- swp_entry_t swap = radix_to_swp_entry(page);
|
|
- page = find_get_page(swap_address_space(swap), swap.val);
|
|
- }
|
|
+ if (shmem_mapping(mapping)) {
|
|
+ page = find_get_entry(mapping, pgoff);
|
|
+ /*
|
|
+ * shmem/tmpfs may return swap: account for swapcache
|
|
+ * page too.
|
|
+ */
|
|
+ if (radix_tree_exceptional_entry(page)) {
|
|
+ swp_entry_t swp = radix_to_swp_entry(page);
|
|
+ page = find_get_page(swap_address_space(swp), swp.val);
|
|
+ }
|
|
+ } else
|
|
+ page = find_get_page(mapping, pgoff);
|
|
+#else
|
|
+ page = find_get_page(mapping, pgoff);
|
|
#endif
|
|
if (page) {
|
|
present = PageUptodate(page);
|
|
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
|
|
index 7b2611a055a7..4b258297cc7c 100644
|
|
--- a/mm/page_alloc.c
|
|
+++ b/mm/page_alloc.c
|
|
@@ -943,6 +943,7 @@ struct page *__rmqueue_smallest(struct zone *zone, unsigned int order,
|
|
rmv_page_order(page);
|
|
area->nr_free--;
|
|
expand(zone, page, order, current_order, area, migratetype);
|
|
+ set_freepage_migratetype(page, migratetype);
|
|
return page;
|
|
}
|
|
|
|
@@ -1069,7 +1070,9 @@ static int try_to_steal_freepages(struct zone *zone, struct page *page,
|
|
|
|
/*
|
|
* When borrowing from MIGRATE_CMA, we need to release the excess
|
|
- * buddy pages to CMA itself.
|
|
+ * buddy pages to CMA itself. We also ensure the freepage_migratetype
|
|
+ * is set to CMA so it is returned to the correct freelist in case
|
|
+ * the page ends up being not actually allocated from the pcp lists.
|
|
*/
|
|
if (is_migrate_cma(fallback_type))
|
|
return fallback_type;
|
|
@@ -1137,6 +1140,12 @@ __rmqueue_fallback(struct zone *zone, int order, int start_migratetype)
|
|
|
|
expand(zone, page, order, current_order, area,
|
|
new_type);
|
|
+ /* The freepage_migratetype may differ from pageblock's
|
|
+ * migratetype depending on the decisions in
|
|
+ * try_to_steal_freepages. This is OK as long as it does
|
|
+ * not differ for MIGRATE_CMA type.
|
|
+ */
|
|
+ set_freepage_migratetype(page, new_type);
|
|
|
|
trace_mm_page_alloc_extfrag(page, order, current_order,
|
|
start_migratetype, migratetype, new_type);
|
|
@@ -1187,7 +1196,7 @@ static int rmqueue_bulk(struct zone *zone, unsigned int order,
|
|
unsigned long count, struct list_head *list,
|
|
int migratetype, int cold)
|
|
{
|
|
- int mt = migratetype, i;
|
|
+ int i;
|
|
|
|
spin_lock(&zone->lock);
|
|
for (i = 0; i < count; ++i) {
|
|
@@ -1208,14 +1217,8 @@ static int rmqueue_bulk(struct zone *zone, unsigned int order,
|
|
list_add(&page->lru, list);
|
|
else
|
|
list_add_tail(&page->lru, list);
|
|
- if (IS_ENABLED(CONFIG_CMA)) {
|
|
- mt = get_pageblock_migratetype(page);
|
|
- if (!is_migrate_cma(mt) && !is_migrate_isolate(mt))
|
|
- mt = migratetype;
|
|
- }
|
|
- set_freepage_migratetype(page, mt);
|
|
list = &page->lru;
|
|
- if (is_migrate_cma(mt))
|
|
+ if (is_migrate_cma(get_freepage_migratetype(page)))
|
|
__mod_zone_page_state(zone, NR_FREE_CMA_PAGES,
|
|
-(1 << order));
|
|
}
|
|
@@ -1584,7 +1587,7 @@ again:
|
|
if (!page)
|
|
goto failed;
|
|
__mod_zone_freepage_state(zone, -(1 << order),
|
|
- get_pageblock_migratetype(page));
|
|
+ get_freepage_migratetype(page));
|
|
}
|
|
|
|
__mod_zone_page_state(zone, NR_ALLOC_BATCH, -(1 << order));
|
|
@@ -2246,7 +2249,7 @@ static struct page *
|
|
__alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order,
|
|
struct zonelist *zonelist, enum zone_type high_zoneidx,
|
|
nodemask_t *nodemask, int alloc_flags, struct zone *preferred_zone,
|
|
- int migratetype, bool sync_migration,
|
|
+ int migratetype, enum migrate_mode mode,
|
|
bool *contended_compaction, bool *deferred_compaction,
|
|
unsigned long *did_some_progress)
|
|
{
|
|
@@ -2260,7 +2263,7 @@ __alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order,
|
|
|
|
current->flags |= PF_MEMALLOC;
|
|
*did_some_progress = try_to_compact_pages(zonelist, order, gfp_mask,
|
|
- nodemask, sync_migration,
|
|
+ nodemask, mode,
|
|
contended_compaction);
|
|
current->flags &= ~PF_MEMALLOC;
|
|
|
|
@@ -2293,7 +2296,7 @@ __alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order,
|
|
* As async compaction considers a subset of pageblocks, only
|
|
* defer if the failure was a sync compaction failure.
|
|
*/
|
|
- if (sync_migration)
|
|
+ if (mode != MIGRATE_ASYNC)
|
|
defer_compaction(preferred_zone, order);
|
|
|
|
cond_resched();
|
|
@@ -2306,9 +2309,8 @@ static inline struct page *
|
|
__alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order,
|
|
struct zonelist *zonelist, enum zone_type high_zoneidx,
|
|
nodemask_t *nodemask, int alloc_flags, struct zone *preferred_zone,
|
|
- int migratetype, bool sync_migration,
|
|
- bool *contended_compaction, bool *deferred_compaction,
|
|
- unsigned long *did_some_progress)
|
|
+ int migratetype, enum migrate_mode mode, bool *contended_compaction,
|
|
+ bool *deferred_compaction, unsigned long *did_some_progress)
|
|
{
|
|
return NULL;
|
|
}
|
|
@@ -2503,7 +2505,7 @@ __alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order,
|
|
int alloc_flags;
|
|
unsigned long pages_reclaimed = 0;
|
|
unsigned long did_some_progress;
|
|
- bool sync_migration = false;
|
|
+ enum migrate_mode migration_mode = MIGRATE_ASYNC;
|
|
bool deferred_compaction = false;
|
|
bool contended_compaction = false;
|
|
|
|
@@ -2597,17 +2599,15 @@ rebalance:
|
|
* Try direct compaction. The first pass is asynchronous. Subsequent
|
|
* attempts after direct reclaim are synchronous
|
|
*/
|
|
- page = __alloc_pages_direct_compact(gfp_mask, order,
|
|
- zonelist, high_zoneidx,
|
|
- nodemask,
|
|
- alloc_flags, preferred_zone,
|
|
- migratetype, sync_migration,
|
|
- &contended_compaction,
|
|
+ page = __alloc_pages_direct_compact(gfp_mask, order, zonelist,
|
|
+ high_zoneidx, nodemask, alloc_flags,
|
|
+ preferred_zone, migratetype,
|
|
+ migration_mode, &contended_compaction,
|
|
&deferred_compaction,
|
|
&did_some_progress);
|
|
if (page)
|
|
goto got_pg;
|
|
- sync_migration = true;
|
|
+ migration_mode = MIGRATE_SYNC_LIGHT;
|
|
|
|
/*
|
|
* If compaction is deferred for high-order allocations, it is because
|
|
@@ -2682,12 +2682,10 @@ rebalance:
|
|
* direct reclaim and reclaim/compaction depends on compaction
|
|
* being called after reclaim so call directly if necessary
|
|
*/
|
|
- page = __alloc_pages_direct_compact(gfp_mask, order,
|
|
- zonelist, high_zoneidx,
|
|
- nodemask,
|
|
- alloc_flags, preferred_zone,
|
|
- migratetype, sync_migration,
|
|
- &contended_compaction,
|
|
+ page = __alloc_pages_direct_compact(gfp_mask, order, zonelist,
|
|
+ high_zoneidx, nodemask, alloc_flags,
|
|
+ preferred_zone, migratetype,
|
|
+ migration_mode, &contended_compaction,
|
|
&deferred_compaction,
|
|
&did_some_progress);
|
|
if (page)
|
|
@@ -6261,7 +6259,7 @@ static int __alloc_contig_migrate_range(struct compact_control *cc,
|
|
cc->nr_migratepages -= nr_reclaimed;
|
|
|
|
ret = migrate_pages(&cc->migratepages, alloc_migrate_target,
|
|
- 0, MIGRATE_SYNC, MR_CMA);
|
|
+ NULL, 0, cc->mode, MR_CMA);
|
|
}
|
|
if (ret < 0) {
|
|
putback_movable_pages(&cc->migratepages);
|
|
@@ -6300,7 +6298,7 @@ int alloc_contig_range(unsigned long start, unsigned long end,
|
|
.nr_migratepages = 0,
|
|
.order = -1,
|
|
.zone = page_zone(pfn_to_page(start)),
|
|
- .sync = true,
|
|
+ .mode = MIGRATE_SYNC,
|
|
.ignore_skip_hint = true,
|
|
};
|
|
INIT_LIST_HEAD(&cc.migratepages);
|
|
diff --git a/mm/readahead.c b/mm/readahead.c
|
|
index 1fa0d6fca556..0ca36a7770b1 100644
|
|
--- a/mm/readahead.c
|
|
+++ b/mm/readahead.c
|
|
@@ -8,9 +8,7 @@
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
-#include <linux/fs.h>
|
|
#include <linux/gfp.h>
|
|
-#include <linux/mm.h>
|
|
#include <linux/export.h>
|
|
#include <linux/blkdev.h>
|
|
#include <linux/backing-dev.h>
|
|
@@ -20,6 +18,8 @@
|
|
#include <linux/syscalls.h>
|
|
#include <linux/file.h>
|
|
|
|
+#include "internal.h"
|
|
+
|
|
/*
|
|
* Initialise a struct file's readahead state. Assumes that the caller has
|
|
* memset *ra to zero.
|
|
@@ -149,8 +149,7 @@ out:
|
|
*
|
|
* Returns the number of pages requested, or the maximum amount of I/O allowed.
|
|
*/
|
|
-static int
|
|
-__do_page_cache_readahead(struct address_space *mapping, struct file *filp,
|
|
+int __do_page_cache_readahead(struct address_space *mapping, struct file *filp,
|
|
pgoff_t offset, unsigned long nr_to_read,
|
|
unsigned long lookahead_size)
|
|
{
|
|
@@ -179,7 +178,7 @@ __do_page_cache_readahead(struct address_space *mapping, struct file *filp,
|
|
rcu_read_lock();
|
|
page = radix_tree_lookup(&mapping->page_tree, page_offset);
|
|
rcu_read_unlock();
|
|
- if (page)
|
|
+ if (page && !radix_tree_exceptional_entry(page))
|
|
continue;
|
|
|
|
page = page_cache_alloc_readahead(mapping);
|
|
@@ -244,20 +243,6 @@ unsigned long max_sane_readahead(unsigned long nr)
|
|
}
|
|
|
|
/*
|
|
- * Submit IO for the read-ahead request in file_ra_state.
|
|
- */
|
|
-unsigned long ra_submit(struct file_ra_state *ra,
|
|
- struct address_space *mapping, struct file *filp)
|
|
-{
|
|
- int actual;
|
|
-
|
|
- actual = __do_page_cache_readahead(mapping, filp,
|
|
- ra->start, ra->size, ra->async_size);
|
|
-
|
|
- return actual;
|
|
-}
|
|
-
|
|
-/*
|
|
* Set the initial window size, round to next power of 2 and square
|
|
* for small size, x 4 for medium, and x 2 for large
|
|
* for 128k (32 page) max ra
|
|
@@ -347,7 +332,7 @@ static pgoff_t count_history_pages(struct address_space *mapping,
|
|
pgoff_t head;
|
|
|
|
rcu_read_lock();
|
|
- head = radix_tree_prev_hole(&mapping->page_tree, offset - 1, max);
|
|
+ head = page_cache_prev_hole(mapping, offset - 1, max);
|
|
rcu_read_unlock();
|
|
|
|
return offset - 1 - head;
|
|
@@ -427,7 +412,7 @@ ondemand_readahead(struct address_space *mapping,
|
|
pgoff_t start;
|
|
|
|
rcu_read_lock();
|
|
- start = radix_tree_next_hole(&mapping->page_tree, offset+1,max);
|
|
+ start = page_cache_next_hole(mapping, offset + 1, max);
|
|
rcu_read_unlock();
|
|
|
|
if (!start || start - offset > max)
|
|
diff --git a/mm/shmem.c b/mm/shmem.c
|
|
index f0d698ba7d0f..0f1447563f17 100644
|
|
--- a/mm/shmem.c
|
|
+++ b/mm/shmem.c
|
|
@@ -243,19 +243,17 @@ static int shmem_radix_tree_replace(struct address_space *mapping,
|
|
pgoff_t index, void *expected, void *replacement)
|
|
{
|
|
void **pslot;
|
|
- void *item = NULL;
|
|
+ void *item;
|
|
|
|
VM_BUG_ON(!expected);
|
|
+ VM_BUG_ON(!replacement);
|
|
pslot = radix_tree_lookup_slot(&mapping->page_tree, index);
|
|
- if (pslot)
|
|
- item = radix_tree_deref_slot_protected(pslot,
|
|
- &mapping->tree_lock);
|
|
+ if (!pslot)
|
|
+ return -ENOENT;
|
|
+ item = radix_tree_deref_slot_protected(pslot, &mapping->tree_lock);
|
|
if (item != expected)
|
|
return -ENOENT;
|
|
- if (replacement)
|
|
- radix_tree_replace_slot(pslot, replacement);
|
|
- else
|
|
- radix_tree_delete(&mapping->page_tree, index);
|
|
+ radix_tree_replace_slot(pslot, replacement);
|
|
return 0;
|
|
}
|
|
|
|
@@ -332,84 +330,20 @@ static void shmem_delete_from_page_cache(struct page *page, void *radswap)
|
|
}
|
|
|
|
/*
|
|
- * Like find_get_pages, but collecting swap entries as well as pages.
|
|
- */
|
|
-static unsigned shmem_find_get_pages_and_swap(struct address_space *mapping,
|
|
- pgoff_t start, unsigned int nr_pages,
|
|
- struct page **pages, pgoff_t *indices)
|
|
-{
|
|
- void **slot;
|
|
- unsigned int ret = 0;
|
|
- struct radix_tree_iter iter;
|
|
-
|
|
- if (!nr_pages)
|
|
- return 0;
|
|
-
|
|
- rcu_read_lock();
|
|
-restart:
|
|
- radix_tree_for_each_slot(slot, &mapping->page_tree, &iter, start) {
|
|
- struct page *page;
|
|
-repeat:
|
|
- page = radix_tree_deref_slot(slot);
|
|
- if (unlikely(!page))
|
|
- continue;
|
|
- if (radix_tree_exception(page)) {
|
|
- if (radix_tree_deref_retry(page))
|
|
- goto restart;
|
|
- /*
|
|
- * Otherwise, we must be storing a swap entry
|
|
- * here as an exceptional entry: so return it
|
|
- * without attempting to raise page count.
|
|
- */
|
|
- goto export;
|
|
- }
|
|
- if (!page_cache_get_speculative(page))
|
|
- goto repeat;
|
|
-
|
|
- /* Has the page moved? */
|
|
- if (unlikely(page != *slot)) {
|
|
- page_cache_release(page);
|
|
- goto repeat;
|
|
- }
|
|
-export:
|
|
- indices[ret] = iter.index;
|
|
- pages[ret] = page;
|
|
- if (++ret == nr_pages)
|
|
- break;
|
|
- }
|
|
- rcu_read_unlock();
|
|
- return ret;
|
|
-}
|
|
-
|
|
-/*
|
|
* Remove swap entry from radix tree, free the swap and its page cache.
|
|
*/
|
|
static int shmem_free_swap(struct address_space *mapping,
|
|
pgoff_t index, void *radswap)
|
|
{
|
|
- int error;
|
|
+ void *old;
|
|
|
|
spin_lock_irq(&mapping->tree_lock);
|
|
- error = shmem_radix_tree_replace(mapping, index, radswap, NULL);
|
|
+ old = radix_tree_delete_item(&mapping->page_tree, index, radswap);
|
|
spin_unlock_irq(&mapping->tree_lock);
|
|
- if (!error)
|
|
- free_swap_and_cache(radix_to_swp_entry(radswap));
|
|
- return error;
|
|
-}
|
|
-
|
|
-/*
|
|
- * Pagevec may contain swap entries, so shuffle up pages before releasing.
|
|
- */
|
|
-static void shmem_deswap_pagevec(struct pagevec *pvec)
|
|
-{
|
|
- int i, j;
|
|
-
|
|
- for (i = 0, j = 0; i < pagevec_count(pvec); i++) {
|
|
- struct page *page = pvec->pages[i];
|
|
- if (!radix_tree_exceptional_entry(page))
|
|
- pvec->pages[j++] = page;
|
|
- }
|
|
- pvec->nr = j;
|
|
+ if (old != radswap)
|
|
+ return -ENOENT;
|
|
+ free_swap_and_cache(radix_to_swp_entry(radswap));
|
|
+ return 0;
|
|
}
|
|
|
|
/*
|
|
@@ -430,12 +364,12 @@ void shmem_unlock_mapping(struct address_space *mapping)
|
|
* Avoid pagevec_lookup(): find_get_pages() returns 0 as if it
|
|
* has finished, if it hits a row of PAGEVEC_SIZE swap entries.
|
|
*/
|
|
- pvec.nr = shmem_find_get_pages_and_swap(mapping, index,
|
|
- PAGEVEC_SIZE, pvec.pages, indices);
|
|
+ pvec.nr = find_get_entries(mapping, index,
|
|
+ PAGEVEC_SIZE, pvec.pages, indices);
|
|
if (!pvec.nr)
|
|
break;
|
|
index = indices[pvec.nr - 1] + 1;
|
|
- shmem_deswap_pagevec(&pvec);
|
|
+ pagevec_remove_exceptionals(&pvec);
|
|
check_move_unevictable_pages(pvec.pages, pvec.nr);
|
|
pagevec_release(&pvec);
|
|
cond_resched();
|
|
@@ -467,9 +401,9 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, loff_t lend,
|
|
pagevec_init(&pvec, 0);
|
|
index = start;
|
|
while (index < end) {
|
|
- pvec.nr = shmem_find_get_pages_and_swap(mapping, index,
|
|
- min(end - index, (pgoff_t)PAGEVEC_SIZE),
|
|
- pvec.pages, indices);
|
|
+ pvec.nr = find_get_entries(mapping, index,
|
|
+ min(end - index, (pgoff_t)PAGEVEC_SIZE),
|
|
+ pvec.pages, indices);
|
|
if (!pvec.nr)
|
|
break;
|
|
mem_cgroup_uncharge_start();
|
|
@@ -498,7 +432,7 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, loff_t lend,
|
|
}
|
|
unlock_page(page);
|
|
}
|
|
- shmem_deswap_pagevec(&pvec);
|
|
+ pagevec_remove_exceptionals(&pvec);
|
|
pagevec_release(&pvec);
|
|
mem_cgroup_uncharge_end();
|
|
cond_resched();
|
|
@@ -536,9 +470,10 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, loff_t lend,
|
|
index = start;
|
|
while (index < end) {
|
|
cond_resched();
|
|
- pvec.nr = shmem_find_get_pages_and_swap(mapping, index,
|
|
+
|
|
+ pvec.nr = find_get_entries(mapping, index,
|
|
min(end - index, (pgoff_t)PAGEVEC_SIZE),
|
|
- pvec.pages, indices);
|
|
+ pvec.pages, indices);
|
|
if (!pvec.nr) {
|
|
/* If all gone or hole-punch or unfalloc, we're done */
|
|
if (index == start || end != -1)
|
|
@@ -581,7 +516,7 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, loff_t lend,
|
|
}
|
|
unlock_page(page);
|
|
}
|
|
- shmem_deswap_pagevec(&pvec);
|
|
+ pagevec_remove_exceptionals(&pvec);
|
|
pagevec_release(&pvec);
|
|
mem_cgroup_uncharge_end();
|
|
index++;
|
|
@@ -1088,7 +1023,7 @@ static int shmem_getpage_gfp(struct inode *inode, pgoff_t index,
|
|
return -EFBIG;
|
|
repeat:
|
|
swap.val = 0;
|
|
- page = find_lock_page(mapping, index);
|
|
+ page = find_lock_entry(mapping, index);
|
|
if (radix_tree_exceptional_entry(page)) {
|
|
swap = radix_to_swp_entry(page);
|
|
page = NULL;
|
|
@@ -1483,6 +1418,11 @@ static struct inode *shmem_get_inode(struct super_block *sb, const struct inode
|
|
return inode;
|
|
}
|
|
|
|
+bool shmem_mapping(struct address_space *mapping)
|
|
+{
|
|
+ return mapping->backing_dev_info == &shmem_backing_dev_info;
|
|
+}
|
|
+
|
|
#ifdef CONFIG_TMPFS
|
|
static const struct inode_operations shmem_symlink_inode_operations;
|
|
static const struct inode_operations shmem_short_symlink_operations;
|
|
@@ -1795,7 +1735,7 @@ static pgoff_t shmem_seek_hole_data(struct address_space *mapping,
|
|
pagevec_init(&pvec, 0);
|
|
pvec.nr = 1; /* start small: we may be there already */
|
|
while (!done) {
|
|
- pvec.nr = shmem_find_get_pages_and_swap(mapping, index,
|
|
+ pvec.nr = find_get_entries(mapping, index,
|
|
pvec.nr, pvec.pages, indices);
|
|
if (!pvec.nr) {
|
|
if (whence == SEEK_DATA)
|
|
@@ -1822,7 +1762,7 @@ static pgoff_t shmem_seek_hole_data(struct address_space *mapping,
|
|
break;
|
|
}
|
|
}
|
|
- shmem_deswap_pagevec(&pvec);
|
|
+ pagevec_remove_exceptionals(&pvec);
|
|
pagevec_release(&pvec);
|
|
pvec.nr = PAGEVEC_SIZE;
|
|
cond_resched();
|
|
diff --git a/mm/swap.c b/mm/swap.c
|
|
index 0092097b3f4c..c8048d71c642 100644
|
|
--- a/mm/swap.c
|
|
+++ b/mm/swap.c
|
|
@@ -948,6 +948,57 @@ void __pagevec_lru_add(struct pagevec *pvec)
|
|
EXPORT_SYMBOL(__pagevec_lru_add);
|
|
|
|
/**
|
|
+ * pagevec_lookup_entries - gang pagecache lookup
|
|
+ * @pvec: Where the resulting entries are placed
|
|
+ * @mapping: The address_space to search
|
|
+ * @start: The starting entry index
|
|
+ * @nr_entries: The maximum number of entries
|
|
+ * @indices: The cache indices corresponding to the entries in @pvec
|
|
+ *
|
|
+ * pagevec_lookup_entries() will search for and return a group of up
|
|
+ * to @nr_entries pages and shadow entries in the mapping. All
|
|
+ * entries are placed in @pvec. pagevec_lookup_entries() takes a
|
|
+ * reference against actual pages in @pvec.
|
|
+ *
|
|
+ * The search returns a group of mapping-contiguous entries with
|
|
+ * ascending indexes. There may be holes in the indices due to
|
|
+ * not-present entries.
|
|
+ *
|
|
+ * pagevec_lookup_entries() returns the number of entries which were
|
|
+ * found.
|
|
+ */
|
|
+unsigned pagevec_lookup_entries(struct pagevec *pvec,
|
|
+ struct address_space *mapping,
|
|
+ pgoff_t start, unsigned nr_pages,
|
|
+ pgoff_t *indices)
|
|
+{
|
|
+ pvec->nr = find_get_entries(mapping, start, nr_pages,
|
|
+ pvec->pages, indices);
|
|
+ return pagevec_count(pvec);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * pagevec_remove_exceptionals - pagevec exceptionals pruning
|
|
+ * @pvec: The pagevec to prune
|
|
+ *
|
|
+ * pagevec_lookup_entries() fills both pages and exceptional radix
|
|
+ * tree entries into the pagevec. This function prunes all
|
|
+ * exceptionals from @pvec without leaving holes, so that it can be
|
|
+ * passed on to page-only pagevec operations.
|
|
+ */
|
|
+void pagevec_remove_exceptionals(struct pagevec *pvec)
|
|
+{
|
|
+ int i, j;
|
|
+
|
|
+ for (i = 0, j = 0; i < pagevec_count(pvec); i++) {
|
|
+ struct page *page = pvec->pages[i];
|
|
+ if (!radix_tree_exceptional_entry(page))
|
|
+ pvec->pages[j++] = page;
|
|
+ }
|
|
+ pvec->nr = j;
|
|
+}
|
|
+
|
|
+/**
|
|
* pagevec_lookup - gang pagecache lookup
|
|
* @pvec: Where the resulting pages are placed
|
|
* @mapping: The address_space to search
|
|
diff --git a/mm/truncate.c b/mm/truncate.c
|
|
index ac18edc30649..827ad8d2b5cd 100644
|
|
--- a/mm/truncate.c
|
|
+++ b/mm/truncate.c
|
|
@@ -23,6 +23,22 @@
|
|
#include <linux/rmap.h>
|
|
#include "internal.h"
|
|
|
|
+static void clear_exceptional_entry(struct address_space *mapping,
|
|
+ pgoff_t index, void *entry)
|
|
+{
|
|
+ /* Handled by shmem itself */
|
|
+ if (shmem_mapping(mapping))
|
|
+ return;
|
|
+
|
|
+ spin_lock_irq(&mapping->tree_lock);
|
|
+ /*
|
|
+ * Regular page slots are stabilized by the page lock even
|
|
+ * without the tree itself locked. These unlocked entries
|
|
+ * need verification under the tree lock.
|
|
+ */
|
|
+ radix_tree_delete_item(&mapping->page_tree, index, entry);
|
|
+ spin_unlock_irq(&mapping->tree_lock);
|
|
+}
|
|
|
|
/**
|
|
* do_invalidatepage - invalidate part or all of a page
|
|
@@ -209,6 +225,7 @@ void truncate_inode_pages_range(struct address_space *mapping,
|
|
unsigned int partial_start; /* inclusive */
|
|
unsigned int partial_end; /* exclusive */
|
|
struct pagevec pvec;
|
|
+ pgoff_t indices[PAGEVEC_SIZE];
|
|
pgoff_t index;
|
|
int i;
|
|
|
|
@@ -239,17 +256,23 @@ void truncate_inode_pages_range(struct address_space *mapping,
|
|
|
|
pagevec_init(&pvec, 0);
|
|
index = start;
|
|
- while (index < end && pagevec_lookup(&pvec, mapping, index,
|
|
- min(end - index, (pgoff_t)PAGEVEC_SIZE))) {
|
|
+ while (index < end && pagevec_lookup_entries(&pvec, mapping, index,
|
|
+ min(end - index, (pgoff_t)PAGEVEC_SIZE),
|
|
+ indices)) {
|
|
mem_cgroup_uncharge_start();
|
|
for (i = 0; i < pagevec_count(&pvec); i++) {
|
|
struct page *page = pvec.pages[i];
|
|
|
|
/* We rely upon deletion not changing page->index */
|
|
- index = page->index;
|
|
+ index = indices[i];
|
|
if (index >= end)
|
|
break;
|
|
|
|
+ if (radix_tree_exceptional_entry(page)) {
|
|
+ clear_exceptional_entry(mapping, index, page);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
if (!trylock_page(page))
|
|
continue;
|
|
WARN_ON(page->index != index);
|
|
@@ -260,6 +283,7 @@ void truncate_inode_pages_range(struct address_space *mapping,
|
|
truncate_inode_page(mapping, page);
|
|
unlock_page(page);
|
|
}
|
|
+ pagevec_remove_exceptionals(&pvec);
|
|
pagevec_release(&pvec);
|
|
mem_cgroup_uncharge_end();
|
|
cond_resched();
|
|
@@ -308,14 +332,16 @@ void truncate_inode_pages_range(struct address_space *mapping,
|
|
index = start;
|
|
for ( ; ; ) {
|
|
cond_resched();
|
|
- if (!pagevec_lookup(&pvec, mapping, index,
|
|
- min(end - index, (pgoff_t)PAGEVEC_SIZE))) {
|
|
+ if (!pagevec_lookup_entries(&pvec, mapping, index,
|
|
+ min(end - index, (pgoff_t)PAGEVEC_SIZE),
|
|
+ indices)) {
|
|
if (index == start)
|
|
break;
|
|
index = start;
|
|
continue;
|
|
}
|
|
- if (index == start && pvec.pages[0]->index >= end) {
|
|
+ if (index == start && indices[0] >= end) {
|
|
+ pagevec_remove_exceptionals(&pvec);
|
|
pagevec_release(&pvec);
|
|
break;
|
|
}
|
|
@@ -324,16 +350,22 @@ void truncate_inode_pages_range(struct address_space *mapping,
|
|
struct page *page = pvec.pages[i];
|
|
|
|
/* We rely upon deletion not changing page->index */
|
|
- index = page->index;
|
|
+ index = indices[i];
|
|
if (index >= end)
|
|
break;
|
|
|
|
+ if (radix_tree_exceptional_entry(page)) {
|
|
+ clear_exceptional_entry(mapping, index, page);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
lock_page(page);
|
|
WARN_ON(page->index != index);
|
|
wait_on_page_writeback(page);
|
|
truncate_inode_page(mapping, page);
|
|
unlock_page(page);
|
|
}
|
|
+ pagevec_remove_exceptionals(&pvec);
|
|
pagevec_release(&pvec);
|
|
mem_cgroup_uncharge_end();
|
|
index++;
|
|
@@ -376,6 +408,7 @@ EXPORT_SYMBOL(truncate_inode_pages);
|
|
unsigned long invalidate_mapping_pages(struct address_space *mapping,
|
|
pgoff_t start, pgoff_t end)
|
|
{
|
|
+ pgoff_t indices[PAGEVEC_SIZE];
|
|
struct pagevec pvec;
|
|
pgoff_t index = start;
|
|
unsigned long ret;
|
|
@@ -391,17 +424,23 @@ unsigned long invalidate_mapping_pages(struct address_space *mapping,
|
|
*/
|
|
|
|
pagevec_init(&pvec, 0);
|
|
- while (index <= end && pagevec_lookup(&pvec, mapping, index,
|
|
- min(end - index, (pgoff_t)PAGEVEC_SIZE - 1) + 1)) {
|
|
+ while (index <= end && pagevec_lookup_entries(&pvec, mapping, index,
|
|
+ min(end - index, (pgoff_t)PAGEVEC_SIZE - 1) + 1,
|
|
+ indices)) {
|
|
mem_cgroup_uncharge_start();
|
|
for (i = 0; i < pagevec_count(&pvec); i++) {
|
|
struct page *page = pvec.pages[i];
|
|
|
|
/* We rely upon deletion not changing page->index */
|
|
- index = page->index;
|
|
+ index = indices[i];
|
|
if (index > end)
|
|
break;
|
|
|
|
+ if (radix_tree_exceptional_entry(page)) {
|
|
+ clear_exceptional_entry(mapping, index, page);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
if (!trylock_page(page))
|
|
continue;
|
|
WARN_ON(page->index != index);
|
|
@@ -415,6 +454,7 @@ unsigned long invalidate_mapping_pages(struct address_space *mapping,
|
|
deactivate_page(page);
|
|
count += ret;
|
|
}
|
|
+ pagevec_remove_exceptionals(&pvec);
|
|
pagevec_release(&pvec);
|
|
mem_cgroup_uncharge_end();
|
|
cond_resched();
|
|
@@ -482,6 +522,7 @@ static int do_launder_page(struct address_space *mapping, struct page *page)
|
|
int invalidate_inode_pages2_range(struct address_space *mapping,
|
|
pgoff_t start, pgoff_t end)
|
|
{
|
|
+ pgoff_t indices[PAGEVEC_SIZE];
|
|
struct pagevec pvec;
|
|
pgoff_t index;
|
|
int i;
|
|
@@ -492,17 +533,23 @@ int invalidate_inode_pages2_range(struct address_space *mapping,
|
|
cleancache_invalidate_inode(mapping);
|
|
pagevec_init(&pvec, 0);
|
|
index = start;
|
|
- while (index <= end && pagevec_lookup(&pvec, mapping, index,
|
|
- min(end - index, (pgoff_t)PAGEVEC_SIZE - 1) + 1)) {
|
|
+ while (index <= end && pagevec_lookup_entries(&pvec, mapping, index,
|
|
+ min(end - index, (pgoff_t)PAGEVEC_SIZE - 1) + 1,
|
|
+ indices)) {
|
|
mem_cgroup_uncharge_start();
|
|
for (i = 0; i < pagevec_count(&pvec); i++) {
|
|
struct page *page = pvec.pages[i];
|
|
|
|
/* We rely upon deletion not changing page->index */
|
|
- index = page->index;
|
|
+ index = indices[i];
|
|
if (index > end)
|
|
break;
|
|
|
|
+ if (radix_tree_exceptional_entry(page)) {
|
|
+ clear_exceptional_entry(mapping, index, page);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
lock_page(page);
|
|
WARN_ON(page->index != index);
|
|
if (page->mapping != mapping) {
|
|
@@ -540,6 +587,7 @@ int invalidate_inode_pages2_range(struct address_space *mapping,
|
|
ret = ret2;
|
|
unlock_page(page);
|
|
}
|
|
+ pagevec_remove_exceptionals(&pvec);
|
|
pagevec_release(&pvec);
|
|
mem_cgroup_uncharge_end();
|
|
cond_resched();
|
|
diff --git a/mm/vmscan.c b/mm/vmscan.c
|
|
index 0c0b36e5b4f8..deb139e6b8ed 100644
|
|
--- a/mm/vmscan.c
|
|
+++ b/mm/vmscan.c
|
|
@@ -2018,13 +2018,27 @@ static void shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)
|
|
unsigned long nr_reclaimed = 0;
|
|
unsigned long nr_to_reclaim = sc->nr_to_reclaim;
|
|
struct blk_plug plug;
|
|
- bool scan_adjusted = false;
|
|
+ bool scan_adjusted;
|
|
|
|
get_scan_count(lruvec, sc, nr);
|
|
|
|
/* Record the original scan target for proportional adjustments later */
|
|
memcpy(targets, nr, sizeof(nr));
|
|
|
|
+ /*
|
|
+ * Global reclaiming within direct reclaim at DEF_PRIORITY is a normal
|
|
+ * event that can occur when there is little memory pressure e.g.
|
|
+ * multiple streaming readers/writers. Hence, we do not abort scanning
|
|
+ * when the requested number of pages are reclaimed when scanning at
|
|
+ * DEF_PRIORITY on the assumption that the fact we are direct
|
|
+ * reclaiming implies that kswapd is not keeping up and it is best to
|
|
+ * do a batch of work at once. For memcg reclaim one check is made to
|
|
+ * abort proportional reclaim if either the file or anon lru has already
|
|
+ * dropped to zero at the first pass.
|
|
+ */
|
|
+ scan_adjusted = (global_reclaim(sc) && !current_is_kswapd() &&
|
|
+ sc->priority == DEF_PRIORITY);
|
|
+
|
|
blk_start_plug(&plug);
|
|
while (nr[LRU_INACTIVE_ANON] || nr[LRU_ACTIVE_FILE] ||
|
|
nr[LRU_INACTIVE_FILE]) {
|
|
@@ -2045,17 +2059,8 @@ static void shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)
|
|
continue;
|
|
|
|
/*
|
|
- * For global direct reclaim, reclaim only the number of pages
|
|
- * requested. Less care is taken to scan proportionally as it
|
|
- * is more important to minimise direct reclaim stall latency
|
|
- * than it is to properly age the LRU lists.
|
|
- */
|
|
- if (global_reclaim(sc) && !current_is_kswapd())
|
|
- break;
|
|
-
|
|
- /*
|
|
* For kswapd and memcg, reclaim at least the number of pages
|
|
- * requested. Ensure that the anon and file LRUs shrink
|
|
+ * requested. Ensure that the anon and file LRUs are scanned
|
|
* proportionally what was requested by get_scan_count(). We
|
|
* stop reclaiming one LRU and reduce the amount scanning
|
|
* proportional to the original scan target.
|
|
@@ -2063,6 +2068,15 @@ static void shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)
|
|
nr_file = nr[LRU_INACTIVE_FILE] + nr[LRU_ACTIVE_FILE];
|
|
nr_anon = nr[LRU_INACTIVE_ANON] + nr[LRU_ACTIVE_ANON];
|
|
|
|
+ /*
|
|
+ * It's just vindictive to attack the larger once the smaller
|
|
+ * has gone to zero. And given the way we stop scanning the
|
|
+ * smaller below, this makes sure that we only make one nudge
|
|
+ * towards proportionality once we've got nr_to_reclaim.
|
|
+ */
|
|
+ if (!nr_file || !nr_anon)
|
|
+ break;
|
|
+
|
|
if (nr_file > nr_anon) {
|
|
unsigned long scan_target = targets[LRU_INACTIVE_ANON] +
|
|
targets[LRU_ACTIVE_ANON] + 1;
|
|
diff --git a/net/ceph/crypto.c b/net/ceph/crypto.c
|
|
index 6e7a236525b6..06f19b9e159a 100644
|
|
--- a/net/ceph/crypto.c
|
|
+++ b/net/ceph/crypto.c
|
|
@@ -89,11 +89,82 @@ static struct crypto_blkcipher *ceph_crypto_alloc_cipher(void)
|
|
|
|
static const u8 *aes_iv = (u8 *)CEPH_AES_IV;
|
|
|
|
+/*
|
|
+ * Should be used for buffers allocated with ceph_kvmalloc().
|
|
+ * Currently these are encrypt out-buffer (ceph_buffer) and decrypt
|
|
+ * in-buffer (msg front).
|
|
+ *
|
|
+ * Dispose of @sgt with teardown_sgtable().
|
|
+ *
|
|
+ * @prealloc_sg is to avoid memory allocation inside sg_alloc_table()
|
|
+ * in cases where a single sg is sufficient. No attempt to reduce the
|
|
+ * number of sgs by squeezing physically contiguous pages together is
|
|
+ * made though, for simplicity.
|
|
+ */
|
|
+static int setup_sgtable(struct sg_table *sgt, struct scatterlist *prealloc_sg,
|
|
+ const void *buf, unsigned int buf_len)
|
|
+{
|
|
+ struct scatterlist *sg;
|
|
+ const bool is_vmalloc = is_vmalloc_addr(buf);
|
|
+ unsigned int off = offset_in_page(buf);
|
|
+ unsigned int chunk_cnt = 1;
|
|
+ unsigned int chunk_len = PAGE_ALIGN(off + buf_len);
|
|
+ int i;
|
|
+ int ret;
|
|
+
|
|
+ if (buf_len == 0) {
|
|
+ memset(sgt, 0, sizeof(*sgt));
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (is_vmalloc) {
|
|
+ chunk_cnt = chunk_len >> PAGE_SHIFT;
|
|
+ chunk_len = PAGE_SIZE;
|
|
+ }
|
|
+
|
|
+ if (chunk_cnt > 1) {
|
|
+ ret = sg_alloc_table(sgt, chunk_cnt, GFP_NOFS);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ } else {
|
|
+ WARN_ON(chunk_cnt != 1);
|
|
+ sg_init_table(prealloc_sg, 1);
|
|
+ sgt->sgl = prealloc_sg;
|
|
+ sgt->nents = sgt->orig_nents = 1;
|
|
+ }
|
|
+
|
|
+ for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
|
|
+ struct page *page;
|
|
+ unsigned int len = min(chunk_len - off, buf_len);
|
|
+
|
|
+ if (is_vmalloc)
|
|
+ page = vmalloc_to_page(buf);
|
|
+ else
|
|
+ page = virt_to_page(buf);
|
|
+
|
|
+ sg_set_page(sg, page, len, off);
|
|
+
|
|
+ off = 0;
|
|
+ buf += len;
|
|
+ buf_len -= len;
|
|
+ }
|
|
+ WARN_ON(buf_len != 0);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void teardown_sgtable(struct sg_table *sgt)
|
|
+{
|
|
+ if (sgt->orig_nents > 1)
|
|
+ sg_free_table(sgt);
|
|
+}
|
|
+
|
|
static int ceph_aes_encrypt(const void *key, int key_len,
|
|
void *dst, size_t *dst_len,
|
|
const void *src, size_t src_len)
|
|
{
|
|
- struct scatterlist sg_in[2], sg_out[1];
|
|
+ struct scatterlist sg_in[2], prealloc_sg;
|
|
+ struct sg_table sg_out;
|
|
struct crypto_blkcipher *tfm = ceph_crypto_alloc_cipher();
|
|
struct blkcipher_desc desc = { .tfm = tfm, .flags = 0 };
|
|
int ret;
|
|
@@ -109,16 +180,18 @@ static int ceph_aes_encrypt(const void *key, int key_len,
|
|
|
|
*dst_len = src_len + zero_padding;
|
|
|
|
- crypto_blkcipher_setkey((void *)tfm, key, key_len);
|
|
sg_init_table(sg_in, 2);
|
|
sg_set_buf(&sg_in[0], src, src_len);
|
|
sg_set_buf(&sg_in[1], pad, zero_padding);
|
|
- sg_init_table(sg_out, 1);
|
|
- sg_set_buf(sg_out, dst, *dst_len);
|
|
+ ret = setup_sgtable(&sg_out, &prealloc_sg, dst, *dst_len);
|
|
+ if (ret)
|
|
+ goto out_tfm;
|
|
+
|
|
+ crypto_blkcipher_setkey((void *)tfm, key, key_len);
|
|
iv = crypto_blkcipher_crt(tfm)->iv;
|
|
ivsize = crypto_blkcipher_ivsize(tfm);
|
|
-
|
|
memcpy(iv, aes_iv, ivsize);
|
|
+
|
|
/*
|
|
print_hex_dump(KERN_ERR, "enc key: ", DUMP_PREFIX_NONE, 16, 1,
|
|
key, key_len, 1);
|
|
@@ -127,16 +200,22 @@ static int ceph_aes_encrypt(const void *key, int key_len,
|
|
print_hex_dump(KERN_ERR, "enc pad: ", DUMP_PREFIX_NONE, 16, 1,
|
|
pad, zero_padding, 1);
|
|
*/
|
|
- ret = crypto_blkcipher_encrypt(&desc, sg_out, sg_in,
|
|
+ ret = crypto_blkcipher_encrypt(&desc, sg_out.sgl, sg_in,
|
|
src_len + zero_padding);
|
|
- crypto_free_blkcipher(tfm);
|
|
- if (ret < 0)
|
|
+ if (ret < 0) {
|
|
pr_err("ceph_aes_crypt failed %d\n", ret);
|
|
+ goto out_sg;
|
|
+ }
|
|
/*
|
|
print_hex_dump(KERN_ERR, "enc out: ", DUMP_PREFIX_NONE, 16, 1,
|
|
dst, *dst_len, 1);
|
|
*/
|
|
- return 0;
|
|
+
|
|
+out_sg:
|
|
+ teardown_sgtable(&sg_out);
|
|
+out_tfm:
|
|
+ crypto_free_blkcipher(tfm);
|
|
+ return ret;
|
|
}
|
|
|
|
static int ceph_aes_encrypt2(const void *key, int key_len, void *dst,
|
|
@@ -144,7 +223,8 @@ static int ceph_aes_encrypt2(const void *key, int key_len, void *dst,
|
|
const void *src1, size_t src1_len,
|
|
const void *src2, size_t src2_len)
|
|
{
|
|
- struct scatterlist sg_in[3], sg_out[1];
|
|
+ struct scatterlist sg_in[3], prealloc_sg;
|
|
+ struct sg_table sg_out;
|
|
struct crypto_blkcipher *tfm = ceph_crypto_alloc_cipher();
|
|
struct blkcipher_desc desc = { .tfm = tfm, .flags = 0 };
|
|
int ret;
|
|
@@ -160,17 +240,19 @@ static int ceph_aes_encrypt2(const void *key, int key_len, void *dst,
|
|
|
|
*dst_len = src1_len + src2_len + zero_padding;
|
|
|
|
- crypto_blkcipher_setkey((void *)tfm, key, key_len);
|
|
sg_init_table(sg_in, 3);
|
|
sg_set_buf(&sg_in[0], src1, src1_len);
|
|
sg_set_buf(&sg_in[1], src2, src2_len);
|
|
sg_set_buf(&sg_in[2], pad, zero_padding);
|
|
- sg_init_table(sg_out, 1);
|
|
- sg_set_buf(sg_out, dst, *dst_len);
|
|
+ ret = setup_sgtable(&sg_out, &prealloc_sg, dst, *dst_len);
|
|
+ if (ret)
|
|
+ goto out_tfm;
|
|
+
|
|
+ crypto_blkcipher_setkey((void *)tfm, key, key_len);
|
|
iv = crypto_blkcipher_crt(tfm)->iv;
|
|
ivsize = crypto_blkcipher_ivsize(tfm);
|
|
-
|
|
memcpy(iv, aes_iv, ivsize);
|
|
+
|
|
/*
|
|
print_hex_dump(KERN_ERR, "enc key: ", DUMP_PREFIX_NONE, 16, 1,
|
|
key, key_len, 1);
|
|
@@ -181,23 +263,30 @@ static int ceph_aes_encrypt2(const void *key, int key_len, void *dst,
|
|
print_hex_dump(KERN_ERR, "enc pad: ", DUMP_PREFIX_NONE, 16, 1,
|
|
pad, zero_padding, 1);
|
|
*/
|
|
- ret = crypto_blkcipher_encrypt(&desc, sg_out, sg_in,
|
|
+ ret = crypto_blkcipher_encrypt(&desc, sg_out.sgl, sg_in,
|
|
src1_len + src2_len + zero_padding);
|
|
- crypto_free_blkcipher(tfm);
|
|
- if (ret < 0)
|
|
+ if (ret < 0) {
|
|
pr_err("ceph_aes_crypt2 failed %d\n", ret);
|
|
+ goto out_sg;
|
|
+ }
|
|
/*
|
|
print_hex_dump(KERN_ERR, "enc out: ", DUMP_PREFIX_NONE, 16, 1,
|
|
dst, *dst_len, 1);
|
|
*/
|
|
- return 0;
|
|
+
|
|
+out_sg:
|
|
+ teardown_sgtable(&sg_out);
|
|
+out_tfm:
|
|
+ crypto_free_blkcipher(tfm);
|
|
+ return ret;
|
|
}
|
|
|
|
static int ceph_aes_decrypt(const void *key, int key_len,
|
|
void *dst, size_t *dst_len,
|
|
const void *src, size_t src_len)
|
|
{
|
|
- struct scatterlist sg_in[1], sg_out[2];
|
|
+ struct sg_table sg_in;
|
|
+ struct scatterlist sg_out[2], prealloc_sg;
|
|
struct crypto_blkcipher *tfm = ceph_crypto_alloc_cipher();
|
|
struct blkcipher_desc desc = { .tfm = tfm };
|
|
char pad[16];
|
|
@@ -209,16 +298,16 @@ static int ceph_aes_decrypt(const void *key, int key_len,
|
|
if (IS_ERR(tfm))
|
|
return PTR_ERR(tfm);
|
|
|
|
- crypto_blkcipher_setkey((void *)tfm, key, key_len);
|
|
- sg_init_table(sg_in, 1);
|
|
sg_init_table(sg_out, 2);
|
|
- sg_set_buf(sg_in, src, src_len);
|
|
sg_set_buf(&sg_out[0], dst, *dst_len);
|
|
sg_set_buf(&sg_out[1], pad, sizeof(pad));
|
|
+ ret = setup_sgtable(&sg_in, &prealloc_sg, src, src_len);
|
|
+ if (ret)
|
|
+ goto out_tfm;
|
|
|
|
+ crypto_blkcipher_setkey((void *)tfm, key, key_len);
|
|
iv = crypto_blkcipher_crt(tfm)->iv;
|
|
ivsize = crypto_blkcipher_ivsize(tfm);
|
|
-
|
|
memcpy(iv, aes_iv, ivsize);
|
|
|
|
/*
|
|
@@ -227,12 +316,10 @@ static int ceph_aes_decrypt(const void *key, int key_len,
|
|
print_hex_dump(KERN_ERR, "dec in: ", DUMP_PREFIX_NONE, 16, 1,
|
|
src, src_len, 1);
|
|
*/
|
|
-
|
|
- ret = crypto_blkcipher_decrypt(&desc, sg_out, sg_in, src_len);
|
|
- crypto_free_blkcipher(tfm);
|
|
+ ret = crypto_blkcipher_decrypt(&desc, sg_out, sg_in.sgl, src_len);
|
|
if (ret < 0) {
|
|
pr_err("ceph_aes_decrypt failed %d\n", ret);
|
|
- return ret;
|
|
+ goto out_sg;
|
|
}
|
|
|
|
if (src_len <= *dst_len)
|
|
@@ -250,7 +337,12 @@ static int ceph_aes_decrypt(const void *key, int key_len,
|
|
print_hex_dump(KERN_ERR, "dec out: ", DUMP_PREFIX_NONE, 16, 1,
|
|
dst, *dst_len, 1);
|
|
*/
|
|
- return 0;
|
|
+
|
|
+out_sg:
|
|
+ teardown_sgtable(&sg_in);
|
|
+out_tfm:
|
|
+ crypto_free_blkcipher(tfm);
|
|
+ return ret;
|
|
}
|
|
|
|
static int ceph_aes_decrypt2(const void *key, int key_len,
|
|
@@ -258,7 +350,8 @@ static int ceph_aes_decrypt2(const void *key, int key_len,
|
|
void *dst2, size_t *dst2_len,
|
|
const void *src, size_t src_len)
|
|
{
|
|
- struct scatterlist sg_in[1], sg_out[3];
|
|
+ struct sg_table sg_in;
|
|
+ struct scatterlist sg_out[3], prealloc_sg;
|
|
struct crypto_blkcipher *tfm = ceph_crypto_alloc_cipher();
|
|
struct blkcipher_desc desc = { .tfm = tfm };
|
|
char pad[16];
|
|
@@ -270,17 +363,17 @@ static int ceph_aes_decrypt2(const void *key, int key_len,
|
|
if (IS_ERR(tfm))
|
|
return PTR_ERR(tfm);
|
|
|
|
- sg_init_table(sg_in, 1);
|
|
- sg_set_buf(sg_in, src, src_len);
|
|
sg_init_table(sg_out, 3);
|
|
sg_set_buf(&sg_out[0], dst1, *dst1_len);
|
|
sg_set_buf(&sg_out[1], dst2, *dst2_len);
|
|
sg_set_buf(&sg_out[2], pad, sizeof(pad));
|
|
+ ret = setup_sgtable(&sg_in, &prealloc_sg, src, src_len);
|
|
+ if (ret)
|
|
+ goto out_tfm;
|
|
|
|
crypto_blkcipher_setkey((void *)tfm, key, key_len);
|
|
iv = crypto_blkcipher_crt(tfm)->iv;
|
|
ivsize = crypto_blkcipher_ivsize(tfm);
|
|
-
|
|
memcpy(iv, aes_iv, ivsize);
|
|
|
|
/*
|
|
@@ -289,12 +382,10 @@ static int ceph_aes_decrypt2(const void *key, int key_len,
|
|
print_hex_dump(KERN_ERR, "dec in: ", DUMP_PREFIX_NONE, 16, 1,
|
|
src, src_len, 1);
|
|
*/
|
|
-
|
|
- ret = crypto_blkcipher_decrypt(&desc, sg_out, sg_in, src_len);
|
|
- crypto_free_blkcipher(tfm);
|
|
+ ret = crypto_blkcipher_decrypt(&desc, sg_out, sg_in.sgl, src_len);
|
|
if (ret < 0) {
|
|
pr_err("ceph_aes_decrypt failed %d\n", ret);
|
|
- return ret;
|
|
+ goto out_sg;
|
|
}
|
|
|
|
if (src_len <= *dst1_len)
|
|
@@ -324,7 +415,11 @@ static int ceph_aes_decrypt2(const void *key, int key_len,
|
|
dst2, *dst2_len, 1);
|
|
*/
|
|
|
|
- return 0;
|
|
+out_sg:
|
|
+ teardown_sgtable(&sg_in);
|
|
+out_tfm:
|
|
+ crypto_free_blkcipher(tfm);
|
|
+ return ret;
|
|
}
|
|
|
|
|
|
diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
|
|
index cb57aa862177..b27f6d34762b 100644
|
|
--- a/net/ipv6/ip6_gre.c
|
|
+++ b/net/ipv6/ip6_gre.c
|
|
@@ -962,8 +962,6 @@ static void ip6gre_tnl_link_config(struct ip6_tnl *t, int set_mtu)
|
|
else
|
|
dev->flags &= ~IFF_POINTOPOINT;
|
|
|
|
- dev->iflink = p->link;
|
|
-
|
|
/* Precalculate GRE options length */
|
|
if (t->parms.o_flags&(GRE_CSUM|GRE_KEY|GRE_SEQ)) {
|
|
if (t->parms.o_flags&GRE_CSUM)
|
|
@@ -1273,6 +1271,7 @@ static int ip6gre_tunnel_init(struct net_device *dev)
|
|
u64_stats_init(&ip6gre_tunnel_stats->syncp);
|
|
}
|
|
|
|
+ dev->iflink = tunnel->parms.link;
|
|
|
|
return 0;
|
|
}
|
|
@@ -1474,6 +1473,8 @@ static int ip6gre_tap_init(struct net_device *dev)
|
|
u64_stats_init(&ip6gre_tap_stats->syncp);
|
|
}
|
|
|
|
+ dev->iflink = tunnel->parms.link;
|
|
+
|
|
return 0;
|
|
}
|
|
|
|
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
|
|
index 912033957ad3..657639d39f70 100644
|
|
--- a/net/ipv6/ip6_tunnel.c
|
|
+++ b/net/ipv6/ip6_tunnel.c
|
|
@@ -272,9 +272,6 @@ static int ip6_tnl_create2(struct net_device *dev)
|
|
int err;
|
|
|
|
t = netdev_priv(dev);
|
|
- err = ip6_tnl_dev_init(dev);
|
|
- if (err < 0)
|
|
- goto out;
|
|
|
|
err = register_netdevice(dev);
|
|
if (err < 0)
|
|
@@ -1456,6 +1453,7 @@ ip6_tnl_change_mtu(struct net_device *dev, int new_mtu)
|
|
|
|
|
|
static const struct net_device_ops ip6_tnl_netdev_ops = {
|
|
+ .ndo_init = ip6_tnl_dev_init,
|
|
.ndo_uninit = ip6_tnl_dev_uninit,
|
|
.ndo_start_xmit = ip6_tnl_xmit,
|
|
.ndo_do_ioctl = ip6_tnl_ioctl,
|
|
@@ -1547,16 +1545,10 @@ static int __net_init ip6_fb_tnl_dev_init(struct net_device *dev)
|
|
struct ip6_tnl *t = netdev_priv(dev);
|
|
struct net *net = dev_net(dev);
|
|
struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id);
|
|
- int err = ip6_tnl_dev_init_gen(dev);
|
|
-
|
|
- if (err)
|
|
- return err;
|
|
|
|
t->parms.proto = IPPROTO_IPV6;
|
|
dev_hold(dev);
|
|
|
|
- ip6_tnl_link_config(t);
|
|
-
|
|
rcu_assign_pointer(ip6n->tnls_wc[0], t);
|
|
return 0;
|
|
}
|
|
diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c
|
|
index 2d19272b8cee..9a5339fcb450 100644
|
|
--- a/net/ipv6/ip6_vti.c
|
|
+++ b/net/ipv6/ip6_vti.c
|
|
@@ -172,10 +172,6 @@ static int vti6_tnl_create2(struct net_device *dev)
|
|
struct vti6_net *ip6n = net_generic(net, vti6_net_id);
|
|
int err;
|
|
|
|
- err = vti6_dev_init(dev);
|
|
- if (err < 0)
|
|
- goto out;
|
|
-
|
|
err = register_netdevice(dev);
|
|
if (err < 0)
|
|
goto out;
|
|
@@ -693,6 +689,7 @@ static int vti6_change_mtu(struct net_device *dev, int new_mtu)
|
|
}
|
|
|
|
static const struct net_device_ops vti6_netdev_ops = {
|
|
+ .ndo_init = vti6_dev_init,
|
|
.ndo_uninit = vti6_dev_uninit,
|
|
.ndo_start_xmit = vti6_tnl_xmit,
|
|
.ndo_do_ioctl = vti6_ioctl,
|
|
@@ -772,16 +769,10 @@ static int __net_init vti6_fb_tnl_dev_init(struct net_device *dev)
|
|
struct ip6_tnl *t = netdev_priv(dev);
|
|
struct net *net = dev_net(dev);
|
|
struct vti6_net *ip6n = net_generic(net, vti6_net_id);
|
|
- int err = vti6_dev_init_gen(dev);
|
|
-
|
|
- if (err)
|
|
- return err;
|
|
|
|
t->parms.proto = IPPROTO_IPV6;
|
|
dev_hold(dev);
|
|
|
|
- vti6_link_config(t);
|
|
-
|
|
rcu_assign_pointer(ip6n->tnls_wc[0], t);
|
|
return 0;
|
|
}
|
|
diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c
|
|
index b12b11b123ff..317b6dbf3190 100644
|
|
--- a/net/ipv6/sit.c
|
|
+++ b/net/ipv6/sit.c
|
|
@@ -195,10 +195,8 @@ static int ipip6_tunnel_create(struct net_device *dev)
|
|
struct sit_net *sitn = net_generic(net, sit_net_id);
|
|
int err;
|
|
|
|
- err = ipip6_tunnel_init(dev);
|
|
- if (err < 0)
|
|
- goto out;
|
|
- ipip6_tunnel_clone_6rd(dev, sitn);
|
|
+ memcpy(dev->dev_addr, &t->parms.iph.saddr, 4);
|
|
+ memcpy(dev->broadcast, &t->parms.iph.daddr, 4);
|
|
|
|
if ((__force u16)t->parms.i_flags & SIT_ISATAP)
|
|
dev->priv_flags |= IFF_ISATAP;
|
|
@@ -207,7 +205,8 @@ static int ipip6_tunnel_create(struct net_device *dev)
|
|
if (err < 0)
|
|
goto out;
|
|
|
|
- strcpy(t->parms.name, dev->name);
|
|
+ ipip6_tunnel_clone_6rd(dev, sitn);
|
|
+
|
|
dev->rtnl_link_ops = &sit_link_ops;
|
|
|
|
dev_hold(dev);
|
|
@@ -1321,6 +1320,7 @@ static int ipip6_tunnel_change_mtu(struct net_device *dev, int new_mtu)
|
|
}
|
|
|
|
static const struct net_device_ops ipip6_netdev_ops = {
|
|
+ .ndo_init = ipip6_tunnel_init,
|
|
.ndo_uninit = ipip6_tunnel_uninit,
|
|
.ndo_start_xmit = sit_tunnel_xmit,
|
|
.ndo_do_ioctl = ipip6_tunnel_ioctl,
|
|
@@ -1367,9 +1367,7 @@ static int ipip6_tunnel_init(struct net_device *dev)
|
|
|
|
tunnel->dev = dev;
|
|
tunnel->net = dev_net(dev);
|
|
-
|
|
- memcpy(dev->dev_addr, &tunnel->parms.iph.saddr, 4);
|
|
- memcpy(dev->broadcast, &tunnel->parms.iph.daddr, 4);
|
|
+ strcpy(tunnel->parms.name, dev->name);
|
|
|
|
ipip6_tunnel_bind_dev(dev);
|
|
dev->tstats = alloc_percpu(struct pcpu_sw_netstats);
|
|
@@ -1401,7 +1399,6 @@ static int __net_init ipip6_fb_tunnel_init(struct net_device *dev)
|
|
|
|
tunnel->dev = dev;
|
|
tunnel->net = dev_net(dev);
|
|
- strcpy(tunnel->parms.name, dev->name);
|
|
|
|
iph->version = 4;
|
|
iph->protocol = IPPROTO_IPV6;
|
|
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
|
|
index ea7013cb7e52..3f076b9c9308 100644
|
|
--- a/net/mac80211/ibss.c
|
|
+++ b/net/mac80211/ibss.c
|
|
@@ -815,7 +815,7 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
|
|
|
memset(¶ms, 0, sizeof(params));
|
|
memset(&csa_ie, 0, sizeof(csa_ie));
|
|
- err = ieee80211_parse_ch_switch_ie(sdata, elems, beacon,
|
|
+ err = ieee80211_parse_ch_switch_ie(sdata, elems,
|
|
ifibss->chandef.chan->band,
|
|
sta_flags, ifibss->bssid, &csa_ie);
|
|
/* can't switch to destination channel, fail */
|
|
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
|
|
index b127902361f4..bf7a1bbb975f 100644
|
|
--- a/net/mac80211/ieee80211_i.h
|
|
+++ b/net/mac80211/ieee80211_i.h
|
|
@@ -1569,7 +1569,6 @@ void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
|
|
* ieee80211_parse_ch_switch_ie - parses channel switch IEs
|
|
* @sdata: the sdata of the interface which has received the frame
|
|
* @elems: parsed 802.11 elements received with the frame
|
|
- * @beacon: indicates if the frame was a beacon or probe response
|
|
* @current_band: indicates the current band
|
|
* @sta_flags: contains information about own capabilities and restrictions
|
|
* to decide which channel switch announcements can be accepted. Only the
|
|
@@ -1583,7 +1582,7 @@ void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
|
|
* Return: 0 on success, <0 on error and >0 if there is nothing to parse.
|
|
*/
|
|
int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
|
|
- struct ieee802_11_elems *elems, bool beacon,
|
|
+ struct ieee802_11_elems *elems,
|
|
enum ieee80211_band current_band,
|
|
u32 sta_flags, u8 *bssid,
|
|
struct ieee80211_csa_ie *csa_ie);
|
|
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
|
|
index 8f7fabc46c97..06f5de4e4fbb 100644
|
|
--- a/net/mac80211/iface.c
|
|
+++ b/net/mac80211/iface.c
|
|
@@ -760,10 +760,12 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
|
|
int i, flushed;
|
|
struct ps_data *ps;
|
|
struct cfg80211_chan_def chandef;
|
|
+ bool cancel_scan;
|
|
|
|
clear_bit(SDATA_STATE_RUNNING, &sdata->state);
|
|
|
|
- if (rcu_access_pointer(local->scan_sdata) == sdata)
|
|
+ cancel_scan = rcu_access_pointer(local->scan_sdata) == sdata;
|
|
+ if (cancel_scan)
|
|
ieee80211_scan_cancel(local);
|
|
|
|
/*
|
|
@@ -973,6 +975,9 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
|
|
|
|
ieee80211_recalc_ps(local, -1);
|
|
|
|
+ if (cancel_scan)
|
|
+ flush_delayed_work(&local->scan_work);
|
|
+
|
|
if (local->open_count == 0) {
|
|
ieee80211_stop_device(local);
|
|
|
|
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
|
|
index 5b919cab1de0..3d52d1d68431 100644
|
|
--- a/net/mac80211/mesh.c
|
|
+++ b/net/mac80211/mesh.c
|
|
@@ -885,7 +885,7 @@ ieee80211_mesh_process_chnswitch(struct ieee80211_sub_if_data *sdata,
|
|
|
|
memset(¶ms, 0, sizeof(params));
|
|
memset(&csa_ie, 0, sizeof(csa_ie));
|
|
- err = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, band,
|
|
+ err = ieee80211_parse_ch_switch_ie(sdata, elems, band,
|
|
sta_flags, sdata->vif.addr,
|
|
&csa_ie);
|
|
if (err < 0)
|
|
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
|
|
index 189eef014c4f..c9535a976b56 100644
|
|
--- a/net/mac80211/mlme.c
|
|
+++ b/net/mac80211/mlme.c
|
|
@@ -1001,7 +1001,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
|
|
|
current_band = cbss->channel->band;
|
|
memset(&csa_ie, 0, sizeof(csa_ie));
|
|
- res = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, current_band,
|
|
+ res = ieee80211_parse_ch_switch_ie(sdata, elems, current_band,
|
|
ifmgd->flags,
|
|
ifmgd->associated->bssid, &csa_ie);
|
|
if (res < 0)
|
|
@@ -1086,7 +1086,8 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
|
ieee80211_queue_work(&local->hw, &ifmgd->chswitch_work);
|
|
else
|
|
mod_timer(&ifmgd->chswitch_timer,
|
|
- TU_TO_EXP_TIME(csa_ie.count * cbss->beacon_interval));
|
|
+ TU_TO_EXP_TIME((csa_ie.count - 1) *
|
|
+ cbss->beacon_interval));
|
|
}
|
|
|
|
static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
|
|
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
|
|
index 3e57f96c9666..095c16037bc5 100644
|
|
--- a/net/mac80211/rx.c
|
|
+++ b/net/mac80211/rx.c
|
|
@@ -1679,11 +1679,14 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
|
|
sc = le16_to_cpu(hdr->seq_ctrl);
|
|
frag = sc & IEEE80211_SCTL_FRAG;
|
|
|
|
- if (likely((!ieee80211_has_morefrags(fc) && frag == 0) ||
|
|
- is_multicast_ether_addr(hdr->addr1))) {
|
|
- /* not fragmented */
|
|
+ if (likely(!ieee80211_has_morefrags(fc) && frag == 0))
|
|
+ goto out;
|
|
+
|
|
+ if (is_multicast_ether_addr(hdr->addr1)) {
|
|
+ rx->local->dot11MulticastReceivedFrameCount++;
|
|
goto out;
|
|
}
|
|
+
|
|
I802_DEBUG_INC(rx->local->rx_handlers_fragments);
|
|
|
|
if (skb_linearize(rx->skb))
|
|
@@ -1776,10 +1779,7 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
|
|
out:
|
|
if (rx->sta)
|
|
rx->sta->rx_packets++;
|
|
- if (is_multicast_ether_addr(hdr->addr1))
|
|
- rx->local->dot11MulticastReceivedFrameCount++;
|
|
- else
|
|
- ieee80211_led_rx(rx->local);
|
|
+ ieee80211_led_rx(rx->local);
|
|
return RX_CONTINUE;
|
|
}
|
|
|
|
diff --git a/net/mac80211/spectmgmt.c b/net/mac80211/spectmgmt.c
|
|
index 6ab009070084..efeba56c913b 100644
|
|
--- a/net/mac80211/spectmgmt.c
|
|
+++ b/net/mac80211/spectmgmt.c
|
|
@@ -22,7 +22,7 @@
|
|
#include "wme.h"
|
|
|
|
int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
|
|
- struct ieee802_11_elems *elems, bool beacon,
|
|
+ struct ieee802_11_elems *elems,
|
|
enum ieee80211_band current_band,
|
|
u32 sta_flags, u8 *bssid,
|
|
struct ieee80211_csa_ie *csa_ie)
|
|
@@ -91,19 +91,13 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
|
|
return -EINVAL;
|
|
}
|
|
|
|
- if (!beacon && sec_chan_offs) {
|
|
+ if (sec_chan_offs) {
|
|
secondary_channel_offset = sec_chan_offs->sec_chan_offs;
|
|
- } else if (beacon && ht_oper) {
|
|
- secondary_channel_offset =
|
|
- ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET;
|
|
} else if (!(sta_flags & IEEE80211_STA_DISABLE_HT)) {
|
|
- /* If it's not a beacon, HT is enabled and the IE not present,
|
|
- * it's 20 MHz, 802.11-2012 8.5.2.6:
|
|
- * This element [the Secondary Channel Offset Element] is
|
|
- * present when switching to a 40 MHz channel. It may be
|
|
- * present when switching to a 20 MHz channel (in which
|
|
- * case the secondary channel offset is set to SCN).
|
|
- */
|
|
+ /* If the secondary channel offset IE is not present,
|
|
+ * we can't know what's the post-CSA offset, so the
|
|
+ * best we can do is use 20MHz.
|
|
+ */
|
|
secondary_channel_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
|
|
}
|
|
|
|
diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c
|
|
index de770ec39e51..cf9937743abb 100644
|
|
--- a/net/netfilter/ipset/ip_set_core.c
|
|
+++ b/net/netfilter/ipset/ip_set_core.c
|
|
@@ -636,7 +636,7 @@ ip_set_nfnl_get_byindex(struct net *net, ip_set_id_t index)
|
|
struct ip_set *set;
|
|
struct ip_set_net *inst = ip_set_pernet(net);
|
|
|
|
- if (index > inst->ip_set_max)
|
|
+ if (index >= inst->ip_set_max)
|
|
return IPSET_INVALID_ID;
|
|
|
|
nfnl_lock(NFNL_SUBSYS_IPSET);
|
|
diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c
|
|
index a155d19a225e..6ff12a191400 100644
|
|
--- a/net/netfilter/nfnetlink_log.c
|
|
+++ b/net/netfilter/nfnetlink_log.c
|
|
@@ -45,7 +45,8 @@
|
|
#define NFULNL_NLBUFSIZ_DEFAULT NLMSG_GOODSIZE
|
|
#define NFULNL_TIMEOUT_DEFAULT 100 /* every second */
|
|
#define NFULNL_QTHRESH_DEFAULT 100 /* 100 packets */
|
|
-#define NFULNL_COPY_RANGE_MAX 0xFFFF /* max packet size is limited by 16-bit struct nfattr nfa_len field */
|
|
+/* max packet size is limited by 16-bit struct nfattr nfa_len field */
|
|
+#define NFULNL_COPY_RANGE_MAX (0xFFFF - NLA_HDRLEN)
|
|
|
|
#define PRINTR(x, args...) do { if (net_ratelimit()) \
|
|
printk(x, ## args); } while (0);
|
|
@@ -255,6 +256,8 @@ nfulnl_set_mode(struct nfulnl_instance *inst, u_int8_t mode,
|
|
|
|
case NFULNL_COPY_PACKET:
|
|
inst->copy_mode = mode;
|
|
+ if (range == 0)
|
|
+ range = NFULNL_COPY_RANGE_MAX;
|
|
inst->copy_range = min_t(unsigned int,
|
|
range, NFULNL_COPY_RANGE_MAX);
|
|
break;
|
|
@@ -346,26 +349,25 @@ nfulnl_alloc_skb(struct net *net, u32 peer_portid, unsigned int inst_size,
|
|
return skb;
|
|
}
|
|
|
|
-static int
|
|
+static void
|
|
__nfulnl_send(struct nfulnl_instance *inst)
|
|
{
|
|
- int status = -1;
|
|
-
|
|
if (inst->qlen > 1) {
|
|
struct nlmsghdr *nlh = nlmsg_put(inst->skb, 0, 0,
|
|
NLMSG_DONE,
|
|
sizeof(struct nfgenmsg),
|
|
0);
|
|
- if (!nlh)
|
|
+ if (WARN_ONCE(!nlh, "bad nlskb size: %u, tailroom %d\n",
|
|
+ inst->skb->len, skb_tailroom(inst->skb))) {
|
|
+ kfree_skb(inst->skb);
|
|
goto out;
|
|
+ }
|
|
}
|
|
- status = nfnetlink_unicast(inst->skb, inst->net, inst->peer_portid,
|
|
- MSG_DONTWAIT);
|
|
-
|
|
+ nfnetlink_unicast(inst->skb, inst->net, inst->peer_portid,
|
|
+ MSG_DONTWAIT);
|
|
+out:
|
|
inst->qlen = 0;
|
|
inst->skb = NULL;
|
|
-out:
|
|
- return status;
|
|
}
|
|
|
|
static void
|
|
@@ -652,7 +654,8 @@ nfulnl_log_packet(struct net *net,
|
|
+ nla_total_size(sizeof(u_int32_t)) /* gid */
|
|
+ nla_total_size(plen) /* prefix */
|
|
+ nla_total_size(sizeof(struct nfulnl_msg_packet_hw))
|
|
- + nla_total_size(sizeof(struct nfulnl_msg_packet_timestamp));
|
|
+ + nla_total_size(sizeof(struct nfulnl_msg_packet_timestamp))
|
|
+ + nla_total_size(sizeof(struct nfgenmsg)); /* NLMSG_DONE */
|
|
|
|
if (in && skb_mac_header_was_set(skb)) {
|
|
size += nla_total_size(skb->dev->hard_header_len)
|
|
@@ -681,8 +684,7 @@ nfulnl_log_packet(struct net *net,
|
|
break;
|
|
|
|
case NFULNL_COPY_PACKET:
|
|
- if (inst->copy_range == 0
|
|
- || inst->copy_range > skb->len)
|
|
+ if (inst->copy_range > skb->len)
|
|
data_len = skb->len;
|
|
else
|
|
data_len = inst->copy_range;
|
|
@@ -695,8 +697,7 @@ nfulnl_log_packet(struct net *net,
|
|
goto unlock_and_release;
|
|
}
|
|
|
|
- if (inst->skb &&
|
|
- size > skb_tailroom(inst->skb) - sizeof(struct nfgenmsg)) {
|
|
+ if (inst->skb && size > skb_tailroom(inst->skb)) {
|
|
/* either the queue len is too high or we don't have
|
|
* enough room in the skb left. flush to userspace. */
|
|
__nfulnl_flush(inst);
|
|
diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c
|
|
index 82cb8236f8a1..ad979612238a 100644
|
|
--- a/net/netfilter/nft_compat.c
|
|
+++ b/net/netfilter/nft_compat.c
|
|
@@ -678,7 +678,7 @@ nft_target_select_ops(const struct nft_ctx *ctx,
|
|
family = ctx->afi->family;
|
|
|
|
/* Re-use the existing target if it's already loaded. */
|
|
- list_for_each_entry(nft_target, &nft_match_list, head) {
|
|
+ list_for_each_entry(nft_target, &nft_target_list, head) {
|
|
struct xt_target *target = nft_target->ops.data;
|
|
|
|
if (strcmp(target->name, tg_name) == 0 &&
|
|
diff --git a/net/sctp/associola.c b/net/sctp/associola.c
|
|
index 5d97d8fe4be7..d477d476714d 100644
|
|
--- a/net/sctp/associola.c
|
|
+++ b/net/sctp/associola.c
|
|
@@ -1627,6 +1627,8 @@ struct sctp_chunk *sctp_assoc_lookup_asconf_ack(
|
|
* ack chunk whose serial number matches that of the request.
|
|
*/
|
|
list_for_each_entry(ack, &asoc->asconf_ack_list, transmitted_list) {
|
|
+ if (sctp_chunk_pending(ack))
|
|
+ continue;
|
|
if (ack->subh.addip_hdr->serial == serial) {
|
|
sctp_chunk_hold(ack);
|
|
return ack;
|
|
diff --git a/net/sctp/auth.c b/net/sctp/auth.c
|
|
index 0e8529113dc5..fb7976aee61c 100644
|
|
--- a/net/sctp/auth.c
|
|
+++ b/net/sctp/auth.c
|
|
@@ -862,8 +862,6 @@ int sctp_auth_set_key(struct sctp_endpoint *ep,
|
|
list_add(&cur_key->key_list, sh_keys);
|
|
|
|
cur_key->key = key;
|
|
- sctp_auth_key_hold(key);
|
|
-
|
|
return 0;
|
|
nomem:
|
|
if (!replace)
|
|
diff --git a/net/sctp/inqueue.c b/net/sctp/inqueue.c
|
|
index 4de12afa13d4..7e8a16c77039 100644
|
|
--- a/net/sctp/inqueue.c
|
|
+++ b/net/sctp/inqueue.c
|
|
@@ -140,18 +140,9 @@ struct sctp_chunk *sctp_inq_pop(struct sctp_inq *queue)
|
|
} else {
|
|
/* Nothing to do. Next chunk in the packet, please. */
|
|
ch = (sctp_chunkhdr_t *) chunk->chunk_end;
|
|
-
|
|
/* Force chunk->skb->data to chunk->chunk_end. */
|
|
- skb_pull(chunk->skb,
|
|
- chunk->chunk_end - chunk->skb->data);
|
|
-
|
|
- /* Verify that we have at least chunk headers
|
|
- * worth of buffer left.
|
|
- */
|
|
- if (skb_headlen(chunk->skb) < sizeof(sctp_chunkhdr_t)) {
|
|
- sctp_chunk_free(chunk);
|
|
- chunk = queue->in_progress = NULL;
|
|
- }
|
|
+ skb_pull(chunk->skb, chunk->chunk_end - chunk->skb->data);
|
|
+ /* We are guaranteed to pull a SCTP header. */
|
|
}
|
|
}
|
|
|
|
@@ -187,24 +178,14 @@ struct sctp_chunk *sctp_inq_pop(struct sctp_inq *queue)
|
|
skb_pull(chunk->skb, sizeof(sctp_chunkhdr_t));
|
|
chunk->subh.v = NULL; /* Subheader is no longer valid. */
|
|
|
|
- if (chunk->chunk_end < skb_tail_pointer(chunk->skb)) {
|
|
+ if (chunk->chunk_end + sizeof(sctp_chunkhdr_t) <
|
|
+ skb_tail_pointer(chunk->skb)) {
|
|
/* This is not a singleton */
|
|
chunk->singleton = 0;
|
|
} else if (chunk->chunk_end > skb_tail_pointer(chunk->skb)) {
|
|
- /* RFC 2960, Section 6.10 Bundling
|
|
- *
|
|
- * Partial chunks MUST NOT be placed in an SCTP packet.
|
|
- * If the receiver detects a partial chunk, it MUST drop
|
|
- * the chunk.
|
|
- *
|
|
- * Since the end of the chunk is past the end of our buffer
|
|
- * (which contains the whole packet, we can freely discard
|
|
- * the whole packet.
|
|
- */
|
|
- sctp_chunk_free(chunk);
|
|
- chunk = queue->in_progress = NULL;
|
|
-
|
|
- return NULL;
|
|
+ /* Discard inside state machine. */
|
|
+ chunk->pdiscard = 1;
|
|
+ chunk->chunk_end = skb_tail_pointer(chunk->skb);
|
|
} else {
|
|
/* We are at the end of the packet, so mark the chunk
|
|
* in case we need to send a SACK.
|
|
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c
|
|
index fee5552ddf92..43abb643f3a1 100644
|
|
--- a/net/sctp/sm_make_chunk.c
|
|
+++ b/net/sctp/sm_make_chunk.c
|
|
@@ -2609,6 +2609,9 @@ do_addr_param:
|
|
addr_param = param.v + sizeof(sctp_addip_param_t);
|
|
|
|
af = sctp_get_af_specific(param_type2af(param.p->type));
|
|
+ if (af == NULL)
|
|
+ break;
|
|
+
|
|
af->from_addr_param(&addr, addr_param,
|
|
htons(asoc->peer.port), 0);
|
|
|
|
@@ -3110,50 +3113,63 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc,
|
|
return SCTP_ERROR_NO_ERROR;
|
|
}
|
|
|
|
-/* Verify the ASCONF packet before we process it. */
|
|
-int sctp_verify_asconf(const struct sctp_association *asoc,
|
|
- struct sctp_paramhdr *param_hdr, void *chunk_end,
|
|
- struct sctp_paramhdr **errp) {
|
|
- sctp_addip_param_t *asconf_param;
|
|
+/* Verify the ASCONF packet before we process it. */
|
|
+bool sctp_verify_asconf(const struct sctp_association *asoc,
|
|
+ struct sctp_chunk *chunk, bool addr_param_needed,
|
|
+ struct sctp_paramhdr **errp)
|
|
+{
|
|
+ sctp_addip_chunk_t *addip = (sctp_addip_chunk_t *) chunk->chunk_hdr;
|
|
union sctp_params param;
|
|
- int length, plen;
|
|
+ bool addr_param_seen = false;
|
|
|
|
- param.v = (sctp_paramhdr_t *) param_hdr;
|
|
- while (param.v <= chunk_end - sizeof(sctp_paramhdr_t)) {
|
|
- length = ntohs(param.p->length);
|
|
- *errp = param.p;
|
|
-
|
|
- if (param.v > chunk_end - length ||
|
|
- length < sizeof(sctp_paramhdr_t))
|
|
- return 0;
|
|
+ sctp_walk_params(param, addip, addip_hdr.params) {
|
|
+ size_t length = ntohs(param.p->length);
|
|
|
|
+ *errp = param.p;
|
|
switch (param.p->type) {
|
|
+ case SCTP_PARAM_ERR_CAUSE:
|
|
+ break;
|
|
+ case SCTP_PARAM_IPV4_ADDRESS:
|
|
+ if (length != sizeof(sctp_ipv4addr_param_t))
|
|
+ return false;
|
|
+ addr_param_seen = true;
|
|
+ break;
|
|
+ case SCTP_PARAM_IPV6_ADDRESS:
|
|
+ if (length != sizeof(sctp_ipv6addr_param_t))
|
|
+ return false;
|
|
+ addr_param_seen = true;
|
|
+ break;
|
|
case SCTP_PARAM_ADD_IP:
|
|
case SCTP_PARAM_DEL_IP:
|
|
case SCTP_PARAM_SET_PRIMARY:
|
|
- asconf_param = (sctp_addip_param_t *)param.v;
|
|
- plen = ntohs(asconf_param->param_hdr.length);
|
|
- if (plen < sizeof(sctp_addip_param_t) +
|
|
- sizeof(sctp_paramhdr_t))
|
|
- return 0;
|
|
+ /* In ASCONF chunks, these need to be first. */
|
|
+ if (addr_param_needed && !addr_param_seen)
|
|
+ return false;
|
|
+ length = ntohs(param.addip->param_hdr.length);
|
|
+ if (length < sizeof(sctp_addip_param_t) +
|
|
+ sizeof(sctp_paramhdr_t))
|
|
+ return false;
|
|
break;
|
|
case SCTP_PARAM_SUCCESS_REPORT:
|
|
case SCTP_PARAM_ADAPTATION_LAYER_IND:
|
|
if (length != sizeof(sctp_addip_param_t))
|
|
- return 0;
|
|
-
|
|
+ return false;
|
|
break;
|
|
default:
|
|
- break;
|
|
+ /* This is unkown to us, reject! */
|
|
+ return false;
|
|
}
|
|
-
|
|
- param.v += WORD_ROUND(length);
|
|
}
|
|
|
|
- if (param.v != chunk_end)
|
|
- return 0;
|
|
+ /* Remaining sanity checks. */
|
|
+ if (addr_param_needed && !addr_param_seen)
|
|
+ return false;
|
|
+ if (!addr_param_needed && addr_param_seen)
|
|
+ return false;
|
|
+ if (param.v != chunk->chunk_end)
|
|
+ return false;
|
|
|
|
- return 1;
|
|
+ return true;
|
|
}
|
|
|
|
/* Process an incoming ASCONF chunk with the next expected serial no. and
|
|
@@ -3162,16 +3178,17 @@ int sctp_verify_asconf(const struct sctp_association *asoc,
|
|
struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc,
|
|
struct sctp_chunk *asconf)
|
|
{
|
|
+ sctp_addip_chunk_t *addip = (sctp_addip_chunk_t *) asconf->chunk_hdr;
|
|
+ bool all_param_pass = true;
|
|
+ union sctp_params param;
|
|
sctp_addiphdr_t *hdr;
|
|
union sctp_addr_param *addr_param;
|
|
sctp_addip_param_t *asconf_param;
|
|
struct sctp_chunk *asconf_ack;
|
|
-
|
|
__be16 err_code;
|
|
int length = 0;
|
|
int chunk_len;
|
|
__u32 serial;
|
|
- int all_param_pass = 1;
|
|
|
|
chunk_len = ntohs(asconf->chunk_hdr->length) - sizeof(sctp_chunkhdr_t);
|
|
hdr = (sctp_addiphdr_t *)asconf->skb->data;
|
|
@@ -3199,9 +3216,14 @@ struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc,
|
|
goto done;
|
|
|
|
/* Process the TLVs contained within the ASCONF chunk. */
|
|
- while (chunk_len > 0) {
|
|
+ sctp_walk_params(param, addip, addip_hdr.params) {
|
|
+ /* Skip preceeding address parameters. */
|
|
+ if (param.p->type == SCTP_PARAM_IPV4_ADDRESS ||
|
|
+ param.p->type == SCTP_PARAM_IPV6_ADDRESS)
|
|
+ continue;
|
|
+
|
|
err_code = sctp_process_asconf_param(asoc, asconf,
|
|
- asconf_param);
|
|
+ param.addip);
|
|
/* ADDIP 4.1 A7)
|
|
* If an error response is received for a TLV parameter,
|
|
* all TLVs with no response before the failed TLV are
|
|
@@ -3209,28 +3231,20 @@ struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc,
|
|
* the failed response are considered unsuccessful unless
|
|
* a specific success indication is present for the parameter.
|
|
*/
|
|
- if (SCTP_ERROR_NO_ERROR != err_code)
|
|
- all_param_pass = 0;
|
|
-
|
|
+ if (err_code != SCTP_ERROR_NO_ERROR)
|
|
+ all_param_pass = false;
|
|
if (!all_param_pass)
|
|
- sctp_add_asconf_response(asconf_ack,
|
|
- asconf_param->crr_id, err_code,
|
|
- asconf_param);
|
|
+ sctp_add_asconf_response(asconf_ack, param.addip->crr_id,
|
|
+ err_code, param.addip);
|
|
|
|
/* ADDIP 4.3 D11) When an endpoint receiving an ASCONF to add
|
|
* an IP address sends an 'Out of Resource' in its response, it
|
|
* MUST also fail any subsequent add or delete requests bundled
|
|
* in the ASCONF.
|
|
*/
|
|
- if (SCTP_ERROR_RSRC_LOW == err_code)
|
|
+ if (err_code == SCTP_ERROR_RSRC_LOW)
|
|
goto done;
|
|
-
|
|
- /* Move to the next ASCONF param. */
|
|
- length = ntohs(asconf_param->param_hdr.length);
|
|
- asconf_param = (void *)asconf_param + length;
|
|
- chunk_len -= length;
|
|
}
|
|
-
|
|
done:
|
|
asoc->peer.addip_serial++;
|
|
|
|
diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
|
|
index 7194fe8589b0..3e287a3fa03b 100644
|
|
--- a/net/sctp/sm_statefuns.c
|
|
+++ b/net/sctp/sm_statefuns.c
|
|
@@ -170,6 +170,9 @@ sctp_chunk_length_valid(struct sctp_chunk *chunk,
|
|
{
|
|
__u16 chunk_length = ntohs(chunk->chunk_hdr->length);
|
|
|
|
+ /* Previously already marked? */
|
|
+ if (unlikely(chunk->pdiscard))
|
|
+ return 0;
|
|
if (unlikely(chunk_length < required_length))
|
|
return 0;
|
|
|
|
@@ -3591,9 +3594,7 @@ sctp_disposition_t sctp_sf_do_asconf(struct net *net,
|
|
struct sctp_chunk *asconf_ack = NULL;
|
|
struct sctp_paramhdr *err_param = NULL;
|
|
sctp_addiphdr_t *hdr;
|
|
- union sctp_addr_param *addr_param;
|
|
__u32 serial;
|
|
- int length;
|
|
|
|
if (!sctp_vtag_verify(chunk, asoc)) {
|
|
sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_BAD_TAG,
|
|
@@ -3618,17 +3619,8 @@ sctp_disposition_t sctp_sf_do_asconf(struct net *net,
|
|
hdr = (sctp_addiphdr_t *)chunk->skb->data;
|
|
serial = ntohl(hdr->serial);
|
|
|
|
- addr_param = (union sctp_addr_param *)hdr->params;
|
|
- length = ntohs(addr_param->p.length);
|
|
- if (length < sizeof(sctp_paramhdr_t))
|
|
- return sctp_sf_violation_paramlen(net, ep, asoc, type, arg,
|
|
- (void *)addr_param, commands);
|
|
-
|
|
/* Verify the ASCONF chunk before processing it. */
|
|
- if (!sctp_verify_asconf(asoc,
|
|
- (sctp_paramhdr_t *)((void *)addr_param + length),
|
|
- (void *)chunk->chunk_end,
|
|
- &err_param))
|
|
+ if (!sctp_verify_asconf(asoc, chunk, true, &err_param))
|
|
return sctp_sf_violation_paramlen(net, ep, asoc, type, arg,
|
|
(void *)err_param, commands);
|
|
|
|
@@ -3745,10 +3737,7 @@ sctp_disposition_t sctp_sf_do_asconf_ack(struct net *net,
|
|
rcvd_serial = ntohl(addip_hdr->serial);
|
|
|
|
/* Verify the ASCONF-ACK chunk before processing it. */
|
|
- if (!sctp_verify_asconf(asoc,
|
|
- (sctp_paramhdr_t *)addip_hdr->params,
|
|
- (void *)asconf_ack->chunk_end,
|
|
- &err_param))
|
|
+ if (!sctp_verify_asconf(asoc, asconf_ack, false, &err_param))
|
|
return sctp_sf_violation_paramlen(net, ep, asoc, type, arg,
|
|
(void *)err_param, commands);
|
|
|
|
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
|
|
index f4b12c216f1c..5a723df670b4 100644
|
|
--- a/sound/usb/mixer_quirks.c
|
|
+++ b/sound/usb/mixer_quirks.c
|
|
@@ -885,6 +885,11 @@ static int snd_ftu_eff_switch_put(struct snd_kcontrol *kctl,
|
|
return changed;
|
|
}
|
|
|
|
+static void kctl_private_value_free(struct snd_kcontrol *kctl)
|
|
+{
|
|
+ kfree((void *)kctl->private_value);
|
|
+}
|
|
+
|
|
static int snd_ftu_create_effect_switch(struct usb_mixer_interface *mixer,
|
|
int validx, int bUnitID)
|
|
{
|
|
@@ -919,6 +924,7 @@ static int snd_ftu_create_effect_switch(struct usb_mixer_interface *mixer,
|
|
return -ENOMEM;
|
|
}
|
|
|
|
+ kctl->private_free = kctl_private_value_free;
|
|
err = snd_ctl_add(mixer->chip->card, kctl);
|
|
if (err < 0)
|
|
return err;
|