Merge branch 'akpm' (aka "Andrew's patch-bomb, take two")
Andrew explains: - various misc stuff - Most of the rest of MM: memcg, threaded hugepages, others. - cpumask - kexec - kdump - some direct-io performance tweaking - radix-tree optimisations - new selftests code A note on this: often people will develop a new userspace-visible feature and will develop userspace code to exercise/test that feature. Then they merge the patch and the selftest code dies. Sometimes we paste it into the changelog. Sometimes the code gets thrown into Documentation/(!). This saddens me. So this patch creates a bare-bones framework which will henceforth allow me to ask people to include their test apps in the kernel tree so we can keep them alive. Then when people enhance or fix the feature, I can ask them to update the test app too. The infrastruture is terribly trivial at present - let's see how it evolves. - checkpoint/restart feature work. A note on this: this is a project by various mad Russians to perform c/r mainly from userspace, with various oddball helper code added into the kernel where the need is demonstrated. So rather than some large central lump of code, what we have is little bits and pieces popping up in various places which either expose something new or which permit something which is normally kernel-private to be modified. The overall project is an ongoing thing. I've judged that the size and scope of the thing means that we're more likely to be successful with it if we integrate the support into mainline piecemeal rather than allowing it all to develop out-of-tree. However I'm less confident than the developers that it will all eventually work! So what I'm asking them to do is to wrap each piece of new code inside CONFIG_CHECKPOINT_RESTORE. So if it all eventually comes to tears and the project as a whole fails, it should be a simple matter to go through and delete all trace of it. This lot pretty much wraps up the -rc1 merge for me. * akpm: (96 commits) unlzo: fix input buffer free ramoops: update parameters only after successful init ramoops: fix use of rounddown_pow_of_two() c/r: prctl: add PR_SET_MM codes to set up mm_struct entries c/r: procfs: add start_data, end_data, start_brk members to /proc/$pid/stat v4 c/r: introduce CHECKPOINT_RESTORE symbol selftests: new x86 breakpoints selftest selftests: new very basic kernel selftests directory radix_tree: take radix_tree_path off stack radix_tree: remove radix_tree_indirect_to_ptr() dio: optimize cache misses in the submission path vfs: cache request_queue in struct block_device fs/direct-io.c: calculate fs_count correctly in get_more_blocks() drivers/parport/parport_pc.c: fix warnings panic: don't print redundant backtraces on oops sysctl: add the kernel.ns_last_pid control kdump: add udev events for memory online/offline include/linux/crash_dump.h needs elf.h kdump: fix crash_kexec()/smp_send_stop() race in panic() kdump: crashk_res init check for /sys/kernel/kexec_crash_size ...
This commit is contained in:
commit
099469502f
@ -346,6 +346,10 @@ Description:
|
||||
number of objects per slab. If a slab cannot be allocated
|
||||
because of fragmentation, SLUB will retry with the minimum order
|
||||
possible depending on its characteristics.
|
||||
When debug_guardpage_minorder=N (N > 0) parameter is specified
|
||||
(see Documentation/kernel-parameters.txt), the minimum possible
|
||||
order is used and this sysfs entry can not be used to change
|
||||
the order at run time.
|
||||
|
||||
What: /sys/kernel/slab/cache/order_fallback
|
||||
Date: April 2008
|
||||
|
@ -61,7 +61,7 @@ Brief summary of control files.
|
||||
memory.failcnt # show the number of memory usage hits limits
|
||||
memory.memsw.failcnt # show the number of memory+Swap hits limits
|
||||
memory.max_usage_in_bytes # show max memory usage recorded
|
||||
memory.memsw.usage_in_bytes # show max memory+Swap usage recorded
|
||||
memory.memsw.max_usage_in_bytes # show max memory+Swap usage recorded
|
||||
memory.soft_limit_in_bytes # set/show soft limit of memory usage
|
||||
memory.stat # show various statistics
|
||||
memory.use_hierarchy # set/show hierarchical account enabled
|
||||
@ -410,8 +410,11 @@ memory.stat file includes following statistics
|
||||
cache - # of bytes of page cache memory.
|
||||
rss - # of bytes of anonymous and swap cache memory.
|
||||
mapped_file - # of bytes of mapped file (includes tmpfs/shmem)
|
||||
pgpgin - # of pages paged in (equivalent to # of charging events).
|
||||
pgpgout - # of pages paged out (equivalent to # of uncharging events).
|
||||
pgpgin - # of charging events to the memory cgroup. The charging
|
||||
event happens each time a page is accounted as either mapped
|
||||
anon page(RSS) or cache page(Page Cache) to the cgroup.
|
||||
pgpgout - # of uncharging events to the memory cgroup. The uncharging
|
||||
event happens each time a page is unaccounted from the cgroup.
|
||||
swap - # of bytes of swap usage
|
||||
inactive_anon - # of bytes of anonymous memory and swap cache memory on
|
||||
LRU list.
|
||||
|
@ -307,6 +307,9 @@ Table 1-4: Contents of the stat files (as of 2.6.30-rc7)
|
||||
blkio_ticks time spent waiting for block IO
|
||||
gtime guest time of the task in jiffies
|
||||
cgtime guest time of the task children in jiffies
|
||||
start_data address above which program data+bss is placed
|
||||
end_data address below which program data+bss is placed
|
||||
start_brk address above which program heap can be expanded with brk()
|
||||
..............................................................................
|
||||
|
||||
The /proc/PID/maps file containing the currently mapped memory regions and
|
||||
|
@ -415,6 +415,14 @@ PIDs of value pid_max or larger are not allocated.
|
||||
|
||||
==============================================================
|
||||
|
||||
ns_last_pid:
|
||||
|
||||
The last pid allocated in the current (the one task using this sysctl
|
||||
lives in) pid namespace. When selecting a pid for a next task on fork
|
||||
kernel tries to allocate a number starting from this one.
|
||||
|
||||
==============================================================
|
||||
|
||||
powersave-nap: (PPC only)
|
||||
|
||||
If set, Linux-PPC will use the 'nap' mode of powersaving,
|
||||
|
@ -131,7 +131,10 @@ slub_min_objects.
|
||||
slub_max_order specified the order at which slub_min_objects should no
|
||||
longer be checked. This is useful to avoid SLUB trying to generate
|
||||
super large order pages to fit slub_min_objects of a slab cache with
|
||||
large object sizes into one high order page.
|
||||
large object sizes into one high order page. Setting command line
|
||||
parameter debug_guardpage_minorder=N (N > 0), forces setting
|
||||
slub_max_order to 0, what cause minimum possible order of slabs
|
||||
allocation.
|
||||
|
||||
SLUB Debug output
|
||||
-----------------
|
||||
|
14
arch/Kconfig
14
arch/Kconfig
@ -185,4 +185,18 @@ config HAVE_RCU_TABLE_FREE
|
||||
config ARCH_HAVE_NMI_SAFE_CMPXCHG
|
||||
bool
|
||||
|
||||
config HAVE_ALIGNED_STRUCT_PAGE
|
||||
bool
|
||||
help
|
||||
This makes sure that struct pages are double word aligned and that
|
||||
e.g. the SLUB allocator can perform double word atomic operations
|
||||
on a struct page for better performance. However selecting this
|
||||
might increase the size of a struct page by a word.
|
||||
|
||||
config HAVE_CMPXCHG_LOCAL
|
||||
bool
|
||||
|
||||
config HAVE_CMPXCHG_DOUBLE
|
||||
bool
|
||||
|
||||
source "kernel/gcov/Kconfig"
|
||||
|
@ -169,7 +169,7 @@ static inline unsigned long __cmpxchg_local(volatile void *ptr,
|
||||
#define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n))
|
||||
|
||||
struct pt_regs;
|
||||
void NORET_TYPE die(const char *str, struct pt_regs *regs, long err);
|
||||
void die(const char *str, struct pt_regs *regs, long err);
|
||||
void _exception(long signr, struct pt_regs *regs, int code,
|
||||
unsigned long addr);
|
||||
|
||||
|
@ -24,7 +24,7 @@
|
||||
|
||||
static DEFINE_SPINLOCK(die_lock);
|
||||
|
||||
void NORET_TYPE die(const char *str, struct pt_regs *regs, long err)
|
||||
void die(const char *str, struct pt_regs *regs, long err)
|
||||
{
|
||||
static int die_counter;
|
||||
|
||||
|
@ -309,7 +309,6 @@ struct thread_struct {
|
||||
}
|
||||
|
||||
#define start_thread(regs,new_ip,new_sp) do { \
|
||||
set_fs(USER_DS); \
|
||||
regs->cr_ipsr = ((regs->cr_ipsr | (IA64_PSR_BITS_TO_SET | IA64_PSR_CPL)) \
|
||||
& ~(IA64_PSR_BITS_TO_CLEAR | IA64_PSR_RI | IA64_PSR_IS)); \
|
||||
regs->cr_iip = new_ip; \
|
||||
|
@ -27,11 +27,11 @@
|
||||
#include <asm/sal.h>
|
||||
#include <asm/mca.h>
|
||||
|
||||
typedef NORET_TYPE void (*relocate_new_kernel_t)(
|
||||
typedef void (*relocate_new_kernel_t)(
|
||||
unsigned long indirection_page,
|
||||
unsigned long start_address,
|
||||
struct ia64_boot_param *boot_param,
|
||||
unsigned long pal_addr) ATTRIB_NORET;
|
||||
unsigned long pal_addr) __noreturn;
|
||||
|
||||
struct kimage *ia64_kimage;
|
||||
|
||||
|
@ -511,8 +511,7 @@ static unsigned long amiga_gettimeoffset(void)
|
||||
return ticks + offset;
|
||||
}
|
||||
|
||||
static NORET_TYPE void amiga_reset(void)
|
||||
ATTRIB_NORET;
|
||||
static void amiga_reset(void) __noreturn;
|
||||
|
||||
static void amiga_reset(void)
|
||||
{
|
||||
|
@ -144,7 +144,7 @@ extern int ptrace_set_watch_regs(struct task_struct *child,
|
||||
extern asmlinkage void syscall_trace_enter(struct pt_regs *regs);
|
||||
extern asmlinkage void syscall_trace_leave(struct pt_regs *regs);
|
||||
|
||||
extern NORET_TYPE void die(const char *, struct pt_regs *) ATTRIB_NORET;
|
||||
extern void die(const char *, struct pt_regs *) __noreturn;
|
||||
|
||||
static inline void die_if_kernel(const char *str, struct pt_regs *regs)
|
||||
{
|
||||
|
@ -1340,7 +1340,7 @@ void ejtag_exception_handler(struct pt_regs *regs)
|
||||
/*
|
||||
* NMI exception handler.
|
||||
*/
|
||||
NORET_TYPE void ATTRIB_NORET nmi_exception_handler(struct pt_regs *regs)
|
||||
void __noreturn nmi_exception_handler(struct pt_regs *regs)
|
||||
{
|
||||
bust_spinlocks(1);
|
||||
printk("NMI taken!!!!\n");
|
||||
|
@ -110,7 +110,7 @@ extern asmlinkage void nmi_handler(void);
|
||||
extern asmlinkage void misalignment(struct pt_regs *, enum exception_code);
|
||||
|
||||
extern void die(const char *, struct pt_regs *, enum exception_code)
|
||||
ATTRIB_NORET;
|
||||
__noreturn;
|
||||
|
||||
extern int die_if_no_fixup(const char *, struct pt_regs *, enum exception_code);
|
||||
|
||||
|
@ -196,7 +196,6 @@ typedef unsigned int elf_caddr_t;
|
||||
/* offset pc for priv. level */ \
|
||||
pc |= 3; \
|
||||
\
|
||||
set_fs(USER_DS); \
|
||||
regs->iasq[0] = spaceid; \
|
||||
regs->iasq[1] = spaceid; \
|
||||
regs->iaoq[0] = pc; \
|
||||
@ -299,7 +298,6 @@ on downward growing arches, it looks like this:
|
||||
elf_addr_t pc = (elf_addr_t)new_pc | 3; \
|
||||
elf_caddr_t *argv = (elf_caddr_t *)bprm->exec + 1; \
|
||||
\
|
||||
set_fs(USER_DS); \
|
||||
regs->iasq[0] = spaceid; \
|
||||
regs->iasq[1] = spaceid; \
|
||||
regs->iaoq[0] = pc; \
|
||||
|
@ -192,7 +192,6 @@ void flush_thread(void)
|
||||
/* Only needs to handle fpu stuff or perf monitors.
|
||||
** REVISIT: several arches implement a "lazy fpu state".
|
||||
*/
|
||||
set_fs(USER_DS);
|
||||
}
|
||||
|
||||
void release_thread(struct task_struct *dead_task)
|
||||
|
@ -16,10 +16,10 @@
|
||||
#include <asm/hw_irq.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
typedef NORET_TYPE void (*relocate_new_kernel_t)(
|
||||
typedef void (*relocate_new_kernel_t)(
|
||||
unsigned long indirection_page,
|
||||
unsigned long reboot_code_buffer,
|
||||
unsigned long start_address) ATTRIB_NORET;
|
||||
unsigned long start_address) __noreturn;
|
||||
|
||||
/*
|
||||
* This is a generic machine_kexec function suitable at least for
|
||||
|
@ -307,9 +307,9 @@ static union thread_union kexec_stack __init_task_data =
|
||||
struct paca_struct kexec_paca;
|
||||
|
||||
/* Our assembly helper, in kexec_stub.S */
|
||||
extern NORET_TYPE void kexec_sequence(void *newstack, unsigned long start,
|
||||
void *image, void *control,
|
||||
void (*clear_all)(void)) ATTRIB_NORET;
|
||||
extern void kexec_sequence(void *newstack, unsigned long start,
|
||||
void *image, void *control,
|
||||
void (*clear_all)(void)) __noreturn;
|
||||
|
||||
/* too late to fail here */
|
||||
void default_machine_kexec(struct kimage *image)
|
||||
|
@ -58,7 +58,7 @@ static int distance_lookup_table[MAX_NUMNODES][MAX_DISTANCE_REF_POINTS];
|
||||
* Allocate node_to_cpumask_map based on number of available nodes
|
||||
* Requires node_possible_map to be valid.
|
||||
*
|
||||
* Note: node_to_cpumask() is not valid until after this is done.
|
||||
* Note: cpumask_of_node() is not valid until after this is done.
|
||||
*/
|
||||
static void __init setup_node_to_cpumask_map(void)
|
||||
{
|
||||
|
@ -638,7 +638,6 @@ static void oops_to_nvram(struct kmsg_dumper *dumper,
|
||||
/* These are almost always orderly shutdowns. */
|
||||
return;
|
||||
case KMSG_DUMP_OOPS:
|
||||
case KMSG_DUMP_KEXEC:
|
||||
break;
|
||||
case KMSG_DUMP_PANIC:
|
||||
panicking = true;
|
||||
|
@ -236,7 +236,7 @@ static inline unsigned long __rewind_psw(psw_t psw, unsigned long ilc)
|
||||
/*
|
||||
* Function to drop a processor into disabled wait state
|
||||
*/
|
||||
static inline void ATTRIB_NORET disabled_wait(unsigned long code)
|
||||
static inline void __noreturn disabled_wait(unsigned long code)
|
||||
{
|
||||
unsigned long ctl_buf;
|
||||
psw_t dw_psw;
|
||||
|
@ -30,7 +30,7 @@ struct mcck_struct {
|
||||
|
||||
static DEFINE_PER_CPU(struct mcck_struct, cpu_mcck);
|
||||
|
||||
static NORET_TYPE void s390_handle_damage(char *msg)
|
||||
static void s390_handle_damage(char *msg)
|
||||
{
|
||||
smp_send_stop();
|
||||
disabled_wait((unsigned long) __builtin_return_address(0));
|
||||
|
@ -70,7 +70,7 @@ void show_regs(struct pt_regs * regs)
|
||||
/*
|
||||
* Create a kernel thread
|
||||
*/
|
||||
ATTRIB_NORET void kernel_thread_helper(void *arg, int (*fn)(void *))
|
||||
__noreturn void kernel_thread_helper(void *arg, int (*fn)(void *))
|
||||
{
|
||||
do_exit(fn(arg));
|
||||
}
|
||||
|
@ -285,7 +285,7 @@ void show_regs(struct pt_regs *regs)
|
||||
/*
|
||||
* Create a kernel thread
|
||||
*/
|
||||
ATTRIB_NORET void kernel_thread_helper(void *arg, int (*fn)(void *))
|
||||
__noreturn void kernel_thread_helper(void *arg, int (*fn)(void *))
|
||||
{
|
||||
do_exit(fn(arg));
|
||||
}
|
||||
|
@ -248,11 +248,11 @@ static void setup_quasi_va_is_pa(void)
|
||||
}
|
||||
|
||||
|
||||
NORET_TYPE void machine_kexec(struct kimage *image)
|
||||
void machine_kexec(struct kimage *image)
|
||||
{
|
||||
void *reboot_code_buffer;
|
||||
NORET_TYPE void (*rnk)(unsigned long, void *, unsigned long)
|
||||
ATTRIB_NORET;
|
||||
void (*rnk)(unsigned long, void *, unsigned long)
|
||||
__noreturn;
|
||||
|
||||
/* Mask all interrupts before starting to reboot. */
|
||||
interrupt_mask_set_mask(~0ULL);
|
||||
|
@ -60,6 +60,9 @@ config X86
|
||||
select PERF_EVENTS
|
||||
select HAVE_PERF_EVENTS_NMI
|
||||
select ANON_INODES
|
||||
select HAVE_ALIGNED_STRUCT_PAGE if SLUB && !M386
|
||||
select HAVE_CMPXCHG_LOCAL if !M386
|
||||
select HAVE_CMPXCHG_DOUBLE
|
||||
select HAVE_ARCH_KMEMCHECK
|
||||
select HAVE_USER_RETURN_NOTIFIER
|
||||
select ARCH_BINFMT_ELF_RANDOMIZE_PIE
|
||||
|
@ -309,12 +309,6 @@ config X86_INTERNODE_CACHE_SHIFT
|
||||
config X86_CMPXCHG
|
||||
def_bool X86_64 || (X86_32 && !M386)
|
||||
|
||||
config CMPXCHG_LOCAL
|
||||
def_bool X86_64 || (X86_32 && !M386)
|
||||
|
||||
config CMPXCHG_DOUBLE
|
||||
def_bool y
|
||||
|
||||
config X86_L1_CACHE_SHIFT
|
||||
int
|
||||
default "7" if MPENTIUM4 || MPSC
|
||||
|
@ -110,7 +110,7 @@ void __cpuinit numa_clear_node(int cpu)
|
||||
* Allocate node_to_cpumask_map based on number of available nodes
|
||||
* Requires node_possible_map to be valid.
|
||||
*
|
||||
* Note: node_to_cpumask() is not valid until after this is done.
|
||||
* Note: cpumask_of_node() is not valid until after this is done.
|
||||
* (Use CONFIG_DEBUG_PER_CPU_MAPS to check this.)
|
||||
*/
|
||||
void __init setup_node_to_cpumask_map(void)
|
||||
|
@ -6,14 +6,6 @@ menu "UML-specific options"
|
||||
|
||||
menu "Host processor type and features"
|
||||
|
||||
config CMPXCHG_LOCAL
|
||||
bool
|
||||
default n
|
||||
|
||||
config CMPXCHG_DOUBLE
|
||||
bool
|
||||
default n
|
||||
|
||||
source "arch/x86/Kconfig.cpu"
|
||||
|
||||
endmenu
|
||||
|
@ -295,11 +295,22 @@ static int memory_block_change_state(struct memory_block *mem,
|
||||
|
||||
ret = memory_block_action(mem->start_section_nr, to_state);
|
||||
|
||||
if (ret)
|
||||
if (ret) {
|
||||
mem->state = from_state_req;
|
||||
else
|
||||
mem->state = to_state;
|
||||
goto out;
|
||||
}
|
||||
|
||||
mem->state = to_state;
|
||||
switch (mem->state) {
|
||||
case MEM_OFFLINE:
|
||||
kobject_uevent(&mem->dev.kobj, KOBJ_OFFLINE);
|
||||
break;
|
||||
case MEM_ONLINE:
|
||||
kobject_uevent(&mem->dev.kobj, KOBJ_ONLINE);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&mem->state_mutex);
|
||||
return ret;
|
||||
|
@ -83,8 +83,7 @@ static void ramoops_do_dump(struct kmsg_dumper *dumper,
|
||||
struct timeval timestamp;
|
||||
|
||||
if (reason != KMSG_DUMP_OOPS &&
|
||||
reason != KMSG_DUMP_PANIC &&
|
||||
reason != KMSG_DUMP_KEXEC)
|
||||
reason != KMSG_DUMP_PANIC)
|
||||
return;
|
||||
|
||||
/* Only dump oopses if dump_oops is set */
|
||||
@ -126,8 +125,8 @@ static int __init ramoops_probe(struct platform_device *pdev)
|
||||
goto fail3;
|
||||
}
|
||||
|
||||
rounddown_pow_of_two(pdata->mem_size);
|
||||
rounddown_pow_of_two(pdata->record_size);
|
||||
pdata->mem_size = rounddown_pow_of_two(pdata->mem_size);
|
||||
pdata->record_size = rounddown_pow_of_two(pdata->record_size);
|
||||
|
||||
/* Check for the minimum memory size */
|
||||
if (pdata->mem_size < MIN_MEM_SIZE &&
|
||||
@ -148,14 +147,6 @@ static int __init ramoops_probe(struct platform_device *pdev)
|
||||
cxt->phys_addr = pdata->mem_address;
|
||||
cxt->record_size = pdata->record_size;
|
||||
cxt->dump_oops = pdata->dump_oops;
|
||||
/*
|
||||
* Update the module parameter variables as well so they are visible
|
||||
* through /sys/module/ramoops/parameters/
|
||||
*/
|
||||
mem_size = pdata->mem_size;
|
||||
mem_address = pdata->mem_address;
|
||||
record_size = pdata->record_size;
|
||||
dump_oops = pdata->dump_oops;
|
||||
|
||||
if (!request_mem_region(cxt->phys_addr, cxt->size, "ramoops")) {
|
||||
pr_err("request mem region failed\n");
|
||||
@ -176,6 +167,15 @@ static int __init ramoops_probe(struct platform_device *pdev)
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update the module parameter variables as well so they are visible
|
||||
* through /sys/module/ramoops/parameters/
|
||||
*/
|
||||
mem_size = pdata->mem_size;
|
||||
mem_address = pdata->mem_address;
|
||||
record_size = pdata->record_size;
|
||||
dump_oops = pdata->dump_oops;
|
||||
|
||||
return 0;
|
||||
|
||||
fail1:
|
||||
|
@ -315,8 +315,7 @@ static void mtdoops_do_dump(struct kmsg_dumper *dumper,
|
||||
char *dst;
|
||||
|
||||
if (reason != KMSG_DUMP_OOPS &&
|
||||
reason != KMSG_DUMP_PANIC &&
|
||||
reason != KMSG_DUMP_KEXEC)
|
||||
reason != KMSG_DUMP_PANIC)
|
||||
return;
|
||||
|
||||
/* Only dump oopses if dump_oops is set */
|
||||
|
@ -3404,8 +3404,8 @@ static int __init parport_init_mode_setup(char *str)
|
||||
#endif
|
||||
|
||||
#ifdef MODULE
|
||||
static const char *irq[PARPORT_PC_MAX_PORTS];
|
||||
static const char *dma[PARPORT_PC_MAX_PORTS];
|
||||
static char *irq[PARPORT_PC_MAX_PORTS];
|
||||
static char *dma[PARPORT_PC_MAX_PORTS];
|
||||
|
||||
MODULE_PARM_DESC(io, "Base I/O address (SPP regs)");
|
||||
module_param_array(io, int, NULL, 0);
|
||||
|
@ -81,7 +81,7 @@ static int vram __devinitdata = 0;
|
||||
static int bpp __devinitdata = 8;
|
||||
static int reverse_i2c __devinitdata;
|
||||
#ifdef CONFIG_MTRR
|
||||
static int nomtrr __devinitdata = 0;
|
||||
static bool nomtrr __devinitdata = false;
|
||||
#endif
|
||||
#ifdef CONFIG_PMAC_BACKLIGHT
|
||||
static int backlight __devinitdata = 1;
|
||||
@ -1509,7 +1509,7 @@ static int __devinit nvidiafb_setup(char *options)
|
||||
backlight = simple_strtoul(this_opt+10, NULL, 0);
|
||||
#ifdef CONFIG_MTRR
|
||||
} else if (!strncmp(this_opt, "nomtrr", 6)) {
|
||||
nomtrr = 1;
|
||||
nomtrr = true;
|
||||
#endif
|
||||
} else if (!strncmp(this_opt, "fpdither:", 9)) {
|
||||
fpdither = simple_strtol(this_opt+9, NULL, 0);
|
||||
@ -1599,7 +1599,7 @@ MODULE_PARM_DESC(bpp, "pixel width in bits"
|
||||
module_param(reverse_i2c, int, 0);
|
||||
MODULE_PARM_DESC(reverse_i2c, "reverse port assignment of the i2c bus");
|
||||
#ifdef CONFIG_MTRR
|
||||
module_param(nomtrr, bool, 0);
|
||||
module_param(nomtrr, bool, false);
|
||||
MODULE_PARM_DESC(nomtrr, "Disables MTRR support (0 or 1=disabled) "
|
||||
"(default=0)");
|
||||
#endif
|
||||
|
@ -1139,6 +1139,7 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part)
|
||||
mutex_lock_nested(&bdev->bd_mutex, for_part);
|
||||
if (!bdev->bd_openers) {
|
||||
bdev->bd_disk = disk;
|
||||
bdev->bd_queue = disk->queue;
|
||||
bdev->bd_contains = bdev;
|
||||
if (!partno) {
|
||||
struct backing_dev_info *bdi;
|
||||
@ -1159,6 +1160,7 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part)
|
||||
disk_put_part(bdev->bd_part);
|
||||
bdev->bd_part = NULL;
|
||||
bdev->bd_disk = NULL;
|
||||
bdev->bd_queue = NULL;
|
||||
mutex_unlock(&bdev->bd_mutex);
|
||||
disk_unblock_events(disk);
|
||||
put_disk(disk);
|
||||
@ -1232,6 +1234,7 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part)
|
||||
disk_put_part(bdev->bd_part);
|
||||
bdev->bd_disk = NULL;
|
||||
bdev->bd_part = NULL;
|
||||
bdev->bd_queue = NULL;
|
||||
bdev_inode_switch_bdi(bdev->bd_inode, &default_backing_dev_info);
|
||||
if (bdev != bdev->bd_contains)
|
||||
__blkdev_put(bdev->bd_contains, mode, 1);
|
||||
|
@ -872,7 +872,8 @@ static int btree_submit_bio_hook(struct inode *inode, int rw, struct bio *bio,
|
||||
|
||||
#ifdef CONFIG_MIGRATION
|
||||
static int btree_migratepage(struct address_space *mapping,
|
||||
struct page *newpage, struct page *page)
|
||||
struct page *newpage, struct page *page,
|
||||
enum migrate_mode mode)
|
||||
{
|
||||
/*
|
||||
* we can't safely write a btree page from here,
|
||||
@ -887,7 +888,7 @@ static int btree_migratepage(struct address_space *mapping,
|
||||
if (page_has_private(page) &&
|
||||
!try_to_release_page(page, GFP_KERNEL))
|
||||
return -EAGAIN;
|
||||
return migrate_page(mapping, newpage, page);
|
||||
return migrate_page(mapping, newpage, page, mode);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include <linux/rwsem.h>
|
||||
#include <linux/uio.h>
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/prefetch.h>
|
||||
|
||||
/*
|
||||
* How many user pages to map in one call to get_user_pages(). This determines
|
||||
@ -580,9 +581,8 @@ static int get_more_blocks(struct dio *dio, struct dio_submit *sdio,
|
||||
{
|
||||
int ret;
|
||||
sector_t fs_startblk; /* Into file, in filesystem-sized blocks */
|
||||
sector_t fs_endblk; /* Into file, in filesystem-sized blocks */
|
||||
unsigned long fs_count; /* Number of filesystem-sized blocks */
|
||||
unsigned long dio_count;/* Number of dio_block-sized blocks */
|
||||
unsigned long blkmask;
|
||||
int create;
|
||||
|
||||
/*
|
||||
@ -593,11 +593,9 @@ static int get_more_blocks(struct dio *dio, struct dio_submit *sdio,
|
||||
if (ret == 0) {
|
||||
BUG_ON(sdio->block_in_file >= sdio->final_block_in_request);
|
||||
fs_startblk = sdio->block_in_file >> sdio->blkfactor;
|
||||
dio_count = sdio->final_block_in_request - sdio->block_in_file;
|
||||
fs_count = dio_count >> sdio->blkfactor;
|
||||
blkmask = (1 << sdio->blkfactor) - 1;
|
||||
if (dio_count & blkmask)
|
||||
fs_count++;
|
||||
fs_endblk = (sdio->final_block_in_request - 1) >>
|
||||
sdio->blkfactor;
|
||||
fs_count = fs_endblk - fs_startblk + 1;
|
||||
|
||||
map_bh->b_state = 0;
|
||||
map_bh->b_size = fs_count << dio->inode->i_blkbits;
|
||||
@ -1090,8 +1088,8 @@ static inline int drop_refcount(struct dio *dio)
|
||||
* individual fields and will generate much worse code. This is important
|
||||
* for the whole file.
|
||||
*/
|
||||
ssize_t
|
||||
__blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,
|
||||
static inline ssize_t
|
||||
do_blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,
|
||||
struct block_device *bdev, const struct iovec *iov, loff_t offset,
|
||||
unsigned long nr_segs, get_block_t get_block, dio_iodone_t end_io,
|
||||
dio_submit_t submit_io, int flags)
|
||||
@ -1100,7 +1098,6 @@ __blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,
|
||||
size_t size;
|
||||
unsigned long addr;
|
||||
unsigned blkbits = inode->i_blkbits;
|
||||
unsigned bdev_blkbits = 0;
|
||||
unsigned blocksize_mask = (1 << blkbits) - 1;
|
||||
ssize_t retval = -EINVAL;
|
||||
loff_t end = offset;
|
||||
@ -1113,12 +1110,14 @@ __blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,
|
||||
if (rw & WRITE)
|
||||
rw = WRITE_ODIRECT;
|
||||
|
||||
if (bdev)
|
||||
bdev_blkbits = blksize_bits(bdev_logical_block_size(bdev));
|
||||
/*
|
||||
* Avoid references to bdev if not absolutely needed to give
|
||||
* the early prefetch in the caller enough time.
|
||||
*/
|
||||
|
||||
if (offset & blocksize_mask) {
|
||||
if (bdev)
|
||||
blkbits = bdev_blkbits;
|
||||
blkbits = blksize_bits(bdev_logical_block_size(bdev));
|
||||
blocksize_mask = (1 << blkbits) - 1;
|
||||
if (offset & blocksize_mask)
|
||||
goto out;
|
||||
@ -1129,11 +1128,13 @@ __blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,
|
||||
addr = (unsigned long)iov[seg].iov_base;
|
||||
size = iov[seg].iov_len;
|
||||
end += size;
|
||||
if ((addr & blocksize_mask) || (size & blocksize_mask)) {
|
||||
if (unlikely((addr & blocksize_mask) ||
|
||||
(size & blocksize_mask))) {
|
||||
if (bdev)
|
||||
blkbits = bdev_blkbits;
|
||||
blkbits = blksize_bits(
|
||||
bdev_logical_block_size(bdev));
|
||||
blocksize_mask = (1 << blkbits) - 1;
|
||||
if ((addr & blocksize_mask) || (size & blocksize_mask))
|
||||
if ((addr & blocksize_mask) || (size & blocksize_mask))
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
@ -1316,6 +1317,30 @@ __blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,
|
||||
out:
|
||||
return retval;
|
||||
}
|
||||
|
||||
ssize_t
|
||||
__blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,
|
||||
struct block_device *bdev, const struct iovec *iov, loff_t offset,
|
||||
unsigned long nr_segs, get_block_t get_block, dio_iodone_t end_io,
|
||||
dio_submit_t submit_io, int flags)
|
||||
{
|
||||
/*
|
||||
* The block device state is needed in the end to finally
|
||||
* submit everything. Since it's likely to be cache cold
|
||||
* prefetch it here as first thing to hide some of the
|
||||
* latency.
|
||||
*
|
||||
* Attempt to prefetch the pieces we likely need later.
|
||||
*/
|
||||
prefetch(&bdev->bd_disk->part_tbl);
|
||||
prefetch(bdev->bd_queue);
|
||||
prefetch((char *)bdev->bd_queue + SMP_CACHE_BYTES);
|
||||
|
||||
return do_blockdev_direct_IO(rw, iocb, inode, bdev, iov, offset,
|
||||
nr_segs, get_block, end_io,
|
||||
submit_io, flags);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(__blockdev_direct_IO);
|
||||
|
||||
static __init int dio_init(void)
|
||||
|
232
fs/eventpoll.c
232
fs/eventpoll.c
@ -197,6 +197,12 @@ struct eventpoll {
|
||||
|
||||
/* The user that created the eventpoll descriptor */
|
||||
struct user_struct *user;
|
||||
|
||||
struct file *file;
|
||||
|
||||
/* used to optimize loop detection check */
|
||||
int visited;
|
||||
struct list_head visited_list_link;
|
||||
};
|
||||
|
||||
/* Wait structure used by the poll hooks */
|
||||
@ -255,6 +261,15 @@ static struct kmem_cache *epi_cache __read_mostly;
|
||||
/* Slab cache used to allocate "struct eppoll_entry" */
|
||||
static struct kmem_cache *pwq_cache __read_mostly;
|
||||
|
||||
/* Visited nodes during ep_loop_check(), so we can unset them when we finish */
|
||||
static LIST_HEAD(visited_list);
|
||||
|
||||
/*
|
||||
* List of files with newly added links, where we may need to limit the number
|
||||
* of emanating paths. Protected by the epmutex.
|
||||
*/
|
||||
static LIST_HEAD(tfile_check_list);
|
||||
|
||||
#ifdef CONFIG_SYSCTL
|
||||
|
||||
#include <linux/sysctl.h>
|
||||
@ -276,6 +291,12 @@ ctl_table epoll_table[] = {
|
||||
};
|
||||
#endif /* CONFIG_SYSCTL */
|
||||
|
||||
static const struct file_operations eventpoll_fops;
|
||||
|
||||
static inline int is_file_epoll(struct file *f)
|
||||
{
|
||||
return f->f_op == &eventpoll_fops;
|
||||
}
|
||||
|
||||
/* Setup the structure that is used as key for the RB tree */
|
||||
static inline void ep_set_ffd(struct epoll_filefd *ffd,
|
||||
@ -711,12 +732,6 @@ static const struct file_operations eventpoll_fops = {
|
||||
.llseek = noop_llseek,
|
||||
};
|
||||
|
||||
/* Fast test to see if the file is an eventpoll file */
|
||||
static inline int is_file_epoll(struct file *f)
|
||||
{
|
||||
return f->f_op == &eventpoll_fops;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is called from eventpoll_release() to unlink files from the eventpoll
|
||||
* interface. We need to have this facility to cleanup correctly files that are
|
||||
@ -926,6 +941,99 @@ static void ep_rbtree_insert(struct eventpoll *ep, struct epitem *epi)
|
||||
rb_insert_color(&epi->rbn, &ep->rbr);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#define PATH_ARR_SIZE 5
|
||||
/*
|
||||
* These are the number paths of length 1 to 5, that we are allowing to emanate
|
||||
* from a single file of interest. For example, we allow 1000 paths of length
|
||||
* 1, to emanate from each file of interest. This essentially represents the
|
||||
* potential wakeup paths, which need to be limited in order to avoid massive
|
||||
* uncontrolled wakeup storms. The common use case should be a single ep which
|
||||
* is connected to n file sources. In this case each file source has 1 path
|
||||
* of length 1. Thus, the numbers below should be more than sufficient. These
|
||||
* path limits are enforced during an EPOLL_CTL_ADD operation, since a modify
|
||||
* and delete can't add additional paths. Protected by the epmutex.
|
||||
*/
|
||||
static const int path_limits[PATH_ARR_SIZE] = { 1000, 500, 100, 50, 10 };
|
||||
static int path_count[PATH_ARR_SIZE];
|
||||
|
||||
static int path_count_inc(int nests)
|
||||
{
|
||||
if (++path_count[nests] > path_limits[nests])
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void path_count_init(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < PATH_ARR_SIZE; i++)
|
||||
path_count[i] = 0;
|
||||
}
|
||||
|
||||
static int reverse_path_check_proc(void *priv, void *cookie, int call_nests)
|
||||
{
|
||||
int error = 0;
|
||||
struct file *file = priv;
|
||||
struct file *child_file;
|
||||
struct epitem *epi;
|
||||
|
||||
list_for_each_entry(epi, &file->f_ep_links, fllink) {
|
||||
child_file = epi->ep->file;
|
||||
if (is_file_epoll(child_file)) {
|
||||
if (list_empty(&child_file->f_ep_links)) {
|
||||
if (path_count_inc(call_nests)) {
|
||||
error = -1;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
error = ep_call_nested(&poll_loop_ncalls,
|
||||
EP_MAX_NESTS,
|
||||
reverse_path_check_proc,
|
||||
child_file, child_file,
|
||||
current);
|
||||
}
|
||||
if (error != 0)
|
||||
break;
|
||||
} else {
|
||||
printk(KERN_ERR "reverse_path_check_proc: "
|
||||
"file is not an ep!\n");
|
||||
}
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* reverse_path_check - The tfile_check_list is list of file *, which have
|
||||
* links that are proposed to be newly added. We need to
|
||||
* make sure that those added links don't add too many
|
||||
* paths such that we will spend all our time waking up
|
||||
* eventpoll objects.
|
||||
*
|
||||
* Returns: Returns zero if the proposed links don't create too many paths,
|
||||
* -1 otherwise.
|
||||
*/
|
||||
static int reverse_path_check(void)
|
||||
{
|
||||
int length = 0;
|
||||
int error = 0;
|
||||
struct file *current_file;
|
||||
|
||||
/* let's call this for all tfiles */
|
||||
list_for_each_entry(current_file, &tfile_check_list, f_tfile_llink) {
|
||||
length++;
|
||||
path_count_init();
|
||||
error = ep_call_nested(&poll_loop_ncalls, EP_MAX_NESTS,
|
||||
reverse_path_check_proc, current_file,
|
||||
current_file, current);
|
||||
if (error)
|
||||
break;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Must be called with "mtx" held.
|
||||
*/
|
||||
@ -987,6 +1095,11 @@ static int ep_insert(struct eventpoll *ep, struct epoll_event *event,
|
||||
*/
|
||||
ep_rbtree_insert(ep, epi);
|
||||
|
||||
/* now check if we've created too many backpaths */
|
||||
error = -EINVAL;
|
||||
if (reverse_path_check())
|
||||
goto error_remove_epi;
|
||||
|
||||
/* We have to drop the new item inside our item list to keep track of it */
|
||||
spin_lock_irqsave(&ep->lock, flags);
|
||||
|
||||
@ -1011,6 +1124,14 @@ static int ep_insert(struct eventpoll *ep, struct epoll_event *event,
|
||||
|
||||
return 0;
|
||||
|
||||
error_remove_epi:
|
||||
spin_lock(&tfile->f_lock);
|
||||
if (ep_is_linked(&epi->fllink))
|
||||
list_del_init(&epi->fllink);
|
||||
spin_unlock(&tfile->f_lock);
|
||||
|
||||
rb_erase(&epi->rbn, &ep->rbr);
|
||||
|
||||
error_unregister:
|
||||
ep_unregister_pollwait(ep, epi);
|
||||
|
||||
@ -1275,18 +1396,36 @@ static int ep_loop_check_proc(void *priv, void *cookie, int call_nests)
|
||||
int error = 0;
|
||||
struct file *file = priv;
|
||||
struct eventpoll *ep = file->private_data;
|
||||
struct eventpoll *ep_tovisit;
|
||||
struct rb_node *rbp;
|
||||
struct epitem *epi;
|
||||
|
||||
mutex_lock_nested(&ep->mtx, call_nests + 1);
|
||||
ep->visited = 1;
|
||||
list_add(&ep->visited_list_link, &visited_list);
|
||||
for (rbp = rb_first(&ep->rbr); rbp; rbp = rb_next(rbp)) {
|
||||
epi = rb_entry(rbp, struct epitem, rbn);
|
||||
if (unlikely(is_file_epoll(epi->ffd.file))) {
|
||||
ep_tovisit = epi->ffd.file->private_data;
|
||||
if (ep_tovisit->visited)
|
||||
continue;
|
||||
error = ep_call_nested(&poll_loop_ncalls, EP_MAX_NESTS,
|
||||
ep_loop_check_proc, epi->ffd.file,
|
||||
epi->ffd.file->private_data, current);
|
||||
ep_loop_check_proc, epi->ffd.file,
|
||||
ep_tovisit, current);
|
||||
if (error != 0)
|
||||
break;
|
||||
} else {
|
||||
/*
|
||||
* If we've reached a file that is not associated with
|
||||
* an ep, then we need to check if the newly added
|
||||
* links are going to add too many wakeup paths. We do
|
||||
* this by adding it to the tfile_check_list, if it's
|
||||
* not already there, and calling reverse_path_check()
|
||||
* during ep_insert().
|
||||
*/
|
||||
if (list_empty(&epi->ffd.file->f_tfile_llink))
|
||||
list_add(&epi->ffd.file->f_tfile_llink,
|
||||
&tfile_check_list);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&ep->mtx);
|
||||
@ -1307,8 +1446,31 @@ static int ep_loop_check_proc(void *priv, void *cookie, int call_nests)
|
||||
*/
|
||||
static int ep_loop_check(struct eventpoll *ep, struct file *file)
|
||||
{
|
||||
return ep_call_nested(&poll_loop_ncalls, EP_MAX_NESTS,
|
||||
int ret;
|
||||
struct eventpoll *ep_cur, *ep_next;
|
||||
|
||||
ret = ep_call_nested(&poll_loop_ncalls, EP_MAX_NESTS,
|
||||
ep_loop_check_proc, file, ep, current);
|
||||
/* clear visited list */
|
||||
list_for_each_entry_safe(ep_cur, ep_next, &visited_list,
|
||||
visited_list_link) {
|
||||
ep_cur->visited = 0;
|
||||
list_del(&ep_cur->visited_list_link);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void clear_tfile_check_list(void)
|
||||
{
|
||||
struct file *file;
|
||||
|
||||
/* first clear the tfile_check_list */
|
||||
while (!list_empty(&tfile_check_list)) {
|
||||
file = list_first_entry(&tfile_check_list, struct file,
|
||||
f_tfile_llink);
|
||||
list_del_init(&file->f_tfile_llink);
|
||||
}
|
||||
INIT_LIST_HEAD(&tfile_check_list);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1316,8 +1478,9 @@ static int ep_loop_check(struct eventpoll *ep, struct file *file)
|
||||
*/
|
||||
SYSCALL_DEFINE1(epoll_create1, int, flags)
|
||||
{
|
||||
int error;
|
||||
int error, fd;
|
||||
struct eventpoll *ep = NULL;
|
||||
struct file *file;
|
||||
|
||||
/* Check the EPOLL_* constant for consistency. */
|
||||
BUILD_BUG_ON(EPOLL_CLOEXEC != O_CLOEXEC);
|
||||
@ -1334,11 +1497,25 @@ SYSCALL_DEFINE1(epoll_create1, int, flags)
|
||||
* Creates all the items needed to setup an eventpoll file. That is,
|
||||
* a file structure and a free file descriptor.
|
||||
*/
|
||||
error = anon_inode_getfd("[eventpoll]", &eventpoll_fops, ep,
|
||||
fd = get_unused_fd_flags(O_RDWR | (flags & O_CLOEXEC));
|
||||
if (fd < 0) {
|
||||
error = fd;
|
||||
goto out_free_ep;
|
||||
}
|
||||
file = anon_inode_getfile("[eventpoll]", &eventpoll_fops, ep,
|
||||
O_RDWR | (flags & O_CLOEXEC));
|
||||
if (error < 0)
|
||||
ep_free(ep);
|
||||
if (IS_ERR(file)) {
|
||||
error = PTR_ERR(file);
|
||||
goto out_free_fd;
|
||||
}
|
||||
fd_install(fd, file);
|
||||
ep->file = file;
|
||||
return fd;
|
||||
|
||||
out_free_fd:
|
||||
put_unused_fd(fd);
|
||||
out_free_ep:
|
||||
ep_free(ep);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -1404,21 +1581,27 @@ SYSCALL_DEFINE4(epoll_ctl, int, epfd, int, op, int, fd,
|
||||
/*
|
||||
* When we insert an epoll file descriptor, inside another epoll file
|
||||
* descriptor, there is the change of creating closed loops, which are
|
||||
* better be handled here, than in more critical paths.
|
||||
* better be handled here, than in more critical paths. While we are
|
||||
* checking for loops we also determine the list of files reachable
|
||||
* and hang them on the tfile_check_list, so we can check that we
|
||||
* haven't created too many possible wakeup paths.
|
||||
*
|
||||
* We hold epmutex across the loop check and the insert in this case, in
|
||||
* order to prevent two separate inserts from racing and each doing the
|
||||
* insert "at the same time" such that ep_loop_check passes on both
|
||||
* before either one does the insert, thereby creating a cycle.
|
||||
* We need to hold the epmutex across both ep_insert and ep_remove
|
||||
* b/c we want to make sure we are looking at a coherent view of
|
||||
* epoll network.
|
||||
*/
|
||||
if (unlikely(is_file_epoll(tfile) && op == EPOLL_CTL_ADD)) {
|
||||
if (op == EPOLL_CTL_ADD || op == EPOLL_CTL_DEL) {
|
||||
mutex_lock(&epmutex);
|
||||
did_lock_epmutex = 1;
|
||||
error = -ELOOP;
|
||||
if (ep_loop_check(ep, tfile) != 0)
|
||||
goto error_tgt_fput;
|
||||
}
|
||||
|
||||
if (op == EPOLL_CTL_ADD) {
|
||||
if (is_file_epoll(tfile)) {
|
||||
error = -ELOOP;
|
||||
if (ep_loop_check(ep, tfile) != 0)
|
||||
goto error_tgt_fput;
|
||||
} else
|
||||
list_add(&tfile->f_tfile_llink, &tfile_check_list);
|
||||
}
|
||||
|
||||
mutex_lock_nested(&ep->mtx, 0);
|
||||
|
||||
@ -1437,6 +1620,7 @@ SYSCALL_DEFINE4(epoll_ctl, int, epfd, int, op, int, fd,
|
||||
error = ep_insert(ep, &epds, tfile, fd);
|
||||
} else
|
||||
error = -EEXIST;
|
||||
clear_tfile_check_list();
|
||||
break;
|
||||
case EPOLL_CTL_DEL:
|
||||
if (epi)
|
||||
@ -1455,7 +1639,7 @@ SYSCALL_DEFINE4(epoll_ctl, int, epfd, int, op, int, fd,
|
||||
mutex_unlock(&ep->mtx);
|
||||
|
||||
error_tgt_fput:
|
||||
if (unlikely(did_lock_epmutex))
|
||||
if (did_lock_epmutex)
|
||||
mutex_unlock(&epmutex);
|
||||
|
||||
fput(tfile);
|
||||
|
@ -583,7 +583,8 @@ static int hugetlbfs_set_page_dirty(struct page *page)
|
||||
}
|
||||
|
||||
static int hugetlbfs_migrate_page(struct address_space *mapping,
|
||||
struct page *newpage, struct page *page)
|
||||
struct page *newpage, struct page *page,
|
||||
enum migrate_mode mode)
|
||||
{
|
||||
int rc;
|
||||
|
||||
|
@ -332,7 +332,7 @@ void nfs_commit_release_pages(struct nfs_write_data *data);
|
||||
|
||||
#ifdef CONFIG_MIGRATION
|
||||
extern int nfs_migrate_page(struct address_space *,
|
||||
struct page *, struct page *);
|
||||
struct page *, struct page *, enum migrate_mode);
|
||||
#else
|
||||
#define nfs_migrate_page NULL
|
||||
#endif
|
||||
|
@ -1688,7 +1688,7 @@ out_error:
|
||||
|
||||
#ifdef CONFIG_MIGRATION
|
||||
int nfs_migrate_page(struct address_space *mapping, struct page *newpage,
|
||||
struct page *page)
|
||||
struct page *page, enum migrate_mode mode)
|
||||
{
|
||||
/*
|
||||
* If PagePrivate is set, then the page is currently associated with
|
||||
@ -1703,7 +1703,7 @@ int nfs_migrate_page(struct address_space *mapping, struct page *newpage,
|
||||
|
||||
nfs_fscache_release_page(page, GFP_KERNEL);
|
||||
|
||||
return migrate_page(mapping, newpage, page);
|
||||
return migrate_page(mapping, newpage, page, mode);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -1137,7 +1137,7 @@ static long pipe_set_size(struct pipe_inode_info *pipe, unsigned long nr_pages)
|
||||
if (nr_pages < pipe->nrbufs)
|
||||
return -EBUSY;
|
||||
|
||||
bufs = kcalloc(nr_pages, sizeof(struct pipe_buffer), GFP_KERNEL);
|
||||
bufs = kcalloc(nr_pages, sizeof(*bufs), GFP_KERNEL | __GFP_NOWARN);
|
||||
if (unlikely(!bufs))
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -464,7 +464,7 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns,
|
||||
|
||||
seq_printf(m, "%d (%s) %c %d %d %d %d %d %u %lu \
|
||||
%lu %lu %lu %lu %lu %ld %ld %ld %ld %d 0 %llu %lu %ld %lu %lu %lu %lu %lu \
|
||||
%lu %lu %lu %lu %lu %lu %lu %lu %d %d %u %u %llu %lu %ld\n",
|
||||
%lu %lu %lu %lu %lu %lu %lu %lu %d %d %u %u %llu %lu %ld %lu %lu %lu\n",
|
||||
pid_nr_ns(pid, ns),
|
||||
tcomm,
|
||||
state,
|
||||
@ -511,7 +511,10 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns,
|
||||
task->policy,
|
||||
(unsigned long long)delayacct_blkio_ticks(task),
|
||||
cputime_to_clock_t(gtime),
|
||||
cputime_to_clock_t(cgtime));
|
||||
cputime_to_clock_t(cgtime),
|
||||
(mm && permitted) ? mm->start_data : 0,
|
||||
(mm && permitted) ? mm->end_data : 0,
|
||||
(mm && permitted) ? mm->start_brk : 0);
|
||||
if (mm)
|
||||
mmput(mm);
|
||||
return 0;
|
||||
|
@ -654,6 +654,8 @@ static int proc_pid_permission(struct inode *inode, int mask)
|
||||
bool has_perms;
|
||||
|
||||
task = get_proc_task(inode);
|
||||
if (!task)
|
||||
return -ESRCH;
|
||||
has_perms = has_pid_permissions(pid, task, 1);
|
||||
put_task_struct(task);
|
||||
|
||||
|
@ -139,6 +139,20 @@ static inline void tlb_remove_page(struct mmu_gather *tlb, struct page *page)
|
||||
__tlb_remove_tlb_entry(tlb, ptep, address); \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* tlb_remove_pmd_tlb_entry - remember a pmd mapping for later tlb invalidation
|
||||
* This is a nop so far, because only x86 needs it.
|
||||
*/
|
||||
#ifndef __tlb_remove_pmd_tlb_entry
|
||||
#define __tlb_remove_pmd_tlb_entry(tlb, pmdp, address) do {} while (0)
|
||||
#endif
|
||||
|
||||
#define tlb_remove_pmd_tlb_entry(tlb, pmdp, address) \
|
||||
do { \
|
||||
tlb->need_flush = 1; \
|
||||
__tlb_remove_pmd_tlb_entry(tlb, pmdp, address); \
|
||||
} while (0)
|
||||
|
||||
#define pte_free_tlb(tlb, ptep, address) \
|
||||
do { \
|
||||
tlb->need_flush = 1; \
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <linux/kexec.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/elf.h>
|
||||
|
||||
#define ELFCORE_ADDR_MAX (-1ULL)
|
||||
#define ELFCORE_ADDR_ERR (-2ULL)
|
||||
|
@ -61,6 +61,7 @@ struct file;
|
||||
static inline void eventpoll_init_file(struct file *file)
|
||||
{
|
||||
INIT_LIST_HEAD(&file->f_ep_links);
|
||||
INIT_LIST_HEAD(&file->f_tfile_llink);
|
||||
}
|
||||
|
||||
|
||||
|
@ -525,6 +525,7 @@ enum positive_aop_returns {
|
||||
struct page;
|
||||
struct address_space;
|
||||
struct writeback_control;
|
||||
enum migrate_mode;
|
||||
|
||||
struct iov_iter {
|
||||
const struct iovec *iov;
|
||||
@ -609,9 +610,12 @@ struct address_space_operations {
|
||||
loff_t offset, unsigned long nr_segs);
|
||||
int (*get_xip_mem)(struct address_space *, pgoff_t, int,
|
||||
void **, unsigned long *);
|
||||
/* migrate the contents of a page to the specified target */
|
||||
/*
|
||||
* migrate the contents of a page to the specified target. If sync
|
||||
* is false, it must not block.
|
||||
*/
|
||||
int (*migratepage) (struct address_space *,
|
||||
struct page *, struct page *);
|
||||
struct page *, struct page *, enum migrate_mode);
|
||||
int (*launder_page) (struct page *);
|
||||
int (*is_partially_uptodate) (struct page *, read_descriptor_t *,
|
||||
unsigned long);
|
||||
@ -656,6 +660,7 @@ struct address_space {
|
||||
* must be enforced here for CRIS, to let the least significant bit
|
||||
* of struct page's "mapping" pointer be used for PAGE_MAPPING_ANON.
|
||||
*/
|
||||
struct request_queue;
|
||||
|
||||
struct block_device {
|
||||
dev_t bd_dev; /* not a kdev_t - it's a search key */
|
||||
@ -678,6 +683,7 @@ struct block_device {
|
||||
unsigned bd_part_count;
|
||||
int bd_invalidated;
|
||||
struct gendisk * bd_disk;
|
||||
struct request_queue * bd_queue;
|
||||
struct list_head bd_list;
|
||||
/*
|
||||
* Private data. You must have bd_claim'ed the block_device
|
||||
@ -1001,6 +1007,7 @@ struct file {
|
||||
#ifdef CONFIG_EPOLL
|
||||
/* Used by fs/eventpoll.c to link all the hooks to this file */
|
||||
struct list_head f_ep_links;
|
||||
struct list_head f_tfile_llink;
|
||||
#endif /* #ifdef CONFIG_EPOLL */
|
||||
struct address_space *f_mapping;
|
||||
#ifdef CONFIG_DEBUG_WRITECOUNT
|
||||
@ -2536,7 +2543,8 @@ extern int generic_check_addressable(unsigned, u64);
|
||||
|
||||
#ifdef CONFIG_MIGRATION
|
||||
extern int buffer_migrate_page(struct address_space *,
|
||||
struct page *, struct page *);
|
||||
struct page *, struct page *,
|
||||
enum migrate_mode);
|
||||
#else
|
||||
#define buffer_migrate_page NULL
|
||||
#endif
|
||||
|
@ -18,7 +18,7 @@ extern struct page *follow_trans_huge_pmd(struct mm_struct *mm,
|
||||
unsigned int flags);
|
||||
extern int zap_huge_pmd(struct mmu_gather *tlb,
|
||||
struct vm_area_struct *vma,
|
||||
pmd_t *pmd);
|
||||
pmd_t *pmd, unsigned long addr);
|
||||
extern int mincore_huge_pmd(struct vm_area_struct *vma, pmd_t *pmd,
|
||||
unsigned long addr, unsigned long end,
|
||||
unsigned char *vec);
|
||||
|
@ -185,16 +185,17 @@ static inline void might_fault(void)
|
||||
|
||||
extern struct atomic_notifier_head panic_notifier_list;
|
||||
extern long (*panic_blink)(int state);
|
||||
NORET_TYPE void panic(const char * fmt, ...)
|
||||
__attribute__ ((NORET_AND format (printf, 1, 2))) __cold;
|
||||
__printf(1, 2)
|
||||
void panic(const char *fmt, ...)
|
||||
__noreturn __cold;
|
||||
extern void oops_enter(void);
|
||||
extern void oops_exit(void);
|
||||
void print_oops_end_marker(void);
|
||||
extern int oops_may_print(void);
|
||||
NORET_TYPE void do_exit(long error_code)
|
||||
ATTRIB_NORET;
|
||||
NORET_TYPE void complete_and_exit(struct completion *, long)
|
||||
ATTRIB_NORET;
|
||||
void do_exit(long error_code)
|
||||
__noreturn;
|
||||
void complete_and_exit(struct completion *, long)
|
||||
__noreturn;
|
||||
|
||||
/* Internal, do not use. */
|
||||
int __must_check _kstrtoul(const char *s, unsigned int base, unsigned long *res);
|
||||
|
@ -18,7 +18,6 @@
|
||||
enum kmsg_dump_reason {
|
||||
KMSG_DUMP_OOPS,
|
||||
KMSG_DUMP_PANIC,
|
||||
KMSG_DUMP_KEXEC,
|
||||
KMSG_DUMP_RESTART,
|
||||
KMSG_DUMP_HALT,
|
||||
KMSG_DUMP_POWEROFF,
|
||||
|
@ -88,8 +88,4 @@
|
||||
|
||||
#endif
|
||||
|
||||
#define NORET_TYPE /**/
|
||||
#define ATTRIB_NORET __attribute__((noreturn))
|
||||
#define NORET_AND noreturn,
|
||||
|
||||
#endif
|
||||
|
@ -32,13 +32,11 @@ enum mem_cgroup_page_stat_item {
|
||||
MEMCG_NR_FILE_MAPPED, /* # of pages charged as file rss */
|
||||
};
|
||||
|
||||
extern unsigned long mem_cgroup_isolate_pages(unsigned long nr_to_scan,
|
||||
struct list_head *dst,
|
||||
unsigned long *scanned, int order,
|
||||
isolate_mode_t mode,
|
||||
struct zone *z,
|
||||
struct mem_cgroup *mem_cont,
|
||||
int active, int file);
|
||||
struct mem_cgroup_reclaim_cookie {
|
||||
struct zone *zone;
|
||||
int priority;
|
||||
unsigned int generation;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_CGROUP_MEM_RES_CTLR
|
||||
/*
|
||||
@ -56,20 +54,21 @@ extern int mem_cgroup_newpage_charge(struct page *page, struct mm_struct *mm,
|
||||
gfp_t gfp_mask);
|
||||
/* for swap handling */
|
||||
extern int mem_cgroup_try_charge_swapin(struct mm_struct *mm,
|
||||
struct page *page, gfp_t mask, struct mem_cgroup **ptr);
|
||||
struct page *page, gfp_t mask, struct mem_cgroup **memcgp);
|
||||
extern void mem_cgroup_commit_charge_swapin(struct page *page,
|
||||
struct mem_cgroup *ptr);
|
||||
extern void mem_cgroup_cancel_charge_swapin(struct mem_cgroup *ptr);
|
||||
struct mem_cgroup *memcg);
|
||||
extern void mem_cgroup_cancel_charge_swapin(struct mem_cgroup *memcg);
|
||||
|
||||
extern int mem_cgroup_cache_charge(struct page *page, struct mm_struct *mm,
|
||||
gfp_t gfp_mask);
|
||||
extern void mem_cgroup_add_lru_list(struct page *page, enum lru_list lru);
|
||||
extern void mem_cgroup_del_lru_list(struct page *page, enum lru_list lru);
|
||||
extern void mem_cgroup_rotate_reclaimable_page(struct page *page);
|
||||
extern void mem_cgroup_rotate_lru_list(struct page *page, enum lru_list lru);
|
||||
extern void mem_cgroup_del_lru(struct page *page);
|
||||
extern void mem_cgroup_move_lists(struct page *page,
|
||||
enum lru_list from, enum lru_list to);
|
||||
|
||||
struct lruvec *mem_cgroup_zone_lruvec(struct zone *, struct mem_cgroup *);
|
||||
struct lruvec *mem_cgroup_lru_add_list(struct zone *, struct page *,
|
||||
enum lru_list);
|
||||
void mem_cgroup_lru_del_list(struct page *, enum lru_list);
|
||||
void mem_cgroup_lru_del(struct page *);
|
||||
struct lruvec *mem_cgroup_lru_move_lists(struct zone *, struct page *,
|
||||
enum lru_list, enum lru_list);
|
||||
|
||||
/* For coalescing uncharge for reducing memcg' overhead*/
|
||||
extern void mem_cgroup_uncharge_start(void);
|
||||
@ -102,10 +101,15 @@ extern struct cgroup_subsys_state *mem_cgroup_css(struct mem_cgroup *memcg);
|
||||
|
||||
extern int
|
||||
mem_cgroup_prepare_migration(struct page *page,
|
||||
struct page *newpage, struct mem_cgroup **ptr, gfp_t gfp_mask);
|
||||
struct page *newpage, struct mem_cgroup **memcgp, gfp_t gfp_mask);
|
||||
extern void mem_cgroup_end_migration(struct mem_cgroup *memcg,
|
||||
struct page *oldpage, struct page *newpage, bool migration_ok);
|
||||
|
||||
struct mem_cgroup *mem_cgroup_iter(struct mem_cgroup *,
|
||||
struct mem_cgroup *,
|
||||
struct mem_cgroup_reclaim_cookie *);
|
||||
void mem_cgroup_iter_break(struct mem_cgroup *, struct mem_cgroup *);
|
||||
|
||||
/*
|
||||
* For memory reclaim.
|
||||
*/
|
||||
@ -122,7 +126,10 @@ struct zone_reclaim_stat*
|
||||
mem_cgroup_get_reclaim_stat_from_page(struct page *page);
|
||||
extern void mem_cgroup_print_oom_info(struct mem_cgroup *memcg,
|
||||
struct task_struct *p);
|
||||
extern void mem_cgroup_replace_page_cache(struct page *oldpage,
|
||||
struct page *newpage);
|
||||
|
||||
extern void mem_cgroup_reset_owner(struct page *page);
|
||||
#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP
|
||||
extern int do_swap_account;
|
||||
#endif
|
||||
@ -157,7 +164,7 @@ u64 mem_cgroup_get_limit(struct mem_cgroup *memcg);
|
||||
|
||||
void mem_cgroup_count_vm_event(struct mm_struct *mm, enum vm_event_item idx);
|
||||
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
|
||||
void mem_cgroup_split_huge_fixup(struct page *head, struct page *tail);
|
||||
void mem_cgroup_split_huge_fixup(struct page *head);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DEBUG_VM
|
||||
@ -180,17 +187,17 @@ static inline int mem_cgroup_cache_charge(struct page *page,
|
||||
}
|
||||
|
||||
static inline int mem_cgroup_try_charge_swapin(struct mm_struct *mm,
|
||||
struct page *page, gfp_t gfp_mask, struct mem_cgroup **ptr)
|
||||
struct page *page, gfp_t gfp_mask, struct mem_cgroup **memcgp)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void mem_cgroup_commit_charge_swapin(struct page *page,
|
||||
struct mem_cgroup *ptr)
|
||||
struct mem_cgroup *memcg)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void mem_cgroup_cancel_charge_swapin(struct mem_cgroup *ptr)
|
||||
static inline void mem_cgroup_cancel_charge_swapin(struct mem_cgroup *memcg)
|
||||
{
|
||||
}
|
||||
|
||||
@ -210,33 +217,33 @@ static inline void mem_cgroup_uncharge_cache_page(struct page *page)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void mem_cgroup_add_lru_list(struct page *page, int lru)
|
||||
static inline struct lruvec *mem_cgroup_zone_lruvec(struct zone *zone,
|
||||
struct mem_cgroup *memcg)
|
||||
{
|
||||
return &zone->lruvec;
|
||||
}
|
||||
|
||||
static inline struct lruvec *mem_cgroup_lru_add_list(struct zone *zone,
|
||||
struct page *page,
|
||||
enum lru_list lru)
|
||||
{
|
||||
return &zone->lruvec;
|
||||
}
|
||||
|
||||
static inline void mem_cgroup_lru_del_list(struct page *page, enum lru_list lru)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void mem_cgroup_del_lru_list(struct page *page, int lru)
|
||||
static inline void mem_cgroup_lru_del(struct page *page)
|
||||
{
|
||||
return ;
|
||||
}
|
||||
|
||||
static inline void mem_cgroup_rotate_reclaimable_page(struct page *page)
|
||||
{
|
||||
return ;
|
||||
}
|
||||
|
||||
static inline void mem_cgroup_rotate_lru_list(struct page *page, int lru)
|
||||
{
|
||||
return ;
|
||||
}
|
||||
|
||||
static inline void mem_cgroup_del_lru(struct page *page)
|
||||
{
|
||||
return ;
|
||||
}
|
||||
|
||||
static inline void
|
||||
mem_cgroup_move_lists(struct page *page, enum lru_list from, enum lru_list to)
|
||||
static inline struct lruvec *mem_cgroup_lru_move_lists(struct zone *zone,
|
||||
struct page *page,
|
||||
enum lru_list from,
|
||||
enum lru_list to)
|
||||
{
|
||||
return &zone->lruvec;
|
||||
}
|
||||
|
||||
static inline struct mem_cgroup *try_get_mem_cgroup_from_page(struct page *page)
|
||||
@ -269,7 +276,7 @@ static inline struct cgroup_subsys_state
|
||||
|
||||
static inline int
|
||||
mem_cgroup_prepare_migration(struct page *page, struct page *newpage,
|
||||
struct mem_cgroup **ptr, gfp_t gfp_mask)
|
||||
struct mem_cgroup **memcgp, gfp_t gfp_mask)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@ -279,6 +286,19 @@ static inline void mem_cgroup_end_migration(struct mem_cgroup *memcg,
|
||||
{
|
||||
}
|
||||
|
||||
static inline struct mem_cgroup *
|
||||
mem_cgroup_iter(struct mem_cgroup *root,
|
||||
struct mem_cgroup *prev,
|
||||
struct mem_cgroup_reclaim_cookie *reclaim)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void mem_cgroup_iter_break(struct mem_cgroup *root,
|
||||
struct mem_cgroup *prev)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int mem_cgroup_get_reclaim_priority(struct mem_cgroup *memcg)
|
||||
{
|
||||
return 0;
|
||||
@ -360,8 +380,7 @@ u64 mem_cgroup_get_limit(struct mem_cgroup *memcg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void mem_cgroup_split_huge_fixup(struct page *head,
|
||||
struct page *tail)
|
||||
static inline void mem_cgroup_split_huge_fixup(struct page *head)
|
||||
{
|
||||
}
|
||||
|
||||
@ -369,6 +388,14 @@ static inline
|
||||
void mem_cgroup_count_vm_event(struct mm_struct *mm, enum vm_event_item idx)
|
||||
{
|
||||
}
|
||||
static inline void mem_cgroup_replace_page_cache(struct page *oldpage,
|
||||
struct page *newpage)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void mem_cgroup_reset_owner(struct page *page)
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_CGROUP_MEM_CONT */
|
||||
|
||||
#if !defined(CONFIG_CGROUP_MEM_RES_CTLR) || !defined(CONFIG_DEBUG_VM)
|
||||
|
@ -6,18 +6,31 @@
|
||||
|
||||
typedef struct page *new_page_t(struct page *, unsigned long private, int **);
|
||||
|
||||
/*
|
||||
* MIGRATE_ASYNC means never block
|
||||
* MIGRATE_SYNC_LIGHT in the current implementation means to allow blocking
|
||||
* on most operations but not ->writepage as the potential stall time
|
||||
* is too significant
|
||||
* MIGRATE_SYNC will block when migrating pages
|
||||
*/
|
||||
enum migrate_mode {
|
||||
MIGRATE_ASYNC,
|
||||
MIGRATE_SYNC_LIGHT,
|
||||
MIGRATE_SYNC,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_MIGRATION
|
||||
#define PAGE_MIGRATION 1
|
||||
|
||||
extern void putback_lru_pages(struct list_head *l);
|
||||
extern int migrate_page(struct address_space *,
|
||||
struct page *, struct page *);
|
||||
struct page *, struct page *, enum migrate_mode);
|
||||
extern int migrate_pages(struct list_head *l, new_page_t x,
|
||||
unsigned long private, bool offlining,
|
||||
bool sync);
|
||||
enum migrate_mode mode);
|
||||
extern int migrate_huge_pages(struct list_head *l, new_page_t x,
|
||||
unsigned long private, bool offlining,
|
||||
bool sync);
|
||||
enum migrate_mode mode);
|
||||
|
||||
extern int fail_migrate_page(struct address_space *,
|
||||
struct page *, struct page *);
|
||||
@ -36,10 +49,10 @@ extern int migrate_huge_page_move_mapping(struct address_space *mapping,
|
||||
static inline void putback_lru_pages(struct list_head *l) {}
|
||||
static inline int migrate_pages(struct list_head *l, new_page_t x,
|
||||
unsigned long private, bool offlining,
|
||||
bool sync) { return -ENOSYS; }
|
||||
enum migrate_mode mode) { return -ENOSYS; }
|
||||
static inline int migrate_huge_pages(struct list_head *l, new_page_t x,
|
||||
unsigned long private, bool offlining,
|
||||
bool sync) { return -ENOSYS; }
|
||||
enum migrate_mode mode) { return -ENOSYS; }
|
||||
|
||||
static inline int migrate_prep(void) { return -ENOSYS; }
|
||||
static inline int migrate_prep_local(void) { return -ENOSYS; }
|
||||
|
@ -22,26 +22,21 @@ static inline int page_is_file_cache(struct page *page)
|
||||
}
|
||||
|
||||
static inline void
|
||||
__add_page_to_lru_list(struct zone *zone, struct page *page, enum lru_list l,
|
||||
struct list_head *head)
|
||||
add_page_to_lru_list(struct zone *zone, struct page *page, enum lru_list lru)
|
||||
{
|
||||
list_add(&page->lru, head);
|
||||
__mod_zone_page_state(zone, NR_LRU_BASE + l, hpage_nr_pages(page));
|
||||
mem_cgroup_add_lru_list(page, l);
|
||||
struct lruvec *lruvec;
|
||||
|
||||
lruvec = mem_cgroup_lru_add_list(zone, page, lru);
|
||||
list_add(&page->lru, &lruvec->lists[lru]);
|
||||
__mod_zone_page_state(zone, NR_LRU_BASE + lru, hpage_nr_pages(page));
|
||||
}
|
||||
|
||||
static inline void
|
||||
add_page_to_lru_list(struct zone *zone, struct page *page, enum lru_list l)
|
||||
{
|
||||
__add_page_to_lru_list(zone, page, l, &zone->lru[l].list);
|
||||
}
|
||||
|
||||
static inline void
|
||||
del_page_from_lru_list(struct zone *zone, struct page *page, enum lru_list l)
|
||||
del_page_from_lru_list(struct zone *zone, struct page *page, enum lru_list lru)
|
||||
{
|
||||
mem_cgroup_lru_del_list(page, lru);
|
||||
list_del(&page->lru);
|
||||
__mod_zone_page_state(zone, NR_LRU_BASE + l, -hpage_nr_pages(page));
|
||||
mem_cgroup_del_lru_list(page, l);
|
||||
__mod_zone_page_state(zone, NR_LRU_BASE + lru, -hpage_nr_pages(page));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -59,24 +54,28 @@ static inline enum lru_list page_lru_base_type(struct page *page)
|
||||
return LRU_INACTIVE_ANON;
|
||||
}
|
||||
|
||||
static inline void
|
||||
del_page_from_lru(struct zone *zone, struct page *page)
|
||||
/**
|
||||
* page_off_lru - which LRU list was page on? clearing its lru flags.
|
||||
* @page: the page to test
|
||||
*
|
||||
* Returns the LRU list a page was on, as an index into the array of LRU
|
||||
* lists; and clears its Unevictable or Active flags, ready for freeing.
|
||||
*/
|
||||
static inline enum lru_list page_off_lru(struct page *page)
|
||||
{
|
||||
enum lru_list l;
|
||||
enum lru_list lru;
|
||||
|
||||
list_del(&page->lru);
|
||||
if (PageUnevictable(page)) {
|
||||
__ClearPageUnevictable(page);
|
||||
l = LRU_UNEVICTABLE;
|
||||
lru = LRU_UNEVICTABLE;
|
||||
} else {
|
||||
l = page_lru_base_type(page);
|
||||
lru = page_lru_base_type(page);
|
||||
if (PageActive(page)) {
|
||||
__ClearPageActive(page);
|
||||
l += LRU_ACTIVE;
|
||||
lru += LRU_ACTIVE;
|
||||
}
|
||||
}
|
||||
__mod_zone_page_state(zone, NR_LRU_BASE + l, -hpage_nr_pages(page));
|
||||
mem_cgroup_del_lru_list(page, l);
|
||||
return lru;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -97,7 +96,6 @@ static inline enum lru_list page_lru(struct page *page)
|
||||
if (PageActive(page))
|
||||
lru += LRU_ACTIVE;
|
||||
}
|
||||
|
||||
return lru;
|
||||
}
|
||||
|
||||
|
@ -151,12 +151,11 @@ struct page {
|
||||
#endif
|
||||
}
|
||||
/*
|
||||
* If another subsystem starts using the double word pairing for atomic
|
||||
* operations on struct page then it must change the #if to ensure
|
||||
* proper alignment of the page struct.
|
||||
* The struct page can be forced to be double word aligned so that atomic ops
|
||||
* on double words work. The SLUB allocator can make use of such a feature.
|
||||
*/
|
||||
#if defined(CONFIG_SLUB) && defined(CONFIG_CMPXCHG_LOCAL)
|
||||
__attribute__((__aligned__(2*sizeof(unsigned long))))
|
||||
#ifdef CONFIG_HAVE_ALIGNED_STRUCT_PAGE
|
||||
__aligned(2 * sizeof(unsigned long))
|
||||
#endif
|
||||
;
|
||||
|
||||
|
@ -140,25 +140,29 @@ enum lru_list {
|
||||
NR_LRU_LISTS
|
||||
};
|
||||
|
||||
#define for_each_lru(l) for (l = 0; l < NR_LRU_LISTS; l++)
|
||||
#define for_each_lru(lru) for (lru = 0; lru < NR_LRU_LISTS; lru++)
|
||||
|
||||
#define for_each_evictable_lru(l) for (l = 0; l <= LRU_ACTIVE_FILE; l++)
|
||||
#define for_each_evictable_lru(lru) for (lru = 0; lru <= LRU_ACTIVE_FILE; lru++)
|
||||
|
||||
static inline int is_file_lru(enum lru_list l)
|
||||
static inline int is_file_lru(enum lru_list lru)
|
||||
{
|
||||
return (l == LRU_INACTIVE_FILE || l == LRU_ACTIVE_FILE);
|
||||
return (lru == LRU_INACTIVE_FILE || lru == LRU_ACTIVE_FILE);
|
||||
}
|
||||
|
||||
static inline int is_active_lru(enum lru_list l)
|
||||
static inline int is_active_lru(enum lru_list lru)
|
||||
{
|
||||
return (l == LRU_ACTIVE_ANON || l == LRU_ACTIVE_FILE);
|
||||
return (lru == LRU_ACTIVE_ANON || lru == LRU_ACTIVE_FILE);
|
||||
}
|
||||
|
||||
static inline int is_unevictable_lru(enum lru_list l)
|
||||
static inline int is_unevictable_lru(enum lru_list lru)
|
||||
{
|
||||
return (l == LRU_UNEVICTABLE);
|
||||
return (lru == LRU_UNEVICTABLE);
|
||||
}
|
||||
|
||||
struct lruvec {
|
||||
struct list_head lists[NR_LRU_LISTS];
|
||||
};
|
||||
|
||||
/* Mask used at gathering information at once (see memcontrol.c) */
|
||||
#define LRU_ALL_FILE (BIT(LRU_INACTIVE_FILE) | BIT(LRU_ACTIVE_FILE))
|
||||
#define LRU_ALL_ANON (BIT(LRU_INACTIVE_ANON) | BIT(LRU_ACTIVE_ANON))
|
||||
@ -173,6 +177,8 @@ static inline int is_unevictable_lru(enum lru_list l)
|
||||
#define ISOLATE_CLEAN ((__force isolate_mode_t)0x4)
|
||||
/* Isolate unmapped file */
|
||||
#define ISOLATE_UNMAPPED ((__force isolate_mode_t)0x8)
|
||||
/* Isolate for asynchronous migration */
|
||||
#define ISOLATE_ASYNC_MIGRATE ((__force isolate_mode_t)0x10)
|
||||
|
||||
/* LRU Isolation modes. */
|
||||
typedef unsigned __bitwise__ isolate_mode_t;
|
||||
@ -364,10 +370,8 @@ struct zone {
|
||||
ZONE_PADDING(_pad1_)
|
||||
|
||||
/* Fields commonly accessed by the page reclaim scanner */
|
||||
spinlock_t lru_lock;
|
||||
struct zone_lru {
|
||||
struct list_head list;
|
||||
} lru[NR_LRU_LISTS];
|
||||
spinlock_t lru_lock;
|
||||
struct lruvec lruvec;
|
||||
|
||||
struct zone_reclaim_stat reclaim_stat;
|
||||
|
||||
|
@ -43,7 +43,7 @@ enum oom_constraint {
|
||||
extern void compare_swap_oom_score_adj(int old_val, int new_val);
|
||||
extern int test_set_oom_score_adj(int new_val);
|
||||
|
||||
extern unsigned int oom_badness(struct task_struct *p, struct mem_cgroup *mem,
|
||||
extern unsigned int oom_badness(struct task_struct *p, struct mem_cgroup *memcg,
|
||||
const nodemask_t *nodemask, unsigned long totalpages);
|
||||
extern int try_set_zonelist_oom(struct zonelist *zonelist, gfp_t gfp_flags);
|
||||
extern void clear_zonelist_oom(struct zonelist *zonelist, gfp_t gfp_flags);
|
||||
|
@ -10,8 +10,6 @@ enum {
|
||||
/* flags for mem_cgroup and file and I/O status */
|
||||
PCG_MOVE_LOCK, /* For race between move_account v.s. following bits */
|
||||
PCG_FILE_MAPPED, /* page is accounted as "mapped" */
|
||||
/* No lock in page_cgroup */
|
||||
PCG_ACCT_LRU, /* page has been accounted for (under lru_lock) */
|
||||
__NR_PCG_FLAGS,
|
||||
};
|
||||
|
||||
@ -31,7 +29,6 @@ enum {
|
||||
struct page_cgroup {
|
||||
unsigned long flags;
|
||||
struct mem_cgroup *mem_cgroup;
|
||||
struct list_head lru; /* per cgroup LRU list */
|
||||
};
|
||||
|
||||
void __meminit pgdat_page_cgroup_init(struct pglist_data *pgdat);
|
||||
@ -76,12 +73,6 @@ TESTPCGFLAG(Used, USED)
|
||||
CLEARPCGFLAG(Used, USED)
|
||||
SETPCGFLAG(Used, USED)
|
||||
|
||||
SETPCGFLAG(AcctLRU, ACCT_LRU)
|
||||
CLEARPCGFLAG(AcctLRU, ACCT_LRU)
|
||||
TESTPCGFLAG(AcctLRU, ACCT_LRU)
|
||||
TESTCLEARPCGFLAG(AcctLRU, ACCT_LRU)
|
||||
|
||||
|
||||
SETPCGFLAG(FileMapped, FILE_MAPPED)
|
||||
CLEARPCGFLAG(FileMapped, FILE_MAPPED)
|
||||
TESTPCGFLAG(FileMapped, FILE_MAPPED)
|
||||
@ -122,39 +113,6 @@ static inline void move_unlock_page_cgroup(struct page_cgroup *pc,
|
||||
local_irq_restore(*flags);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SPARSEMEM
|
||||
#define PCG_ARRAYID_WIDTH SECTIONS_SHIFT
|
||||
#else
|
||||
#define PCG_ARRAYID_WIDTH NODES_SHIFT
|
||||
#endif
|
||||
|
||||
#if (PCG_ARRAYID_WIDTH > BITS_PER_LONG - NR_PCG_FLAGS)
|
||||
#error Not enough space left in pc->flags to store page_cgroup array IDs
|
||||
#endif
|
||||
|
||||
/* pc->flags: ARRAY-ID | FLAGS */
|
||||
|
||||
#define PCG_ARRAYID_MASK ((1UL << PCG_ARRAYID_WIDTH) - 1)
|
||||
|
||||
#define PCG_ARRAYID_OFFSET (BITS_PER_LONG - PCG_ARRAYID_WIDTH)
|
||||
/*
|
||||
* Zero the shift count for non-existent fields, to prevent compiler
|
||||
* warnings and ensure references are optimized away.
|
||||
*/
|
||||
#define PCG_ARRAYID_SHIFT (PCG_ARRAYID_OFFSET * (PCG_ARRAYID_WIDTH != 0))
|
||||
|
||||
static inline void set_page_cgroup_array_id(struct page_cgroup *pc,
|
||||
unsigned long id)
|
||||
{
|
||||
pc->flags &= ~(PCG_ARRAYID_MASK << PCG_ARRAYID_SHIFT);
|
||||
pc->flags |= (id & PCG_ARRAYID_MASK) << PCG_ARRAYID_SHIFT;
|
||||
}
|
||||
|
||||
static inline unsigned long page_cgroup_array_id(struct page_cgroup *pc)
|
||||
{
|
||||
return (pc->flags >> PCG_ARRAYID_SHIFT) & PCG_ARRAYID_MASK;
|
||||
}
|
||||
|
||||
#else /* CONFIG_CGROUP_MEM_RES_CTLR */
|
||||
struct page_cgroup;
|
||||
|
||||
@ -183,7 +141,7 @@ static inline void __init page_cgroup_init_flatmem(void)
|
||||
extern unsigned short swap_cgroup_cmpxchg(swp_entry_t ent,
|
||||
unsigned short old, unsigned short new);
|
||||
extern unsigned short swap_cgroup_record(swp_entry_t ent, unsigned short id);
|
||||
extern unsigned short lookup_swap_cgroup(swp_entry_t ent);
|
||||
extern unsigned short lookup_swap_cgroup_id(swp_entry_t ent);
|
||||
extern int swap_cgroup_swapon(int type, unsigned long max_pages);
|
||||
extern void swap_cgroup_swapoff(int type);
|
||||
#else
|
||||
@ -195,7 +153,7 @@ unsigned short swap_cgroup_record(swp_entry_t ent, unsigned short id)
|
||||
}
|
||||
|
||||
static inline
|
||||
unsigned short lookup_swap_cgroup(swp_entry_t ent)
|
||||
unsigned short lookup_swap_cgroup_id(swp_entry_t ent)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
@ -21,8 +21,7 @@ struct pagevec {
|
||||
};
|
||||
|
||||
void __pagevec_release(struct pagevec *pvec);
|
||||
void ____pagevec_lru_add(struct pagevec *pvec, enum lru_list lru);
|
||||
void pagevec_strip(struct pagevec *pvec);
|
||||
void __pagevec_lru_add(struct pagevec *pvec, enum lru_list lru);
|
||||
unsigned pagevec_lookup(struct pagevec *pvec, struct address_space *mapping,
|
||||
pgoff_t start, unsigned nr_pages);
|
||||
unsigned pagevec_lookup_tag(struct pagevec *pvec,
|
||||
@ -59,7 +58,6 @@ static inline unsigned pagevec_add(struct pagevec *pvec, struct page *page)
|
||||
return pagevec_space(pvec);
|
||||
}
|
||||
|
||||
|
||||
static inline void pagevec_release(struct pagevec *pvec)
|
||||
{
|
||||
if (pagevec_count(pvec))
|
||||
@ -68,22 +66,22 @@ static inline void pagevec_release(struct pagevec *pvec)
|
||||
|
||||
static inline void __pagevec_lru_add_anon(struct pagevec *pvec)
|
||||
{
|
||||
____pagevec_lru_add(pvec, LRU_INACTIVE_ANON);
|
||||
__pagevec_lru_add(pvec, LRU_INACTIVE_ANON);
|
||||
}
|
||||
|
||||
static inline void __pagevec_lru_add_active_anon(struct pagevec *pvec)
|
||||
{
|
||||
____pagevec_lru_add(pvec, LRU_ACTIVE_ANON);
|
||||
__pagevec_lru_add(pvec, LRU_ACTIVE_ANON);
|
||||
}
|
||||
|
||||
static inline void __pagevec_lru_add_file(struct pagevec *pvec)
|
||||
{
|
||||
____pagevec_lru_add(pvec, LRU_INACTIVE_FILE);
|
||||
__pagevec_lru_add(pvec, LRU_INACTIVE_FILE);
|
||||
}
|
||||
|
||||
static inline void __pagevec_lru_add_active_file(struct pagevec *pvec)
|
||||
{
|
||||
____pagevec_lru_add(pvec, LRU_ACTIVE_FILE);
|
||||
__pagevec_lru_add(pvec, LRU_ACTIVE_FILE);
|
||||
}
|
||||
|
||||
static inline void pagevec_lru_add_file(struct pagevec *pvec)
|
||||
|
@ -102,4 +102,16 @@
|
||||
|
||||
#define PR_MCE_KILL_GET 34
|
||||
|
||||
/*
|
||||
* Tune up process memory map specifics.
|
||||
*/
|
||||
#define PR_SET_MM 35
|
||||
# define PR_SET_MM_START_CODE 1
|
||||
# define PR_SET_MM_END_CODE 2
|
||||
# define PR_SET_MM_START_DATA 3
|
||||
# define PR_SET_MM_END_DATA 4
|
||||
# define PR_SET_MM_START_STACK 5
|
||||
# define PR_SET_MM_START_BRK 6
|
||||
# define PR_SET_MM_BRK 7
|
||||
|
||||
#endif /* _LINUX_PRCTL_H */
|
||||
|
@ -49,9 +49,6 @@
|
||||
#define RADIX_TREE_EXCEPTIONAL_ENTRY 2
|
||||
#define RADIX_TREE_EXCEPTIONAL_SHIFT 2
|
||||
|
||||
#define radix_tree_indirect_to_ptr(ptr) \
|
||||
radix_tree_indirect_to_ptr((void __force *)(ptr))
|
||||
|
||||
static inline int radix_tree_is_indirect_ptr(void *ptr)
|
||||
{
|
||||
return (int)((unsigned long)ptr & RADIX_TREE_INDIRECT_PTR);
|
||||
|
@ -158,7 +158,7 @@ static inline void page_dup_rmap(struct page *page)
|
||||
* Called from mm/vmscan.c to handle paging out
|
||||
*/
|
||||
int page_referenced(struct page *, int is_locked,
|
||||
struct mem_cgroup *cnt, unsigned long *vm_flags);
|
||||
struct mem_cgroup *memcg, unsigned long *vm_flags);
|
||||
int page_referenced_one(struct page *, struct vm_area_struct *,
|
||||
unsigned long address, unsigned int *mapcount, unsigned long *vm_flags);
|
||||
|
||||
@ -236,7 +236,7 @@ int rmap_walk(struct page *page, int (*rmap_one)(struct page *,
|
||||
#define anon_vma_link(vma) do {} while (0)
|
||||
|
||||
static inline int page_referenced(struct page *page, int is_locked,
|
||||
struct mem_cgroup *cnt,
|
||||
struct mem_cgroup *memcg,
|
||||
unsigned long *vm_flags)
|
||||
{
|
||||
*vm_flags = 0;
|
||||
|
@ -2275,7 +2275,7 @@ extern void __cleanup_sighand(struct sighand_struct *);
|
||||
extern void exit_itimers(struct signal_struct *);
|
||||
extern void flush_itimer_signals(void);
|
||||
|
||||
extern NORET_TYPE void do_group_exit(int);
|
||||
extern void do_group_exit(int);
|
||||
|
||||
extern void daemonize(const char *, ...);
|
||||
extern int allow_signal(int);
|
||||
|
@ -266,9 +266,10 @@ DECLARE_EVENT_CLASS(mm_vmscan_lru_isolate_template,
|
||||
unsigned long nr_lumpy_taken,
|
||||
unsigned long nr_lumpy_dirty,
|
||||
unsigned long nr_lumpy_failed,
|
||||
isolate_mode_t isolate_mode),
|
||||
isolate_mode_t isolate_mode,
|
||||
int file),
|
||||
|
||||
TP_ARGS(order, nr_requested, nr_scanned, nr_taken, nr_lumpy_taken, nr_lumpy_dirty, nr_lumpy_failed, isolate_mode),
|
||||
TP_ARGS(order, nr_requested, nr_scanned, nr_taken, nr_lumpy_taken, nr_lumpy_dirty, nr_lumpy_failed, isolate_mode, file),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(int, order)
|
||||
@ -279,6 +280,7 @@ DECLARE_EVENT_CLASS(mm_vmscan_lru_isolate_template,
|
||||
__field(unsigned long, nr_lumpy_dirty)
|
||||
__field(unsigned long, nr_lumpy_failed)
|
||||
__field(isolate_mode_t, isolate_mode)
|
||||
__field(int, file)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
@ -290,9 +292,10 @@ DECLARE_EVENT_CLASS(mm_vmscan_lru_isolate_template,
|
||||
__entry->nr_lumpy_dirty = nr_lumpy_dirty;
|
||||
__entry->nr_lumpy_failed = nr_lumpy_failed;
|
||||
__entry->isolate_mode = isolate_mode;
|
||||
__entry->file = file;
|
||||
),
|
||||
|
||||
TP_printk("isolate_mode=%d order=%d nr_requested=%lu nr_scanned=%lu nr_taken=%lu contig_taken=%lu contig_dirty=%lu contig_failed=%lu",
|
||||
TP_printk("isolate_mode=%d order=%d nr_requested=%lu nr_scanned=%lu nr_taken=%lu contig_taken=%lu contig_dirty=%lu contig_failed=%lu file=%d",
|
||||
__entry->isolate_mode,
|
||||
__entry->order,
|
||||
__entry->nr_requested,
|
||||
@ -300,7 +303,8 @@ DECLARE_EVENT_CLASS(mm_vmscan_lru_isolate_template,
|
||||
__entry->nr_taken,
|
||||
__entry->nr_lumpy_taken,
|
||||
__entry->nr_lumpy_dirty,
|
||||
__entry->nr_lumpy_failed)
|
||||
__entry->nr_lumpy_failed,
|
||||
__entry->file)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(mm_vmscan_lru_isolate_template, mm_vmscan_lru_isolate,
|
||||
@ -312,9 +316,10 @@ DEFINE_EVENT(mm_vmscan_lru_isolate_template, mm_vmscan_lru_isolate,
|
||||
unsigned long nr_lumpy_taken,
|
||||
unsigned long nr_lumpy_dirty,
|
||||
unsigned long nr_lumpy_failed,
|
||||
isolate_mode_t isolate_mode),
|
||||
isolate_mode_t isolate_mode,
|
||||
int file),
|
||||
|
||||
TP_ARGS(order, nr_requested, nr_scanned, nr_taken, nr_lumpy_taken, nr_lumpy_dirty, nr_lumpy_failed, isolate_mode)
|
||||
TP_ARGS(order, nr_requested, nr_scanned, nr_taken, nr_lumpy_taken, nr_lumpy_dirty, nr_lumpy_failed, isolate_mode, file)
|
||||
|
||||
);
|
||||
|
||||
@ -327,9 +332,10 @@ DEFINE_EVENT(mm_vmscan_lru_isolate_template, mm_vmscan_memcg_isolate,
|
||||
unsigned long nr_lumpy_taken,
|
||||
unsigned long nr_lumpy_dirty,
|
||||
unsigned long nr_lumpy_failed,
|
||||
isolate_mode_t isolate_mode),
|
||||
isolate_mode_t isolate_mode,
|
||||
int file),
|
||||
|
||||
TP_ARGS(order, nr_requested, nr_scanned, nr_taken, nr_lumpy_taken, nr_lumpy_dirty, nr_lumpy_failed, isolate_mode)
|
||||
TP_ARGS(order, nr_requested, nr_scanned, nr_taken, nr_lumpy_taken, nr_lumpy_dirty, nr_lumpy_failed, isolate_mode, file)
|
||||
|
||||
);
|
||||
|
||||
|
11
init/Kconfig
11
init/Kconfig
@ -783,6 +783,17 @@ config DEBUG_BLK_CGROUP
|
||||
|
||||
endif # CGROUPS
|
||||
|
||||
config CHECKPOINT_RESTORE
|
||||
bool "Checkpoint/restore support" if EXPERT
|
||||
default n
|
||||
help
|
||||
Enables additional kernel features in a sake of checkpoint/restore.
|
||||
In particular it adds auxiliary prctl codes to setup process text,
|
||||
data and heap segment sizes, and a few additional /proc filesystem
|
||||
entries.
|
||||
|
||||
If unsure, say N here.
|
||||
|
||||
menuconfig NAMESPACES
|
||||
bool "Namespaces support" if EXPERT
|
||||
default !EXPERT
|
||||
|
@ -887,7 +887,7 @@ static void check_stack_usage(void)
|
||||
static inline void check_stack_usage(void) {}
|
||||
#endif
|
||||
|
||||
NORET_TYPE void do_exit(long code)
|
||||
void do_exit(long code)
|
||||
{
|
||||
struct task_struct *tsk = current;
|
||||
int group_dead;
|
||||
@ -1051,7 +1051,7 @@ NORET_TYPE void do_exit(long code)
|
||||
|
||||
EXPORT_SYMBOL_GPL(do_exit);
|
||||
|
||||
NORET_TYPE void complete_and_exit(struct completion *comp, long code)
|
||||
void complete_and_exit(struct completion *comp, long code)
|
||||
{
|
||||
if (comp)
|
||||
complete(comp);
|
||||
@ -1070,7 +1070,7 @@ SYSCALL_DEFINE1(exit, int, error_code)
|
||||
* Take down every thread in the group. This is called by fatal signals
|
||||
* as well as by sys_exit_group (below).
|
||||
*/
|
||||
NORET_TYPE void
|
||||
void
|
||||
do_group_exit(int exit_code)
|
||||
{
|
||||
struct signal_struct *sig = current->signal;
|
||||
|
@ -32,7 +32,6 @@
|
||||
#include <linux/console.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/swap.h>
|
||||
#include <linux/kmsg_dump.h>
|
||||
#include <linux/syscore_ops.h>
|
||||
|
||||
#include <asm/page.h>
|
||||
@ -1094,8 +1093,6 @@ void crash_kexec(struct pt_regs *regs)
|
||||
if (kexec_crash_image) {
|
||||
struct pt_regs fixed_regs;
|
||||
|
||||
kmsg_dump(KMSG_DUMP_KEXEC);
|
||||
|
||||
crash_setup_regs(&fixed_regs, regs);
|
||||
crash_save_vmcoreinfo();
|
||||
machine_crash_shutdown(&fixed_regs);
|
||||
@ -1132,6 +1129,8 @@ int crash_shrink_memory(unsigned long new_size)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned long start, end;
|
||||
unsigned long old_size;
|
||||
struct resource *ram_res;
|
||||
|
||||
mutex_lock(&kexec_mutex);
|
||||
|
||||
@ -1141,11 +1140,15 @@ int crash_shrink_memory(unsigned long new_size)
|
||||
}
|
||||
start = crashk_res.start;
|
||||
end = crashk_res.end;
|
||||
old_size = (end == 0) ? 0 : end - start + 1;
|
||||
if (new_size >= old_size) {
|
||||
ret = (new_size == old_size) ? 0 : -EINVAL;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (new_size >= end - start + 1) {
|
||||
ret = -EINVAL;
|
||||
if (new_size == end - start + 1)
|
||||
ret = 0;
|
||||
ram_res = kzalloc(sizeof(*ram_res), GFP_KERNEL);
|
||||
if (!ram_res) {
|
||||
ret = -ENOMEM;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
@ -1157,7 +1160,15 @@ int crash_shrink_memory(unsigned long new_size)
|
||||
|
||||
if ((start == end) && (crashk_res.parent != NULL))
|
||||
release_resource(&crashk_res);
|
||||
|
||||
ram_res->start = end;
|
||||
ram_res->end = crashk_res.end;
|
||||
ram_res->flags = IORESOURCE_BUSY | IORESOURCE_MEM;
|
||||
ram_res->name = "System RAM";
|
||||
|
||||
crashk_res.end = end - 1;
|
||||
|
||||
insert_resource(&iomem_resource, ram_res);
|
||||
crash_unmap_reserved_pages();
|
||||
|
||||
unlock:
|
||||
|
@ -2198,7 +2198,7 @@ static ssize_t write_enabled_file_bool(struct file *file,
|
||||
const char __user *user_buf, size_t count, loff_t *ppos)
|
||||
{
|
||||
char buf[32];
|
||||
int buf_size;
|
||||
size_t buf_size;
|
||||
|
||||
buf_size = min(count, (sizeof(buf)-1));
|
||||
if (copy_from_user(buf, user_buf, buf_size))
|
||||
|
@ -49,6 +49,15 @@ static long no_blink(int state)
|
||||
long (*panic_blink)(int state);
|
||||
EXPORT_SYMBOL(panic_blink);
|
||||
|
||||
/*
|
||||
* Stop ourself in panic -- architecture code may override this
|
||||
*/
|
||||
void __weak panic_smp_self_stop(void)
|
||||
{
|
||||
while (1)
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
/**
|
||||
* panic - halt the system
|
||||
* @fmt: The text string to print
|
||||
@ -57,8 +66,9 @@ EXPORT_SYMBOL(panic_blink);
|
||||
*
|
||||
* This function never returns.
|
||||
*/
|
||||
NORET_TYPE void panic(const char * fmt, ...)
|
||||
void panic(const char *fmt, ...)
|
||||
{
|
||||
static DEFINE_SPINLOCK(panic_lock);
|
||||
static char buf[1024];
|
||||
va_list args;
|
||||
long i, i_next = 0;
|
||||
@ -68,8 +78,14 @@ NORET_TYPE void panic(const char * fmt, ...)
|
||||
* It's possible to come here directly from a panic-assertion and
|
||||
* not have preempt disabled. Some functions called from here want
|
||||
* preempt to be disabled. No point enabling it later though...
|
||||
*
|
||||
* Only one CPU is allowed to execute the panic code from here. For
|
||||
* multiple parallel invocations of panic, all other CPUs either
|
||||
* stop themself or will wait until they are stopped by the 1st CPU
|
||||
* with smp_send_stop().
|
||||
*/
|
||||
preempt_disable();
|
||||
if (!spin_trylock(&panic_lock))
|
||||
panic_smp_self_stop();
|
||||
|
||||
console_verbose();
|
||||
bust_spinlocks(1);
|
||||
@ -78,7 +94,11 @@ NORET_TYPE void panic(const char * fmt, ...)
|
||||
va_end(args);
|
||||
printk(KERN_EMERG "Kernel panic - not syncing: %s\n",buf);
|
||||
#ifdef CONFIG_DEBUG_BUGVERBOSE
|
||||
dump_stack();
|
||||
/*
|
||||
* Avoid nested stack-dumping if a panic occurs during oops processing
|
||||
*/
|
||||
if (!oops_in_progress)
|
||||
dump_stack();
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
@ -137,7 +137,9 @@ static int pid_before(int base, int a, int b)
|
||||
}
|
||||
|
||||
/*
|
||||
* We might be racing with someone else trying to set pid_ns->last_pid.
|
||||
* We might be racing with someone else trying to set pid_ns->last_pid
|
||||
* at the pid allocation time (there's also a sysctl for this, but racing
|
||||
* with this one is OK, see comment in kernel/pid_namespace.c about it).
|
||||
* We want the winner to have the "later" value, because if the
|
||||
* "earlier" value prevails, then a pid may get reused immediately.
|
||||
*
|
||||
|
@ -191,9 +191,40 @@ void zap_pid_ns_processes(struct pid_namespace *pid_ns)
|
||||
return;
|
||||
}
|
||||
|
||||
static int pid_ns_ctl_handler(struct ctl_table *table, int write,
|
||||
void __user *buffer, size_t *lenp, loff_t *ppos)
|
||||
{
|
||||
struct ctl_table tmp = *table;
|
||||
|
||||
if (write && !capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
/*
|
||||
* Writing directly to ns' last_pid field is OK, since this field
|
||||
* is volatile in a living namespace anyway and a code writing to
|
||||
* it should synchronize its usage with external means.
|
||||
*/
|
||||
|
||||
tmp.data = ¤t->nsproxy->pid_ns->last_pid;
|
||||
return proc_dointvec(&tmp, write, buffer, lenp, ppos);
|
||||
}
|
||||
|
||||
static struct ctl_table pid_ns_ctl_table[] = {
|
||||
{
|
||||
.procname = "ns_last_pid",
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0666, /* permissions are checked in the handler */
|
||||
.proc_handler = pid_ns_ctl_handler,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct ctl_path kern_path[] = { { .procname = "kernel", }, { } };
|
||||
|
||||
static __init int pid_namespaces_init(void)
|
||||
{
|
||||
pid_ns_cachep = KMEM_CACHE(pid_namespace, SLAB_PANIC);
|
||||
register_sysctl_paths(kern_path, pid_ns_ctl_table);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
121
kernel/sys.c
121
kernel/sys.c
@ -1692,6 +1692,124 @@ SYSCALL_DEFINE1(umask, int, mask)
|
||||
return mask;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CHECKPOINT_RESTORE
|
||||
static int prctl_set_mm(int opt, unsigned long addr,
|
||||
unsigned long arg4, unsigned long arg5)
|
||||
{
|
||||
unsigned long rlim = rlimit(RLIMIT_DATA);
|
||||
unsigned long vm_req_flags;
|
||||
unsigned long vm_bad_flags;
|
||||
struct vm_area_struct *vma;
|
||||
int error = 0;
|
||||
struct mm_struct *mm = current->mm;
|
||||
|
||||
if (arg4 | arg5)
|
||||
return -EINVAL;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
if (addr >= TASK_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
down_read(&mm->mmap_sem);
|
||||
vma = find_vma(mm, addr);
|
||||
|
||||
if (opt != PR_SET_MM_START_BRK && opt != PR_SET_MM_BRK) {
|
||||
/* It must be existing VMA */
|
||||
if (!vma || vma->vm_start > addr)
|
||||
goto out;
|
||||
}
|
||||
|
||||
error = -EINVAL;
|
||||
switch (opt) {
|
||||
case PR_SET_MM_START_CODE:
|
||||
case PR_SET_MM_END_CODE:
|
||||
vm_req_flags = VM_READ | VM_EXEC;
|
||||
vm_bad_flags = VM_WRITE | VM_MAYSHARE;
|
||||
|
||||
if ((vma->vm_flags & vm_req_flags) != vm_req_flags ||
|
||||
(vma->vm_flags & vm_bad_flags))
|
||||
goto out;
|
||||
|
||||
if (opt == PR_SET_MM_START_CODE)
|
||||
mm->start_code = addr;
|
||||
else
|
||||
mm->end_code = addr;
|
||||
break;
|
||||
|
||||
case PR_SET_MM_START_DATA:
|
||||
case PR_SET_MM_END_DATA:
|
||||
vm_req_flags = VM_READ | VM_WRITE;
|
||||
vm_bad_flags = VM_EXEC | VM_MAYSHARE;
|
||||
|
||||
if ((vma->vm_flags & vm_req_flags) != vm_req_flags ||
|
||||
(vma->vm_flags & vm_bad_flags))
|
||||
goto out;
|
||||
|
||||
if (opt == PR_SET_MM_START_DATA)
|
||||
mm->start_data = addr;
|
||||
else
|
||||
mm->end_data = addr;
|
||||
break;
|
||||
|
||||
case PR_SET_MM_START_STACK:
|
||||
|
||||
#ifdef CONFIG_STACK_GROWSUP
|
||||
vm_req_flags = VM_READ | VM_WRITE | VM_GROWSUP;
|
||||
#else
|
||||
vm_req_flags = VM_READ | VM_WRITE | VM_GROWSDOWN;
|
||||
#endif
|
||||
if ((vma->vm_flags & vm_req_flags) != vm_req_flags)
|
||||
goto out;
|
||||
|
||||
mm->start_stack = addr;
|
||||
break;
|
||||
|
||||
case PR_SET_MM_START_BRK:
|
||||
if (addr <= mm->end_data)
|
||||
goto out;
|
||||
|
||||
if (rlim < RLIM_INFINITY &&
|
||||
(mm->brk - addr) +
|
||||
(mm->end_data - mm->start_data) > rlim)
|
||||
goto out;
|
||||
|
||||
mm->start_brk = addr;
|
||||
break;
|
||||
|
||||
case PR_SET_MM_BRK:
|
||||
if (addr <= mm->end_data)
|
||||
goto out;
|
||||
|
||||
if (rlim < RLIM_INFINITY &&
|
||||
(addr - mm->start_brk) +
|
||||
(mm->end_data - mm->start_data) > rlim)
|
||||
goto out;
|
||||
|
||||
mm->brk = addr;
|
||||
break;
|
||||
|
||||
default:
|
||||
error = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
error = 0;
|
||||
|
||||
out:
|
||||
up_read(&mm->mmap_sem);
|
||||
|
||||
return error;
|
||||
}
|
||||
#else /* CONFIG_CHECKPOINT_RESTORE */
|
||||
static int prctl_set_mm(int opt, unsigned long addr,
|
||||
unsigned long arg4, unsigned long arg5)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
|
||||
unsigned long, arg4, unsigned long, arg5)
|
||||
{
|
||||
@ -1841,6 +1959,9 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
|
||||
else
|
||||
error = PR_MCE_KILL_DEFAULT;
|
||||
break;
|
||||
case PR_SET_MM:
|
||||
error = prctl_set_mm(arg2, arg3, arg4, arg5);
|
||||
break;
|
||||
default:
|
||||
error = -EINVAL;
|
||||
break;
|
||||
|
@ -279,7 +279,7 @@ STATIC inline int INIT unlzo(u8 *input, int in_len,
|
||||
ret = 0;
|
||||
exit_2:
|
||||
if (!input)
|
||||
free(in_buf);
|
||||
free(in_buf_save);
|
||||
exit_1:
|
||||
if (!output)
|
||||
free(out_buf);
|
||||
|
158
lib/radix-tree.c
158
lib/radix-tree.c
@ -48,16 +48,14 @@
|
||||
struct radix_tree_node {
|
||||
unsigned int height; /* Height from the bottom */
|
||||
unsigned int count;
|
||||
struct rcu_head rcu_head;
|
||||
union {
|
||||
struct radix_tree_node *parent; /* Used when ascending tree */
|
||||
struct rcu_head rcu_head; /* Used when freeing node */
|
||||
};
|
||||
void __rcu *slots[RADIX_TREE_MAP_SIZE];
|
||||
unsigned long tags[RADIX_TREE_MAX_TAGS][RADIX_TREE_TAG_LONGS];
|
||||
};
|
||||
|
||||
struct radix_tree_path {
|
||||
struct radix_tree_node *node;
|
||||
int offset;
|
||||
};
|
||||
|
||||
#define RADIX_TREE_INDEX_BITS (8 /* CHAR_BIT */ * sizeof(unsigned long))
|
||||
#define RADIX_TREE_MAX_PATH (DIV_ROUND_UP(RADIX_TREE_INDEX_BITS, \
|
||||
RADIX_TREE_MAP_SHIFT))
|
||||
@ -256,6 +254,7 @@ static inline unsigned long radix_tree_maxindex(unsigned int height)
|
||||
static int radix_tree_extend(struct radix_tree_root *root, unsigned long index)
|
||||
{
|
||||
struct radix_tree_node *node;
|
||||
struct radix_tree_node *slot;
|
||||
unsigned int height;
|
||||
int tag;
|
||||
|
||||
@ -274,18 +273,23 @@ static int radix_tree_extend(struct radix_tree_root *root, unsigned long index)
|
||||
if (!(node = radix_tree_node_alloc(root)))
|
||||
return -ENOMEM;
|
||||
|
||||
/* Increase the height. */
|
||||
node->slots[0] = indirect_to_ptr(root->rnode);
|
||||
|
||||
/* Propagate the aggregated tag info into the new root */
|
||||
for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) {
|
||||
if (root_tag_get(root, tag))
|
||||
tag_set(node, tag, 0);
|
||||
}
|
||||
|
||||
/* Increase the height. */
|
||||
newheight = root->height+1;
|
||||
node->height = newheight;
|
||||
node->count = 1;
|
||||
node->parent = NULL;
|
||||
slot = root->rnode;
|
||||
if (newheight > 1) {
|
||||
slot = indirect_to_ptr(slot);
|
||||
slot->parent = node;
|
||||
}
|
||||
node->slots[0] = slot;
|
||||
node = ptr_to_indirect(node);
|
||||
rcu_assign_pointer(root->rnode, node);
|
||||
root->height = newheight;
|
||||
@ -331,6 +335,7 @@ int radix_tree_insert(struct radix_tree_root *root,
|
||||
if (!(slot = radix_tree_node_alloc(root)))
|
||||
return -ENOMEM;
|
||||
slot->height = height;
|
||||
slot->parent = node;
|
||||
if (node) {
|
||||
rcu_assign_pointer(node->slots[offset], slot);
|
||||
node->count++;
|
||||
@ -504,47 +509,41 @@ EXPORT_SYMBOL(radix_tree_tag_set);
|
||||
void *radix_tree_tag_clear(struct radix_tree_root *root,
|
||||
unsigned long index, unsigned int tag)
|
||||
{
|
||||
/*
|
||||
* The radix tree path needs to be one longer than the maximum path
|
||||
* since the "list" is null terminated.
|
||||
*/
|
||||
struct radix_tree_path path[RADIX_TREE_MAX_PATH + 1], *pathp = path;
|
||||
struct radix_tree_node *node = NULL;
|
||||
struct radix_tree_node *slot = NULL;
|
||||
unsigned int height, shift;
|
||||
int uninitialized_var(offset);
|
||||
|
||||
height = root->height;
|
||||
if (index > radix_tree_maxindex(height))
|
||||
goto out;
|
||||
|
||||
shift = (height - 1) * RADIX_TREE_MAP_SHIFT;
|
||||
pathp->node = NULL;
|
||||
shift = height * RADIX_TREE_MAP_SHIFT;
|
||||
slot = indirect_to_ptr(root->rnode);
|
||||
|
||||
while (height > 0) {
|
||||
int offset;
|
||||
|
||||
while (shift) {
|
||||
if (slot == NULL)
|
||||
goto out;
|
||||
|
||||
offset = (index >> shift) & RADIX_TREE_MAP_MASK;
|
||||
pathp[1].offset = offset;
|
||||
pathp[1].node = slot;
|
||||
slot = slot->slots[offset];
|
||||
pathp++;
|
||||
shift -= RADIX_TREE_MAP_SHIFT;
|
||||
height--;
|
||||
offset = (index >> shift) & RADIX_TREE_MAP_MASK;
|
||||
node = slot;
|
||||
slot = slot->slots[offset];
|
||||
}
|
||||
|
||||
if (slot == NULL)
|
||||
goto out;
|
||||
|
||||
while (pathp->node) {
|
||||
if (!tag_get(pathp->node, tag, pathp->offset))
|
||||
while (node) {
|
||||
if (!tag_get(node, tag, offset))
|
||||
goto out;
|
||||
tag_clear(pathp->node, tag, pathp->offset);
|
||||
if (any_tag_set(pathp->node, tag))
|
||||
tag_clear(node, tag, offset);
|
||||
if (any_tag_set(node, tag))
|
||||
goto out;
|
||||
pathp--;
|
||||
|
||||
index >>= RADIX_TREE_MAP_SHIFT;
|
||||
offset = index & RADIX_TREE_MAP_MASK;
|
||||
node = node->parent;
|
||||
}
|
||||
|
||||
/* clear the root's tag bit */
|
||||
@ -646,8 +645,7 @@ unsigned long radix_tree_range_tag_if_tagged(struct radix_tree_root *root,
|
||||
unsigned int iftag, unsigned int settag)
|
||||
{
|
||||
unsigned int height = root->height;
|
||||
struct radix_tree_path path[height];
|
||||
struct radix_tree_path *pathp = path;
|
||||
struct radix_tree_node *node = NULL;
|
||||
struct radix_tree_node *slot;
|
||||
unsigned int shift;
|
||||
unsigned long tagged = 0;
|
||||
@ -671,14 +669,8 @@ unsigned long radix_tree_range_tag_if_tagged(struct radix_tree_root *root,
|
||||
shift = (height - 1) * RADIX_TREE_MAP_SHIFT;
|
||||
slot = indirect_to_ptr(root->rnode);
|
||||
|
||||
/*
|
||||
* we fill the path from (root->height - 2) to 0, leaving the index at
|
||||
* (root->height - 1) as a terminator. Zero the node in the terminator
|
||||
* so that we can use this to end walk loops back up the path.
|
||||
*/
|
||||
path[height - 1].node = NULL;
|
||||
|
||||
for (;;) {
|
||||
unsigned long upindex;
|
||||
int offset;
|
||||
|
||||
offset = (index >> shift) & RADIX_TREE_MAP_MASK;
|
||||
@ -686,12 +678,10 @@ unsigned long radix_tree_range_tag_if_tagged(struct radix_tree_root *root,
|
||||
goto next;
|
||||
if (!tag_get(slot, iftag, offset))
|
||||
goto next;
|
||||
if (height > 1) {
|
||||
if (shift) {
|
||||
/* Go down one level */
|
||||
height--;
|
||||
shift -= RADIX_TREE_MAP_SHIFT;
|
||||
path[height - 1].node = slot;
|
||||
path[height - 1].offset = offset;
|
||||
node = slot;
|
||||
slot = slot->slots[offset];
|
||||
continue;
|
||||
}
|
||||
@ -701,15 +691,27 @@ unsigned long radix_tree_range_tag_if_tagged(struct radix_tree_root *root,
|
||||
tag_set(slot, settag, offset);
|
||||
|
||||
/* walk back up the path tagging interior nodes */
|
||||
pathp = &path[0];
|
||||
while (pathp->node) {
|
||||
upindex = index;
|
||||
while (node) {
|
||||
upindex >>= RADIX_TREE_MAP_SHIFT;
|
||||
offset = upindex & RADIX_TREE_MAP_MASK;
|
||||
|
||||
/* stop if we find a node with the tag already set */
|
||||
if (tag_get(pathp->node, settag, pathp->offset))
|
||||
if (tag_get(node, settag, offset))
|
||||
break;
|
||||
tag_set(pathp->node, settag, pathp->offset);
|
||||
pathp++;
|
||||
tag_set(node, settag, offset);
|
||||
node = node->parent;
|
||||
}
|
||||
|
||||
/*
|
||||
* Small optimization: now clear that node pointer.
|
||||
* Since all of this slot's ancestors now have the tag set
|
||||
* from setting it above, we have no further need to walk
|
||||
* back up the tree setting tags, until we update slot to
|
||||
* point to another radix_tree_node.
|
||||
*/
|
||||
node = NULL;
|
||||
|
||||
next:
|
||||
/* Go to next item at level determined by 'shift' */
|
||||
index = ((index >> shift) + 1) << shift;
|
||||
@ -724,8 +726,7 @@ next:
|
||||
* last_index is guaranteed to be in the tree, what
|
||||
* we do below cannot wander astray.
|
||||
*/
|
||||
slot = path[height - 1].node;
|
||||
height++;
|
||||
slot = slot->parent;
|
||||
shift += RADIX_TREE_MAP_SHIFT;
|
||||
}
|
||||
}
|
||||
@ -1299,7 +1300,7 @@ static inline void radix_tree_shrink(struct radix_tree_root *root)
|
||||
/* try to shrink tree height */
|
||||
while (root->height > 0) {
|
||||
struct radix_tree_node *to_free = root->rnode;
|
||||
void *newptr;
|
||||
struct radix_tree_node *slot;
|
||||
|
||||
BUG_ON(!radix_tree_is_indirect_ptr(to_free));
|
||||
to_free = indirect_to_ptr(to_free);
|
||||
@ -1320,10 +1321,12 @@ static inline void radix_tree_shrink(struct radix_tree_root *root)
|
||||
* (to_free->slots[0]), it will be safe to dereference the new
|
||||
* one (root->rnode) as far as dependent read barriers go.
|
||||
*/
|
||||
newptr = to_free->slots[0];
|
||||
if (root->height > 1)
|
||||
newptr = ptr_to_indirect(newptr);
|
||||
root->rnode = newptr;
|
||||
slot = to_free->slots[0];
|
||||
if (root->height > 1) {
|
||||
slot->parent = NULL;
|
||||
slot = ptr_to_indirect(slot);
|
||||
}
|
||||
root->rnode = slot;
|
||||
root->height--;
|
||||
|
||||
/*
|
||||
@ -1363,16 +1366,12 @@ static inline void radix_tree_shrink(struct radix_tree_root *root)
|
||||
*/
|
||||
void *radix_tree_delete(struct radix_tree_root *root, unsigned long index)
|
||||
{
|
||||
/*
|
||||
* The radix tree path needs to be one longer than the maximum path
|
||||
* since the "list" is null terminated.
|
||||
*/
|
||||
struct radix_tree_path path[RADIX_TREE_MAX_PATH + 1], *pathp = path;
|
||||
struct radix_tree_node *node = NULL;
|
||||
struct radix_tree_node *slot = NULL;
|
||||
struct radix_tree_node *to_free;
|
||||
unsigned int height, shift;
|
||||
int tag;
|
||||
int offset;
|
||||
int uninitialized_var(offset);
|
||||
|
||||
height = root->height;
|
||||
if (index > radix_tree_maxindex(height))
|
||||
@ -1385,39 +1384,35 @@ void *radix_tree_delete(struct radix_tree_root *root, unsigned long index)
|
||||
goto out;
|
||||
}
|
||||
slot = indirect_to_ptr(slot);
|
||||
|
||||
shift = (height - 1) * RADIX_TREE_MAP_SHIFT;
|
||||
pathp->node = NULL;
|
||||
shift = height * RADIX_TREE_MAP_SHIFT;
|
||||
|
||||
do {
|
||||
if (slot == NULL)
|
||||
goto out;
|
||||
|
||||
pathp++;
|
||||
offset = (index >> shift) & RADIX_TREE_MAP_MASK;
|
||||
pathp->offset = offset;
|
||||
pathp->node = slot;
|
||||
slot = slot->slots[offset];
|
||||
shift -= RADIX_TREE_MAP_SHIFT;
|
||||
height--;
|
||||
} while (height > 0);
|
||||
offset = (index >> shift) & RADIX_TREE_MAP_MASK;
|
||||
node = slot;
|
||||
slot = slot->slots[offset];
|
||||
} while (shift);
|
||||
|
||||
if (slot == NULL)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Clear all tags associated with the just-deleted item
|
||||
* Clear all tags associated with the item to be deleted.
|
||||
* This way of doing it would be inefficient, but seldom is any set.
|
||||
*/
|
||||
for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) {
|
||||
if (tag_get(pathp->node, tag, pathp->offset))
|
||||
if (tag_get(node, tag, offset))
|
||||
radix_tree_tag_clear(root, index, tag);
|
||||
}
|
||||
|
||||
to_free = NULL;
|
||||
/* Now free the nodes we do not need anymore */
|
||||
while (pathp->node) {
|
||||
pathp->node->slots[pathp->offset] = NULL;
|
||||
pathp->node->count--;
|
||||
while (node) {
|
||||
node->slots[offset] = NULL;
|
||||
node->count--;
|
||||
/*
|
||||
* Queue the node for deferred freeing after the
|
||||
* last reference to it disappears (set NULL, above).
|
||||
@ -1425,17 +1420,20 @@ void *radix_tree_delete(struct radix_tree_root *root, unsigned long index)
|
||||
if (to_free)
|
||||
radix_tree_node_free(to_free);
|
||||
|
||||
if (pathp->node->count) {
|
||||
if (pathp->node == indirect_to_ptr(root->rnode))
|
||||
if (node->count) {
|
||||
if (node == indirect_to_ptr(root->rnode))
|
||||
radix_tree_shrink(root);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Node with zero slots in use so free it */
|
||||
to_free = pathp->node;
|
||||
pathp--;
|
||||
to_free = node;
|
||||
|
||||
index >>= RADIX_TREE_MAP_SHIFT;
|
||||
offset = index & RADIX_TREE_MAP_MASK;
|
||||
node = node->parent;
|
||||
}
|
||||
|
||||
root_tag_clear_all(root);
|
||||
root->height = 0;
|
||||
root->rnode = NULL;
|
||||
|
@ -350,7 +350,7 @@ static isolate_migrate_t isolate_migratepages(struct zone *zone,
|
||||
}
|
||||
|
||||
if (!cc->sync)
|
||||
mode |= ISOLATE_CLEAN;
|
||||
mode |= ISOLATE_ASYNC_MIGRATE;
|
||||
|
||||
/* Try isolate the page */
|
||||
if (__isolate_lru_page(page, mode, 0) != 0)
|
||||
@ -557,7 +557,7 @@ static int compact_zone(struct zone *zone, struct compact_control *cc)
|
||||
nr_migrate = cc->nr_migratepages;
|
||||
err = migrate_pages(&cc->migratepages, compaction_alloc,
|
||||
(unsigned long)cc, false,
|
||||
cc->sync);
|
||||
cc->sync ? MIGRATE_SYNC_LIGHT : MIGRATE_ASYNC);
|
||||
update_nr_listpages(cc);
|
||||
nr_remaining = cc->nr_migratepages;
|
||||
|
||||
@ -671,6 +671,7 @@ static int compact_node(int nid)
|
||||
.nr_freepages = 0,
|
||||
.nr_migratepages = 0,
|
||||
.order = -1,
|
||||
.sync = true,
|
||||
};
|
||||
|
||||
zone = &pgdat->node_zones[zoneid];
|
||||
|
18
mm/filemap.c
18
mm/filemap.c
@ -393,24 +393,11 @@ EXPORT_SYMBOL(filemap_write_and_wait_range);
|
||||
int replace_page_cache_page(struct page *old, struct page *new, gfp_t gfp_mask)
|
||||
{
|
||||
int error;
|
||||
struct mem_cgroup *memcg = NULL;
|
||||
|
||||
VM_BUG_ON(!PageLocked(old));
|
||||
VM_BUG_ON(!PageLocked(new));
|
||||
VM_BUG_ON(new->mapping);
|
||||
|
||||
/*
|
||||
* This is not page migration, but prepare_migration and
|
||||
* end_migration does enough work for charge replacement.
|
||||
*
|
||||
* In the longer term we probably want a specialized function
|
||||
* for moving the charge from old to new in a more efficient
|
||||
* manner.
|
||||
*/
|
||||
error = mem_cgroup_prepare_migration(old, new, &memcg, gfp_mask);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = radix_tree_preload(gfp_mask & ~__GFP_HIGHMEM);
|
||||
if (!error) {
|
||||
struct address_space *mapping = old->mapping;
|
||||
@ -432,13 +419,12 @@ int replace_page_cache_page(struct page *old, struct page *new, gfp_t gfp_mask)
|
||||
if (PageSwapBacked(new))
|
||||
__inc_zone_page_state(new, NR_SHMEM);
|
||||
spin_unlock_irq(&mapping->tree_lock);
|
||||
/* mem_cgroup codes must not be called under tree_lock */
|
||||
mem_cgroup_replace_page_cache(old, new);
|
||||
radix_tree_preload_end();
|
||||
if (freepage)
|
||||
freepage(old);
|
||||
page_cache_release(old);
|
||||
mem_cgroup_end_migration(memcg, old, new, true);
|
||||
} else {
|
||||
mem_cgroup_end_migration(memcg, old, new, false);
|
||||
}
|
||||
|
||||
return error;
|
||||
|
101
mm/huge_memory.c
101
mm/huge_memory.c
@ -487,41 +487,68 @@ static struct attribute_group khugepaged_attr_group = {
|
||||
.attrs = khugepaged_attr,
|
||||
.name = "khugepaged",
|
||||
};
|
||||
|
||||
static int __init hugepage_init_sysfs(struct kobject **hugepage_kobj)
|
||||
{
|
||||
int err;
|
||||
|
||||
*hugepage_kobj = kobject_create_and_add("transparent_hugepage", mm_kobj);
|
||||
if (unlikely(!*hugepage_kobj)) {
|
||||
printk(KERN_ERR "hugepage: failed kobject create\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
err = sysfs_create_group(*hugepage_kobj, &hugepage_attr_group);
|
||||
if (err) {
|
||||
printk(KERN_ERR "hugepage: failed register hugeage group\n");
|
||||
goto delete_obj;
|
||||
}
|
||||
|
||||
err = sysfs_create_group(*hugepage_kobj, &khugepaged_attr_group);
|
||||
if (err) {
|
||||
printk(KERN_ERR "hugepage: failed register hugeage group\n");
|
||||
goto remove_hp_group;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
remove_hp_group:
|
||||
sysfs_remove_group(*hugepage_kobj, &hugepage_attr_group);
|
||||
delete_obj:
|
||||
kobject_put(*hugepage_kobj);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __init hugepage_exit_sysfs(struct kobject *hugepage_kobj)
|
||||
{
|
||||
sysfs_remove_group(hugepage_kobj, &khugepaged_attr_group);
|
||||
sysfs_remove_group(hugepage_kobj, &hugepage_attr_group);
|
||||
kobject_put(hugepage_kobj);
|
||||
}
|
||||
#else
|
||||
static inline int hugepage_init_sysfs(struct kobject **hugepage_kobj)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void hugepage_exit_sysfs(struct kobject *hugepage_kobj)
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_SYSFS */
|
||||
|
||||
static int __init hugepage_init(void)
|
||||
{
|
||||
int err;
|
||||
#ifdef CONFIG_SYSFS
|
||||
static struct kobject *hugepage_kobj;
|
||||
#endif
|
||||
struct kobject *hugepage_kobj;
|
||||
|
||||
err = -EINVAL;
|
||||
if (!has_transparent_hugepage()) {
|
||||
transparent_hugepage_flags = 0;
|
||||
goto out;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SYSFS
|
||||
err = -ENOMEM;
|
||||
hugepage_kobj = kobject_create_and_add("transparent_hugepage", mm_kobj);
|
||||
if (unlikely(!hugepage_kobj)) {
|
||||
printk(KERN_ERR "hugepage: failed kobject create\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = sysfs_create_group(hugepage_kobj, &hugepage_attr_group);
|
||||
if (err) {
|
||||
printk(KERN_ERR "hugepage: failed register hugeage group\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = sysfs_create_group(hugepage_kobj, &khugepaged_attr_group);
|
||||
if (err) {
|
||||
printk(KERN_ERR "hugepage: failed register hugeage group\n");
|
||||
goto out;
|
||||
}
|
||||
#endif
|
||||
err = hugepage_init_sysfs(&hugepage_kobj);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = khugepaged_slab_init();
|
||||
if (err)
|
||||
@ -545,7 +572,9 @@ static int __init hugepage_init(void)
|
||||
|
||||
set_recommended_min_free_kbytes();
|
||||
|
||||
return 0;
|
||||
out:
|
||||
hugepage_exit_sysfs(hugepage_kobj);
|
||||
return err;
|
||||
}
|
||||
module_init(hugepage_init)
|
||||
@ -997,7 +1026,7 @@ out:
|
||||
}
|
||||
|
||||
int zap_huge_pmd(struct mmu_gather *tlb, struct vm_area_struct *vma,
|
||||
pmd_t *pmd)
|
||||
pmd_t *pmd, unsigned long addr)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
@ -1013,6 +1042,7 @@ int zap_huge_pmd(struct mmu_gather *tlb, struct vm_area_struct *vma,
|
||||
pgtable = get_pmd_huge_pte(tlb->mm);
|
||||
page = pmd_page(*pmd);
|
||||
pmd_clear(pmd);
|
||||
tlb_remove_pmd_tlb_entry(tlb, pmd, addr);
|
||||
page_remove_rmap(page);
|
||||
VM_BUG_ON(page_mapcount(page) < 0);
|
||||
add_mm_counter(tlb->mm, MM_ANONPAGES, -HPAGE_PMD_NR);
|
||||
@ -1116,7 +1146,6 @@ int change_huge_pmd(struct vm_area_struct *vma, pmd_t *pmd,
|
||||
entry = pmd_modify(entry, newprot);
|
||||
set_pmd_at(mm, addr, pmd, entry);
|
||||
spin_unlock(&vma->vm_mm->page_table_lock);
|
||||
flush_tlb_range(vma, addr, addr + HPAGE_PMD_SIZE);
|
||||
ret = 1;
|
||||
}
|
||||
} else
|
||||
@ -1199,16 +1228,16 @@ static int __split_huge_page_splitting(struct page *page,
|
||||
static void __split_huge_page_refcount(struct page *page)
|
||||
{
|
||||
int i;
|
||||
unsigned long head_index = page->index;
|
||||
struct zone *zone = page_zone(page);
|
||||
int zonestat;
|
||||
int tail_count = 0;
|
||||
|
||||
/* prevent PageLRU to go away from under us, and freeze lru stats */
|
||||
spin_lock_irq(&zone->lru_lock);
|
||||
compound_lock(page);
|
||||
/* complete memcg works before add pages to LRU */
|
||||
mem_cgroup_split_huge_fixup(page);
|
||||
|
||||
for (i = 1; i < HPAGE_PMD_NR; i++) {
|
||||
for (i = HPAGE_PMD_NR - 1; i >= 1; i--) {
|
||||
struct page *page_tail = page + i;
|
||||
|
||||
/* tail_page->_mapcount cannot change */
|
||||
@ -1271,14 +1300,13 @@ static void __split_huge_page_refcount(struct page *page)
|
||||
BUG_ON(page_tail->mapping);
|
||||
page_tail->mapping = page->mapping;
|
||||
|
||||
page_tail->index = ++head_index;
|
||||
page_tail->index = page->index + i;
|
||||
|
||||
BUG_ON(!PageAnon(page_tail));
|
||||
BUG_ON(!PageUptodate(page_tail));
|
||||
BUG_ON(!PageDirty(page_tail));
|
||||
BUG_ON(!PageSwapBacked(page_tail));
|
||||
|
||||
mem_cgroup_split_huge_fixup(page, page_tail);
|
||||
|
||||
lru_add_page_tail(zone, page, page_tail);
|
||||
}
|
||||
@ -1288,15 +1316,6 @@ static void __split_huge_page_refcount(struct page *page)
|
||||
__dec_zone_page_state(page, NR_ANON_TRANSPARENT_HUGEPAGES);
|
||||
__mod_zone_page_state(zone, NR_ANON_PAGES, HPAGE_PMD_NR);
|
||||
|
||||
/*
|
||||
* A hugepage counts for HPAGE_PMD_NR pages on the LRU statistics,
|
||||
* so adjust those appropriately if this page is on the LRU.
|
||||
*/
|
||||
if (PageLRU(page)) {
|
||||
zonestat = NR_LRU_BASE + page_lru(page);
|
||||
__mod_zone_page_state(zone, zonestat, -(HPAGE_PMD_NR-1));
|
||||
}
|
||||
|
||||
ClearPageCompound(page);
|
||||
compound_unlock(page);
|
||||
spin_unlock_irq(&zone->lru_lock);
|
||||
|
11
mm/ksm.c
11
mm/ksm.c
@ -28,6 +28,7 @@
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/memcontrol.h>
|
||||
#include <linux/rbtree.h>
|
||||
#include <linux/memory.h>
|
||||
#include <linux/mmu_notifier.h>
|
||||
@ -1571,6 +1572,16 @@ struct page *ksm_does_need_to_copy(struct page *page,
|
||||
|
||||
new_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, address);
|
||||
if (new_page) {
|
||||
/*
|
||||
* The memcg-specific accounting when moving
|
||||
* pages around the LRU lists relies on the
|
||||
* page's owner (memcg) to be valid. Usually,
|
||||
* pages are assigned to a new owner before
|
||||
* being put on the LRU list, but since this
|
||||
* is not the case here, the stale owner from
|
||||
* a previous allocation cycle must be reset.
|
||||
*/
|
||||
mem_cgroup_reset_owner(new_page);
|
||||
copy_user_highpage(new_page, page, address, vma);
|
||||
|
||||
SetPageDirty(new_page);
|
||||
|
1128
mm/memcontrol.c
1128
mm/memcontrol.c
File diff suppressed because it is too large
Load Diff
@ -1557,7 +1557,7 @@ int soft_offline_page(struct page *page, int flags)
|
||||
page_is_file_cache(page));
|
||||
list_add(&page->lru, &pagelist);
|
||||
ret = migrate_pages(&pagelist, new_page, MPOL_MF_MOVE_ALL,
|
||||
0, true);
|
||||
0, MIGRATE_SYNC);
|
||||
if (ret) {
|
||||
putback_lru_pages(&pagelist);
|
||||
pr_info("soft offline: %#lx: migration failed %d, type %lx\n",
|
||||
|
@ -293,7 +293,7 @@ int __tlb_remove_page(struct mmu_gather *tlb, struct page *page)
|
||||
{
|
||||
struct mmu_gather_batch *batch;
|
||||
|
||||
tlb->need_flush = 1;
|
||||
VM_BUG_ON(!tlb->need_flush);
|
||||
|
||||
if (tlb_fast_mode(tlb)) {
|
||||
free_page_and_swap_cache(page);
|
||||
@ -1231,7 +1231,7 @@ static inline unsigned long zap_pmd_range(struct mmu_gather *tlb,
|
||||
if (next-addr != HPAGE_PMD_SIZE) {
|
||||
VM_BUG_ON(!rwsem_is_locked(&tlb->mm->mmap_sem));
|
||||
split_huge_page_pmd(vma->vm_mm, pmd);
|
||||
} else if (zap_huge_pmd(tlb, vma, pmd))
|
||||
} else if (zap_huge_pmd(tlb, vma, pmd, addr))
|
||||
continue;
|
||||
/* fall through */
|
||||
}
|
||||
|
@ -809,7 +809,7 @@ do_migrate_range(unsigned long start_pfn, unsigned long end_pfn)
|
||||
}
|
||||
/* this function returns # of failed pages */
|
||||
ret = migrate_pages(&source, hotremove_migrate_alloc, 0,
|
||||
true, true);
|
||||
true, MIGRATE_SYNC);
|
||||
if (ret)
|
||||
putback_lru_pages(&source);
|
||||
}
|
||||
|
@ -942,7 +942,7 @@ static int migrate_to_node(struct mm_struct *mm, int source, int dest,
|
||||
|
||||
if (!list_empty(&pagelist)) {
|
||||
err = migrate_pages(&pagelist, new_node_page, dest,
|
||||
false, true);
|
||||
false, MIGRATE_SYNC);
|
||||
if (err)
|
||||
putback_lru_pages(&pagelist);
|
||||
}
|
||||
|
173
mm/migrate.c
173
mm/migrate.c
@ -216,6 +216,56 @@ out:
|
||||
pte_unmap_unlock(ptep, ptl);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BLOCK
|
||||
/* Returns true if all buffers are successfully locked */
|
||||
static bool buffer_migrate_lock_buffers(struct buffer_head *head,
|
||||
enum migrate_mode mode)
|
||||
{
|
||||
struct buffer_head *bh = head;
|
||||
|
||||
/* Simple case, sync compaction */
|
||||
if (mode != MIGRATE_ASYNC) {
|
||||
do {
|
||||
get_bh(bh);
|
||||
lock_buffer(bh);
|
||||
bh = bh->b_this_page;
|
||||
|
||||
} while (bh != head);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* async case, we cannot block on lock_buffer so use trylock_buffer */
|
||||
do {
|
||||
get_bh(bh);
|
||||
if (!trylock_buffer(bh)) {
|
||||
/*
|
||||
* We failed to lock the buffer and cannot stall in
|
||||
* async migration. Release the taken locks
|
||||
*/
|
||||
struct buffer_head *failed_bh = bh;
|
||||
put_bh(failed_bh);
|
||||
bh = head;
|
||||
while (bh != failed_bh) {
|
||||
unlock_buffer(bh);
|
||||
put_bh(bh);
|
||||
bh = bh->b_this_page;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bh = bh->b_this_page;
|
||||
} while (bh != head);
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
static inline bool buffer_migrate_lock_buffers(struct buffer_head *head,
|
||||
enum migrate_mode mode)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#endif /* CONFIG_BLOCK */
|
||||
|
||||
/*
|
||||
* Replace the page in the mapping.
|
||||
*
|
||||
@ -225,7 +275,8 @@ out:
|
||||
* 3 for pages with a mapping and PagePrivate/PagePrivate2 set.
|
||||
*/
|
||||
static int migrate_page_move_mapping(struct address_space *mapping,
|
||||
struct page *newpage, struct page *page)
|
||||
struct page *newpage, struct page *page,
|
||||
struct buffer_head *head, enum migrate_mode mode)
|
||||
{
|
||||
int expected_count;
|
||||
void **pslot;
|
||||
@ -254,6 +305,20 @@ static int migrate_page_move_mapping(struct address_space *mapping,
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
/*
|
||||
* In the async migration case of moving a page with buffers, lock the
|
||||
* buffers using trylock before the mapping is moved. If the mapping
|
||||
* was moved, we later failed to lock the buffers and could not move
|
||||
* the mapping back due to an elevated page count, we would have to
|
||||
* block waiting on other references to be dropped.
|
||||
*/
|
||||
if (mode == MIGRATE_ASYNC && head &&
|
||||
!buffer_migrate_lock_buffers(head, mode)) {
|
||||
page_unfreeze_refs(page, expected_count);
|
||||
spin_unlock_irq(&mapping->tree_lock);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now we know that no one else is looking at the page.
|
||||
*/
|
||||
@ -409,13 +474,14 @@ EXPORT_SYMBOL(fail_migrate_page);
|
||||
* Pages are locked upon entry and exit.
|
||||
*/
|
||||
int migrate_page(struct address_space *mapping,
|
||||
struct page *newpage, struct page *page)
|
||||
struct page *newpage, struct page *page,
|
||||
enum migrate_mode mode)
|
||||
{
|
||||
int rc;
|
||||
|
||||
BUG_ON(PageWriteback(page)); /* Writeback must be complete */
|
||||
|
||||
rc = migrate_page_move_mapping(mapping, newpage, page);
|
||||
rc = migrate_page_move_mapping(mapping, newpage, page, NULL, mode);
|
||||
|
||||
if (rc)
|
||||
return rc;
|
||||
@ -432,28 +498,28 @@ EXPORT_SYMBOL(migrate_page);
|
||||
* exist.
|
||||
*/
|
||||
int buffer_migrate_page(struct address_space *mapping,
|
||||
struct page *newpage, struct page *page)
|
||||
struct page *newpage, struct page *page, enum migrate_mode mode)
|
||||
{
|
||||
struct buffer_head *bh, *head;
|
||||
int rc;
|
||||
|
||||
if (!page_has_buffers(page))
|
||||
return migrate_page(mapping, newpage, page);
|
||||
return migrate_page(mapping, newpage, page, mode);
|
||||
|
||||
head = page_buffers(page);
|
||||
|
||||
rc = migrate_page_move_mapping(mapping, newpage, page);
|
||||
rc = migrate_page_move_mapping(mapping, newpage, page, head, mode);
|
||||
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
bh = head;
|
||||
do {
|
||||
get_bh(bh);
|
||||
lock_buffer(bh);
|
||||
bh = bh->b_this_page;
|
||||
|
||||
} while (bh != head);
|
||||
/*
|
||||
* In the async case, migrate_page_move_mapping locked the buffers
|
||||
* with an IRQ-safe spinlock held. In the sync case, the buffers
|
||||
* need to be locked now
|
||||
*/
|
||||
if (mode != MIGRATE_ASYNC)
|
||||
BUG_ON(!buffer_migrate_lock_buffers(head, mode));
|
||||
|
||||
ClearPagePrivate(page);
|
||||
set_page_private(newpage, page_private(page));
|
||||
@ -530,10 +596,14 @@ static int writeout(struct address_space *mapping, struct page *page)
|
||||
* Default handling if a filesystem does not provide a migration function.
|
||||
*/
|
||||
static int fallback_migrate_page(struct address_space *mapping,
|
||||
struct page *newpage, struct page *page)
|
||||
struct page *newpage, struct page *page, enum migrate_mode mode)
|
||||
{
|
||||
if (PageDirty(page))
|
||||
if (PageDirty(page)) {
|
||||
/* Only writeback pages in full synchronous migration */
|
||||
if (mode != MIGRATE_SYNC)
|
||||
return -EBUSY;
|
||||
return writeout(mapping, page);
|
||||
}
|
||||
|
||||
/*
|
||||
* Buffers may be managed in a filesystem specific way.
|
||||
@ -543,7 +613,7 @@ static int fallback_migrate_page(struct address_space *mapping,
|
||||
!try_to_release_page(page, GFP_KERNEL))
|
||||
return -EAGAIN;
|
||||
|
||||
return migrate_page(mapping, newpage, page);
|
||||
return migrate_page(mapping, newpage, page, mode);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -558,7 +628,7 @@ static int fallback_migrate_page(struct address_space *mapping,
|
||||
* == 0 - success
|
||||
*/
|
||||
static int move_to_new_page(struct page *newpage, struct page *page,
|
||||
int remap_swapcache, bool sync)
|
||||
int remap_swapcache, enum migrate_mode mode)
|
||||
{
|
||||
struct address_space *mapping;
|
||||
int rc;
|
||||
@ -579,29 +649,18 @@ static int move_to_new_page(struct page *newpage, struct page *page,
|
||||
|
||||
mapping = page_mapping(page);
|
||||
if (!mapping)
|
||||
rc = migrate_page(mapping, newpage, page);
|
||||
else {
|
||||
rc = migrate_page(mapping, newpage, page, mode);
|
||||
else if (mapping->a_ops->migratepage)
|
||||
/*
|
||||
* Do not writeback pages if !sync and migratepage is
|
||||
* not pointing to migrate_page() which is nonblocking
|
||||
* (swapcache/tmpfs uses migratepage = migrate_page).
|
||||
* Most pages have a mapping and most filesystems provide a
|
||||
* migratepage callback. Anonymous pages are part of swap
|
||||
* space which also has its own migratepage callback. This
|
||||
* is the most common path for page migration.
|
||||
*/
|
||||
if (PageDirty(page) && !sync &&
|
||||
mapping->a_ops->migratepage != migrate_page)
|
||||
rc = -EBUSY;
|
||||
else if (mapping->a_ops->migratepage)
|
||||
/*
|
||||
* Most pages have a mapping and most filesystems
|
||||
* should provide a migration function. Anonymous
|
||||
* pages are part of swap space which also has its
|
||||
* own migration function. This is the most common
|
||||
* path for page migration.
|
||||
*/
|
||||
rc = mapping->a_ops->migratepage(mapping,
|
||||
newpage, page);
|
||||
else
|
||||
rc = fallback_migrate_page(mapping, newpage, page);
|
||||
}
|
||||
rc = mapping->a_ops->migratepage(mapping,
|
||||
newpage, page, mode);
|
||||
else
|
||||
rc = fallback_migrate_page(mapping, newpage, page, mode);
|
||||
|
||||
if (rc) {
|
||||
newpage->mapping = NULL;
|
||||
@ -616,7 +675,7 @@ static int move_to_new_page(struct page *newpage, struct page *page,
|
||||
}
|
||||
|
||||
static int __unmap_and_move(struct page *page, struct page *newpage,
|
||||
int force, bool offlining, bool sync)
|
||||
int force, bool offlining, enum migrate_mode mode)
|
||||
{
|
||||
int rc = -EAGAIN;
|
||||
int remap_swapcache = 1;
|
||||
@ -625,7 +684,7 @@ static int __unmap_and_move(struct page *page, struct page *newpage,
|
||||
struct anon_vma *anon_vma = NULL;
|
||||
|
||||
if (!trylock_page(page)) {
|
||||
if (!force || !sync)
|
||||
if (!force || mode == MIGRATE_ASYNC)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
@ -671,10 +730,12 @@ static int __unmap_and_move(struct page *page, struct page *newpage,
|
||||
|
||||
if (PageWriteback(page)) {
|
||||
/*
|
||||
* For !sync, there is no point retrying as the retry loop
|
||||
* is expected to be too short for PageWriteback to be cleared
|
||||
* Only in the case of a full syncronous migration is it
|
||||
* necessary to wait for PageWriteback. In the async case,
|
||||
* the retry loop is too short and in the sync-light case,
|
||||
* the overhead of stalling is too much
|
||||
*/
|
||||
if (!sync) {
|
||||
if (mode != MIGRATE_SYNC) {
|
||||
rc = -EBUSY;
|
||||
goto uncharge;
|
||||
}
|
||||
@ -745,7 +806,7 @@ static int __unmap_and_move(struct page *page, struct page *newpage,
|
||||
|
||||
skip_unmap:
|
||||
if (!page_mapped(page))
|
||||
rc = move_to_new_page(newpage, page, remap_swapcache, sync);
|
||||
rc = move_to_new_page(newpage, page, remap_swapcache, mode);
|
||||
|
||||
if (rc && remap_swapcache)
|
||||
remove_migration_ptes(page, page);
|
||||
@ -768,7 +829,8 @@ out:
|
||||
* 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, bool offlining, bool sync)
|
||||
struct page *page, int force, bool offlining,
|
||||
enum migrate_mode mode)
|
||||
{
|
||||
int rc = 0;
|
||||
int *result = NULL;
|
||||
@ -777,6 +839,8 @@ static int unmap_and_move(new_page_t get_new_page, unsigned long private,
|
||||
if (!newpage)
|
||||
return -ENOMEM;
|
||||
|
||||
mem_cgroup_reset_owner(newpage);
|
||||
|
||||
if (page_count(page) == 1) {
|
||||
/* page was freed from under us. So we are done. */
|
||||
goto out;
|
||||
@ -786,7 +850,7 @@ static int unmap_and_move(new_page_t get_new_page, unsigned long private,
|
||||
if (unlikely(split_huge_page(page)))
|
||||
goto out;
|
||||
|
||||
rc = __unmap_and_move(page, newpage, force, offlining, sync);
|
||||
rc = __unmap_and_move(page, newpage, force, offlining, mode);
|
||||
out:
|
||||
if (rc != -EAGAIN) {
|
||||
/*
|
||||
@ -834,7 +898,8 @@ out:
|
||||
*/
|
||||
static int unmap_and_move_huge_page(new_page_t get_new_page,
|
||||
unsigned long private, struct page *hpage,
|
||||
int force, bool offlining, bool sync)
|
||||
int force, bool offlining,
|
||||
enum migrate_mode mode)
|
||||
{
|
||||
int rc = 0;
|
||||
int *result = NULL;
|
||||
@ -847,7 +912,7 @@ static int unmap_and_move_huge_page(new_page_t get_new_page,
|
||||
rc = -EAGAIN;
|
||||
|
||||
if (!trylock_page(hpage)) {
|
||||
if (!force || !sync)
|
||||
if (!force || mode != MIGRATE_SYNC)
|
||||
goto out;
|
||||
lock_page(hpage);
|
||||
}
|
||||
@ -858,7 +923,7 @@ static int unmap_and_move_huge_page(new_page_t get_new_page,
|
||||
try_to_unmap(hpage, TTU_MIGRATION|TTU_IGNORE_MLOCK|TTU_IGNORE_ACCESS);
|
||||
|
||||
if (!page_mapped(hpage))
|
||||
rc = move_to_new_page(new_hpage, hpage, 1, sync);
|
||||
rc = move_to_new_page(new_hpage, hpage, 1, mode);
|
||||
|
||||
if (rc)
|
||||
remove_migration_ptes(hpage, hpage);
|
||||
@ -901,7 +966,7 @@ out:
|
||||
*/
|
||||
int migrate_pages(struct list_head *from,
|
||||
new_page_t get_new_page, unsigned long private, bool offlining,
|
||||
bool sync)
|
||||
enum migrate_mode mode)
|
||||
{
|
||||
int retry = 1;
|
||||
int nr_failed = 0;
|
||||
@ -922,7 +987,7 @@ int migrate_pages(struct list_head *from,
|
||||
|
||||
rc = unmap_and_move(get_new_page, private,
|
||||
page, pass > 2, offlining,
|
||||
sync);
|
||||
mode);
|
||||
|
||||
switch(rc) {
|
||||
case -ENOMEM:
|
||||
@ -952,7 +1017,7 @@ out:
|
||||
|
||||
int migrate_huge_pages(struct list_head *from,
|
||||
new_page_t get_new_page, unsigned long private, bool offlining,
|
||||
bool sync)
|
||||
enum migrate_mode mode)
|
||||
{
|
||||
int retry = 1;
|
||||
int nr_failed = 0;
|
||||
@ -969,7 +1034,7 @@ int migrate_huge_pages(struct list_head *from,
|
||||
|
||||
rc = unmap_and_move_huge_page(get_new_page,
|
||||
private, page, pass > 2, offlining,
|
||||
sync);
|
||||
mode);
|
||||
|
||||
switch(rc) {
|
||||
case -ENOMEM:
|
||||
@ -1098,7 +1163,7 @@ set_status:
|
||||
err = 0;
|
||||
if (!list_empty(&pagelist)) {
|
||||
err = migrate_pages(&pagelist, new_page_node,
|
||||
(unsigned long)pm, 0, true);
|
||||
(unsigned long)pm, 0, MIGRATE_SYNC);
|
||||
if (err)
|
||||
putback_lru_pages(&pagelist);
|
||||
}
|
||||
|
@ -152,7 +152,7 @@ struct task_struct *find_lock_task_mm(struct task_struct *p)
|
||||
|
||||
/* return true if the task is not adequate as candidate victim task. */
|
||||
static bool oom_unkillable_task(struct task_struct *p,
|
||||
const struct mem_cgroup *mem, const nodemask_t *nodemask)
|
||||
const struct mem_cgroup *memcg, const nodemask_t *nodemask)
|
||||
{
|
||||
if (is_global_init(p))
|
||||
return true;
|
||||
@ -160,7 +160,7 @@ static bool oom_unkillable_task(struct task_struct *p,
|
||||
return true;
|
||||
|
||||
/* When mem_cgroup_out_of_memory() and p is not member of the group */
|
||||
if (mem && !task_in_mem_cgroup(p, mem))
|
||||
if (memcg && !task_in_mem_cgroup(p, memcg))
|
||||
return true;
|
||||
|
||||
/* p may not have freeable memory in nodemask */
|
||||
@ -179,12 +179,12 @@ static bool oom_unkillable_task(struct task_struct *p,
|
||||
* predictable as possible. The goal is to return the highest value for the
|
||||
* task consuming the most memory to avoid subsequent oom failures.
|
||||
*/
|
||||
unsigned int oom_badness(struct task_struct *p, struct mem_cgroup *mem,
|
||||
unsigned int oom_badness(struct task_struct *p, struct mem_cgroup *memcg,
|
||||
const nodemask_t *nodemask, unsigned long totalpages)
|
||||
{
|
||||
long points;
|
||||
|
||||
if (oom_unkillable_task(p, mem, nodemask))
|
||||
if (oom_unkillable_task(p, memcg, nodemask))
|
||||
return 0;
|
||||
|
||||
p = find_lock_task_mm(p);
|
||||
@ -308,7 +308,7 @@ static enum oom_constraint constrained_alloc(struct zonelist *zonelist,
|
||||
* (not docbooked, we don't want this one cluttering up the manual)
|
||||
*/
|
||||
static struct task_struct *select_bad_process(unsigned int *ppoints,
|
||||
unsigned long totalpages, struct mem_cgroup *mem,
|
||||
unsigned long totalpages, struct mem_cgroup *memcg,
|
||||
const nodemask_t *nodemask)
|
||||
{
|
||||
struct task_struct *g, *p;
|
||||
@ -320,7 +320,7 @@ static struct task_struct *select_bad_process(unsigned int *ppoints,
|
||||
|
||||
if (p->exit_state)
|
||||
continue;
|
||||
if (oom_unkillable_task(p, mem, nodemask))
|
||||
if (oom_unkillable_task(p, memcg, nodemask))
|
||||
continue;
|
||||
|
||||
/*
|
||||
@ -364,7 +364,7 @@ static struct task_struct *select_bad_process(unsigned int *ppoints,
|
||||
}
|
||||
}
|
||||
|
||||
points = oom_badness(p, mem, nodemask, totalpages);
|
||||
points = oom_badness(p, memcg, nodemask, totalpages);
|
||||
if (points > *ppoints) {
|
||||
chosen = p;
|
||||
*ppoints = points;
|
||||
@ -387,14 +387,14 @@ static struct task_struct *select_bad_process(unsigned int *ppoints,
|
||||
*
|
||||
* Call with tasklist_lock read-locked.
|
||||
*/
|
||||
static void dump_tasks(const struct mem_cgroup *mem, const nodemask_t *nodemask)
|
||||
static void dump_tasks(const struct mem_cgroup *memcg, const nodemask_t *nodemask)
|
||||
{
|
||||
struct task_struct *p;
|
||||
struct task_struct *task;
|
||||
|
||||
pr_info("[ pid ] uid tgid total_vm rss cpu oom_adj oom_score_adj name\n");
|
||||
for_each_process(p) {
|
||||
if (oom_unkillable_task(p, mem, nodemask))
|
||||
if (oom_unkillable_task(p, memcg, nodemask))
|
||||
continue;
|
||||
|
||||
task = find_lock_task_mm(p);
|
||||
@ -417,7 +417,7 @@ static void dump_tasks(const struct mem_cgroup *mem, const nodemask_t *nodemask)
|
||||
}
|
||||
|
||||
static void dump_header(struct task_struct *p, gfp_t gfp_mask, int order,
|
||||
struct mem_cgroup *mem, const nodemask_t *nodemask)
|
||||
struct mem_cgroup *memcg, const nodemask_t *nodemask)
|
||||
{
|
||||
task_lock(current);
|
||||
pr_warning("%s invoked oom-killer: gfp_mask=0x%x, order=%d, "
|
||||
@ -427,14 +427,14 @@ static void dump_header(struct task_struct *p, gfp_t gfp_mask, int order,
|
||||
cpuset_print_task_mems_allowed(current);
|
||||
task_unlock(current);
|
||||
dump_stack();
|
||||
mem_cgroup_print_oom_info(mem, p);
|
||||
mem_cgroup_print_oom_info(memcg, p);
|
||||
show_mem(SHOW_MEM_FILTER_NODES);
|
||||
if (sysctl_oom_dump_tasks)
|
||||
dump_tasks(mem, nodemask);
|
||||
dump_tasks(memcg, nodemask);
|
||||
}
|
||||
|
||||
#define K(x) ((x) << (PAGE_SHIFT-10))
|
||||
static int oom_kill_task(struct task_struct *p, struct mem_cgroup *mem)
|
||||
static int oom_kill_task(struct task_struct *p)
|
||||
{
|
||||
struct task_struct *q;
|
||||
struct mm_struct *mm;
|
||||
@ -484,7 +484,7 @@ static int oom_kill_task(struct task_struct *p, struct mem_cgroup *mem)
|
||||
|
||||
static int oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order,
|
||||
unsigned int points, unsigned long totalpages,
|
||||
struct mem_cgroup *mem, nodemask_t *nodemask,
|
||||
struct mem_cgroup *memcg, nodemask_t *nodemask,
|
||||
const char *message)
|
||||
{
|
||||
struct task_struct *victim = p;
|
||||
@ -493,7 +493,7 @@ static int oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order,
|
||||
unsigned int victim_points = 0;
|
||||
|
||||
if (printk_ratelimit())
|
||||
dump_header(p, gfp_mask, order, mem, nodemask);
|
||||
dump_header(p, gfp_mask, order, memcg, nodemask);
|
||||
|
||||
/*
|
||||
* If the task is already exiting, don't alarm the sysadmin or kill
|
||||
@ -524,7 +524,7 @@ static int oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order,
|
||||
/*
|
||||
* oom_badness() returns 0 if the thread is unkillable
|
||||
*/
|
||||
child_points = oom_badness(child, mem, nodemask,
|
||||
child_points = oom_badness(child, memcg, nodemask,
|
||||
totalpages);
|
||||
if (child_points > victim_points) {
|
||||
victim = child;
|
||||
@ -533,7 +533,7 @@ static int oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order,
|
||||
}
|
||||
} while_each_thread(p, t);
|
||||
|
||||
return oom_kill_task(victim, mem);
|
||||
return oom_kill_task(victim);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -561,7 +561,7 @@ static void check_panic_on_oom(enum oom_constraint constraint, gfp_t gfp_mask,
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CGROUP_MEM_RES_CTLR
|
||||
void mem_cgroup_out_of_memory(struct mem_cgroup *mem, gfp_t gfp_mask)
|
||||
void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask)
|
||||
{
|
||||
unsigned long limit;
|
||||
unsigned int points = 0;
|
||||
@ -578,14 +578,14 @@ void mem_cgroup_out_of_memory(struct mem_cgroup *mem, gfp_t gfp_mask)
|
||||
}
|
||||
|
||||
check_panic_on_oom(CONSTRAINT_MEMCG, gfp_mask, 0, NULL);
|
||||
limit = mem_cgroup_get_limit(mem) >> PAGE_SHIFT;
|
||||
limit = mem_cgroup_get_limit(memcg) >> PAGE_SHIFT;
|
||||
read_lock(&tasklist_lock);
|
||||
retry:
|
||||
p = select_bad_process(&points, limit, mem, NULL);
|
||||
p = select_bad_process(&points, limit, memcg, NULL);
|
||||
if (!p || PTR_ERR(p) == -1UL)
|
||||
goto out;
|
||||
|
||||
if (oom_kill_process(p, gfp_mask, 0, points, limit, mem, NULL,
|
||||
if (oom_kill_process(p, gfp_mask, 0, points, limit, memcg, NULL,
|
||||
"Memory cgroup out of memory"))
|
||||
goto retry;
|
||||
out:
|
||||
|
@ -1981,14 +1981,20 @@ 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, unsigned long *did_some_progress,
|
||||
bool sync_migration)
|
||||
int migratetype, bool sync_migration,
|
||||
bool *deferred_compaction,
|
||||
unsigned long *did_some_progress)
|
||||
{
|
||||
struct page *page;
|
||||
|
||||
if (!order || compaction_deferred(preferred_zone))
|
||||
if (!order)
|
||||
return NULL;
|
||||
|
||||
if (compaction_deferred(preferred_zone)) {
|
||||
*deferred_compaction = true;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
current->flags |= PF_MEMALLOC;
|
||||
*did_some_progress = try_to_compact_pages(zonelist, order, gfp_mask,
|
||||
nodemask, sync_migration);
|
||||
@ -2016,7 +2022,13 @@ __alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order,
|
||||
* but not enough to satisfy watermarks.
|
||||
*/
|
||||
count_vm_event(COMPACTFAIL);
|
||||
defer_compaction(preferred_zone);
|
||||
|
||||
/*
|
||||
* As async compaction considers a subset of pageblocks, only
|
||||
* defer if the failure was a sync compaction failure.
|
||||
*/
|
||||
if (sync_migration)
|
||||
defer_compaction(preferred_zone);
|
||||
|
||||
cond_resched();
|
||||
}
|
||||
@ -2028,8 +2040,9 @@ 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, unsigned long *did_some_progress,
|
||||
bool sync_migration)
|
||||
int migratetype, bool sync_migration,
|
||||
bool *deferred_compaction,
|
||||
unsigned long *did_some_progress)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
@ -2179,6 +2192,7 @@ __alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order,
|
||||
unsigned long pages_reclaimed = 0;
|
||||
unsigned long did_some_progress;
|
||||
bool sync_migration = false;
|
||||
bool deferred_compaction = false;
|
||||
|
||||
/*
|
||||
* In the slowpath, we sanity check order to avoid ever trying to
|
||||
@ -2259,12 +2273,22 @@ rebalance:
|
||||
zonelist, high_zoneidx,
|
||||
nodemask,
|
||||
alloc_flags, preferred_zone,
|
||||
migratetype, &did_some_progress,
|
||||
sync_migration);
|
||||
migratetype, sync_migration,
|
||||
&deferred_compaction,
|
||||
&did_some_progress);
|
||||
if (page)
|
||||
goto got_pg;
|
||||
sync_migration = true;
|
||||
|
||||
/*
|
||||
* If compaction is deferred for high-order allocations, it is because
|
||||
* sync compaction recently failed. In this is the case and the caller
|
||||
* has requested the system not be heavily disrupted, fail the
|
||||
* allocation now instead of entering direct reclaim
|
||||
*/
|
||||
if (deferred_compaction && (gfp_mask & __GFP_NO_KSWAPD))
|
||||
goto nopage;
|
||||
|
||||
/* Try direct reclaim and then allocating */
|
||||
page = __alloc_pages_direct_reclaim(gfp_mask, order,
|
||||
zonelist, high_zoneidx,
|
||||
@ -2328,8 +2352,9 @@ rebalance:
|
||||
zonelist, high_zoneidx,
|
||||
nodemask,
|
||||
alloc_flags, preferred_zone,
|
||||
migratetype, &did_some_progress,
|
||||
sync_migration);
|
||||
migratetype, sync_migration,
|
||||
&deferred_compaction,
|
||||
&did_some_progress);
|
||||
if (page)
|
||||
goto got_pg;
|
||||
}
|
||||
@ -4237,7 +4262,7 @@ static void __paginginit free_area_init_core(struct pglist_data *pgdat,
|
||||
for (j = 0; j < MAX_NR_ZONES; j++) {
|
||||
struct zone *zone = pgdat->node_zones + j;
|
||||
unsigned long size, realsize, memmap_pages;
|
||||
enum lru_list l;
|
||||
enum lru_list lru;
|
||||
|
||||
size = zone_spanned_pages_in_node(nid, j, zones_size);
|
||||
realsize = size - zone_absent_pages_in_node(nid, j,
|
||||
@ -4287,8 +4312,8 @@ static void __paginginit free_area_init_core(struct pglist_data *pgdat,
|
||||
zone->zone_pgdat = pgdat;
|
||||
|
||||
zone_pcp_init(zone);
|
||||
for_each_lru(l)
|
||||
INIT_LIST_HEAD(&zone->lru[l].list);
|
||||
for_each_lru(lru)
|
||||
INIT_LIST_HEAD(&zone->lruvec.lists[lru]);
|
||||
zone->reclaim_stat.recent_rotated[0] = 0;
|
||||
zone->reclaim_stat.recent_rotated[1] = 0;
|
||||
zone->reclaim_stat.recent_scanned[0] = 0;
|
||||
@ -4642,8 +4667,10 @@ static void check_for_regular_memory(pg_data_t *pgdat)
|
||||
|
||||
for (zone_type = 0; zone_type <= ZONE_NORMAL; zone_type++) {
|
||||
struct zone *zone = &pgdat->node_zones[zone_type];
|
||||
if (zone->present_pages)
|
||||
if (zone->present_pages) {
|
||||
node_set_state(zone_to_nid(zone), N_NORMAL_MEMORY);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
164
mm/page_cgroup.c
164
mm/page_cgroup.c
@ -11,13 +11,6 @@
|
||||
#include <linux/swapops.h>
|
||||
#include <linux/kmemleak.h>
|
||||
|
||||
static void __meminit init_page_cgroup(struct page_cgroup *pc, unsigned long id)
|
||||
{
|
||||
pc->flags = 0;
|
||||
set_page_cgroup_array_id(pc, id);
|
||||
pc->mem_cgroup = NULL;
|
||||
INIT_LIST_HEAD(&pc->lru);
|
||||
}
|
||||
static unsigned long total_usage;
|
||||
|
||||
#if !defined(CONFIG_SPARSEMEM)
|
||||
@ -35,35 +28,27 @@ struct page_cgroup *lookup_page_cgroup(struct page *page)
|
||||
struct page_cgroup *base;
|
||||
|
||||
base = NODE_DATA(page_to_nid(page))->node_page_cgroup;
|
||||
#ifdef CONFIG_DEBUG_VM
|
||||
/*
|
||||
* The sanity checks the page allocator does upon freeing a
|
||||
* page can reach here before the page_cgroup arrays are
|
||||
* allocated when feeding a range of pages to the allocator
|
||||
* for the first time during bootup or memory hotplug.
|
||||
*/
|
||||
if (unlikely(!base))
|
||||
return NULL;
|
||||
|
||||
#endif
|
||||
offset = pfn - NODE_DATA(page_to_nid(page))->node_start_pfn;
|
||||
return base + offset;
|
||||
}
|
||||
|
||||
struct page *lookup_cgroup_page(struct page_cgroup *pc)
|
||||
{
|
||||
unsigned long pfn;
|
||||
struct page *page;
|
||||
pg_data_t *pgdat;
|
||||
|
||||
pgdat = NODE_DATA(page_cgroup_array_id(pc));
|
||||
pfn = pc - pgdat->node_page_cgroup + pgdat->node_start_pfn;
|
||||
page = pfn_to_page(pfn);
|
||||
VM_BUG_ON(pc != lookup_page_cgroup(page));
|
||||
return page;
|
||||
}
|
||||
|
||||
static int __init alloc_node_page_cgroup(int nid)
|
||||
{
|
||||
struct page_cgroup *base, *pc;
|
||||
struct page_cgroup *base;
|
||||
unsigned long table_size;
|
||||
unsigned long start_pfn, nr_pages, index;
|
||||
unsigned long nr_pages;
|
||||
|
||||
start_pfn = NODE_DATA(nid)->node_start_pfn;
|
||||
nr_pages = NODE_DATA(nid)->node_spanned_pages;
|
||||
|
||||
if (!nr_pages)
|
||||
return 0;
|
||||
|
||||
@ -73,10 +58,6 @@ static int __init alloc_node_page_cgroup(int nid)
|
||||
table_size, PAGE_SIZE, __pa(MAX_DMA_ADDRESS));
|
||||
if (!base)
|
||||
return -ENOMEM;
|
||||
for (index = 0; index < nr_pages; index++) {
|
||||
pc = base + index;
|
||||
init_page_cgroup(pc, nid);
|
||||
}
|
||||
NODE_DATA(nid)->node_page_cgroup = base;
|
||||
total_usage += table_size;
|
||||
return 0;
|
||||
@ -111,29 +92,23 @@ struct page_cgroup *lookup_page_cgroup(struct page *page)
|
||||
{
|
||||
unsigned long pfn = page_to_pfn(page);
|
||||
struct mem_section *section = __pfn_to_section(pfn);
|
||||
|
||||
#ifdef CONFIG_DEBUG_VM
|
||||
/*
|
||||
* The sanity checks the page allocator does upon freeing a
|
||||
* page can reach here before the page_cgroup arrays are
|
||||
* allocated when feeding a range of pages to the allocator
|
||||
* for the first time during bootup or memory hotplug.
|
||||
*/
|
||||
if (!section->page_cgroup)
|
||||
return NULL;
|
||||
#endif
|
||||
return section->page_cgroup + pfn;
|
||||
}
|
||||
|
||||
struct page *lookup_cgroup_page(struct page_cgroup *pc)
|
||||
{
|
||||
struct mem_section *section;
|
||||
struct page *page;
|
||||
unsigned long nr;
|
||||
|
||||
nr = page_cgroup_array_id(pc);
|
||||
section = __nr_to_section(nr);
|
||||
page = pfn_to_page(pc - section->page_cgroup);
|
||||
VM_BUG_ON(pc != lookup_page_cgroup(page));
|
||||
return page;
|
||||
}
|
||||
|
||||
static void *__meminit alloc_page_cgroup(size_t size, int nid)
|
||||
{
|
||||
gfp_t flags = GFP_KERNEL | __GFP_ZERO | __GFP_NOWARN;
|
||||
void *addr = NULL;
|
||||
gfp_t flags = GFP_KERNEL | __GFP_NOWARN;
|
||||
|
||||
addr = alloc_pages_exact_nid(nid, size, flags);
|
||||
if (addr) {
|
||||
@ -142,39 +117,20 @@ static void *__meminit alloc_page_cgroup(size_t size, int nid)
|
||||
}
|
||||
|
||||
if (node_state(nid, N_HIGH_MEMORY))
|
||||
addr = vmalloc_node(size, nid);
|
||||
addr = vzalloc_node(size, nid);
|
||||
else
|
||||
addr = vmalloc(size);
|
||||
addr = vzalloc(size);
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MEMORY_HOTPLUG
|
||||
static void free_page_cgroup(void *addr)
|
||||
{
|
||||
if (is_vmalloc_addr(addr)) {
|
||||
vfree(addr);
|
||||
} else {
|
||||
struct page *page = virt_to_page(addr);
|
||||
size_t table_size =
|
||||
sizeof(struct page_cgroup) * PAGES_PER_SECTION;
|
||||
|
||||
BUG_ON(PageReserved(page));
|
||||
free_pages_exact(addr, table_size);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static int __meminit init_section_page_cgroup(unsigned long pfn, int nid)
|
||||
{
|
||||
struct page_cgroup *base, *pc;
|
||||
struct mem_section *section;
|
||||
struct page_cgroup *base;
|
||||
unsigned long table_size;
|
||||
unsigned long nr;
|
||||
int index;
|
||||
|
||||
nr = pfn_to_section_nr(pfn);
|
||||
section = __nr_to_section(nr);
|
||||
section = __pfn_to_section(pfn);
|
||||
|
||||
if (section->page_cgroup)
|
||||
return 0;
|
||||
@ -194,10 +150,6 @@ static int __meminit init_section_page_cgroup(unsigned long pfn, int nid)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (index = 0; index < PAGES_PER_SECTION; index++) {
|
||||
pc = base + index;
|
||||
init_page_cgroup(pc, nr);
|
||||
}
|
||||
/*
|
||||
* The passed "pfn" may not be aligned to SECTION. For the calculation
|
||||
* we need to apply a mask.
|
||||
@ -208,6 +160,20 @@ static int __meminit init_section_page_cgroup(unsigned long pfn, int nid)
|
||||
return 0;
|
||||
}
|
||||
#ifdef CONFIG_MEMORY_HOTPLUG
|
||||
static void free_page_cgroup(void *addr)
|
||||
{
|
||||
if (is_vmalloc_addr(addr)) {
|
||||
vfree(addr);
|
||||
} else {
|
||||
struct page *page = virt_to_page(addr);
|
||||
size_t table_size =
|
||||
sizeof(struct page_cgroup) * PAGES_PER_SECTION;
|
||||
|
||||
BUG_ON(PageReserved(page));
|
||||
free_pages_exact(addr, table_size);
|
||||
}
|
||||
}
|
||||
|
||||
void __free_page_cgroup(unsigned long pfn)
|
||||
{
|
||||
struct mem_section *ms;
|
||||
@ -366,7 +332,6 @@ struct swap_cgroup {
|
||||
unsigned short id;
|
||||
};
|
||||
#define SC_PER_PAGE (PAGE_SIZE/sizeof(struct swap_cgroup))
|
||||
#define SC_POS_MASK (SC_PER_PAGE - 1)
|
||||
|
||||
/*
|
||||
* SwapCgroup implements "lookup" and "exchange" operations.
|
||||
@ -408,6 +373,21 @@ not_enough_page:
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static struct swap_cgroup *lookup_swap_cgroup(swp_entry_t ent,
|
||||
struct swap_cgroup_ctrl **ctrlp)
|
||||
{
|
||||
pgoff_t offset = swp_offset(ent);
|
||||
struct swap_cgroup_ctrl *ctrl;
|
||||
struct page *mappage;
|
||||
|
||||
ctrl = &swap_cgroup_ctrl[swp_type(ent)];
|
||||
if (ctrlp)
|
||||
*ctrlp = ctrl;
|
||||
|
||||
mappage = ctrl->map[offset / SC_PER_PAGE];
|
||||
return page_address(mappage) + offset % SC_PER_PAGE;
|
||||
}
|
||||
|
||||
/**
|
||||
* swap_cgroup_cmpxchg - cmpxchg mem_cgroup's id for this swp_entry.
|
||||
* @end: swap entry to be cmpxchged
|
||||
@ -420,21 +400,13 @@ not_enough_page:
|
||||
unsigned short swap_cgroup_cmpxchg(swp_entry_t ent,
|
||||
unsigned short old, unsigned short new)
|
||||
{
|
||||
int type = swp_type(ent);
|
||||
unsigned long offset = swp_offset(ent);
|
||||
unsigned long idx = offset / SC_PER_PAGE;
|
||||
unsigned long pos = offset & SC_POS_MASK;
|
||||
struct swap_cgroup_ctrl *ctrl;
|
||||
struct page *mappage;
|
||||
struct swap_cgroup *sc;
|
||||
unsigned long flags;
|
||||
unsigned short retval;
|
||||
|
||||
ctrl = &swap_cgroup_ctrl[type];
|
||||
sc = lookup_swap_cgroup(ent, &ctrl);
|
||||
|
||||
mappage = ctrl->map[idx];
|
||||
sc = page_address(mappage);
|
||||
sc += pos;
|
||||
spin_lock_irqsave(&ctrl->lock, flags);
|
||||
retval = sc->id;
|
||||
if (retval == old)
|
||||
@ -455,21 +427,13 @@ unsigned short swap_cgroup_cmpxchg(swp_entry_t ent,
|
||||
*/
|
||||
unsigned short swap_cgroup_record(swp_entry_t ent, unsigned short id)
|
||||
{
|
||||
int type = swp_type(ent);
|
||||
unsigned long offset = swp_offset(ent);
|
||||
unsigned long idx = offset / SC_PER_PAGE;
|
||||
unsigned long pos = offset & SC_POS_MASK;
|
||||
struct swap_cgroup_ctrl *ctrl;
|
||||
struct page *mappage;
|
||||
struct swap_cgroup *sc;
|
||||
unsigned short old;
|
||||
unsigned long flags;
|
||||
|
||||
ctrl = &swap_cgroup_ctrl[type];
|
||||
sc = lookup_swap_cgroup(ent, &ctrl);
|
||||
|
||||
mappage = ctrl->map[idx];
|
||||
sc = page_address(mappage);
|
||||
sc += pos;
|
||||
spin_lock_irqsave(&ctrl->lock, flags);
|
||||
old = sc->id;
|
||||
sc->id = id;
|
||||
@ -479,28 +443,14 @@ unsigned short swap_cgroup_record(swp_entry_t ent, unsigned short id)
|
||||
}
|
||||
|
||||
/**
|
||||
* lookup_swap_cgroup - lookup mem_cgroup tied to swap entry
|
||||
* lookup_swap_cgroup_id - lookup mem_cgroup id tied to swap entry
|
||||
* @ent: swap entry to be looked up.
|
||||
*
|
||||
* Returns CSS ID of mem_cgroup at success. 0 at failure. (0 is invalid ID)
|
||||
*/
|
||||
unsigned short lookup_swap_cgroup(swp_entry_t ent)
|
||||
unsigned short lookup_swap_cgroup_id(swp_entry_t ent)
|
||||
{
|
||||
int type = swp_type(ent);
|
||||
unsigned long offset = swp_offset(ent);
|
||||
unsigned long idx = offset / SC_PER_PAGE;
|
||||
unsigned long pos = offset & SC_POS_MASK;
|
||||
struct swap_cgroup_ctrl *ctrl;
|
||||
struct page *mappage;
|
||||
struct swap_cgroup *sc;
|
||||
unsigned short ret;
|
||||
|
||||
ctrl = &swap_cgroup_ctrl[type];
|
||||
mappage = ctrl->map[idx];
|
||||
sc = page_address(mappage);
|
||||
sc += pos;
|
||||
ret = sc->id;
|
||||
return ret;
|
||||
return lookup_swap_cgroup(ent, NULL)->id;
|
||||
}
|
||||
|
||||
int swap_cgroup_swapon(int type, unsigned long max_pages)
|
||||
|
20
mm/rmap.c
20
mm/rmap.c
@ -773,7 +773,7 @@ out:
|
||||
}
|
||||
|
||||
static int page_referenced_anon(struct page *page,
|
||||
struct mem_cgroup *mem_cont,
|
||||
struct mem_cgroup *memcg,
|
||||
unsigned long *vm_flags)
|
||||
{
|
||||
unsigned int mapcount;
|
||||
@ -796,7 +796,7 @@ static int page_referenced_anon(struct page *page,
|
||||
* counting on behalf of references from different
|
||||
* cgroups
|
||||
*/
|
||||
if (mem_cont && !mm_match_cgroup(vma->vm_mm, mem_cont))
|
||||
if (memcg && !mm_match_cgroup(vma->vm_mm, memcg))
|
||||
continue;
|
||||
referenced += page_referenced_one(page, vma, address,
|
||||
&mapcount, vm_flags);
|
||||
@ -811,7 +811,7 @@ static int page_referenced_anon(struct page *page,
|
||||
/**
|
||||
* page_referenced_file - referenced check for object-based rmap
|
||||
* @page: the page we're checking references on.
|
||||
* @mem_cont: target memory controller
|
||||
* @memcg: target memory control group
|
||||
* @vm_flags: collect encountered vma->vm_flags who actually referenced the page
|
||||
*
|
||||
* For an object-based mapped page, find all the places it is mapped and
|
||||
@ -822,7 +822,7 @@ static int page_referenced_anon(struct page *page,
|
||||
* This function is only called from page_referenced for object-based pages.
|
||||
*/
|
||||
static int page_referenced_file(struct page *page,
|
||||
struct mem_cgroup *mem_cont,
|
||||
struct mem_cgroup *memcg,
|
||||
unsigned long *vm_flags)
|
||||
{
|
||||
unsigned int mapcount;
|
||||
@ -864,7 +864,7 @@ static int page_referenced_file(struct page *page,
|
||||
* counting on behalf of references from different
|
||||
* cgroups
|
||||
*/
|
||||
if (mem_cont && !mm_match_cgroup(vma->vm_mm, mem_cont))
|
||||
if (memcg && !mm_match_cgroup(vma->vm_mm, memcg))
|
||||
continue;
|
||||
referenced += page_referenced_one(page, vma, address,
|
||||
&mapcount, vm_flags);
|
||||
@ -880,7 +880,7 @@ static int page_referenced_file(struct page *page,
|
||||
* page_referenced - test if the page was referenced
|
||||
* @page: the page to test
|
||||
* @is_locked: caller holds lock on the page
|
||||
* @mem_cont: target memory controller
|
||||
* @memcg: target memory cgroup
|
||||
* @vm_flags: collect encountered vma->vm_flags who actually referenced the page
|
||||
*
|
||||
* Quick test_and_clear_referenced for all mappings to a page,
|
||||
@ -888,7 +888,7 @@ static int page_referenced_file(struct page *page,
|
||||
*/
|
||||
int page_referenced(struct page *page,
|
||||
int is_locked,
|
||||
struct mem_cgroup *mem_cont,
|
||||
struct mem_cgroup *memcg,
|
||||
unsigned long *vm_flags)
|
||||
{
|
||||
int referenced = 0;
|
||||
@ -904,13 +904,13 @@ int page_referenced(struct page *page,
|
||||
}
|
||||
}
|
||||
if (unlikely(PageKsm(page)))
|
||||
referenced += page_referenced_ksm(page, mem_cont,
|
||||
referenced += page_referenced_ksm(page, memcg,
|
||||
vm_flags);
|
||||
else if (PageAnon(page))
|
||||
referenced += page_referenced_anon(page, mem_cont,
|
||||
referenced += page_referenced_anon(page, memcg,
|
||||
vm_flags);
|
||||
else if (page->mapping)
|
||||
referenced += page_referenced_file(page, mem_cont,
|
||||
referenced += page_referenced_file(page, memcg,
|
||||
vm_flags);
|
||||
if (we_locked)
|
||||
unlock_page(page);
|
||||
|
@ -366,7 +366,8 @@ static inline bool __cmpxchg_double_slab(struct kmem_cache *s, struct page *page
|
||||
const char *n)
|
||||
{
|
||||
VM_BUG_ON(!irqs_disabled());
|
||||
#ifdef CONFIG_CMPXCHG_DOUBLE
|
||||
#if defined(CONFIG_HAVE_CMPXCHG_DOUBLE) && \
|
||||
defined(CONFIG_HAVE_ALIGNED_STRUCT_PAGE)
|
||||
if (s->flags & __CMPXCHG_DOUBLE) {
|
||||
if (cmpxchg_double(&page->freelist, &page->counters,
|
||||
freelist_old, counters_old,
|
||||
@ -400,7 +401,8 @@ static inline bool cmpxchg_double_slab(struct kmem_cache *s, struct page *page,
|
||||
void *freelist_new, unsigned long counters_new,
|
||||
const char *n)
|
||||
{
|
||||
#ifdef CONFIG_CMPXCHG_DOUBLE
|
||||
#if defined(CONFIG_HAVE_CMPXCHG_DOUBLE) && \
|
||||
defined(CONFIG_HAVE_ALIGNED_STRUCT_PAGE)
|
||||
if (s->flags & __CMPXCHG_DOUBLE) {
|
||||
if (cmpxchg_double(&page->freelist, &page->counters,
|
||||
freelist_old, counters_old,
|
||||
@ -3014,7 +3016,8 @@ static int kmem_cache_open(struct kmem_cache *s,
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CMPXCHG_DOUBLE
|
||||
#if defined(CONFIG_HAVE_CMPXCHG_DOUBLE) && \
|
||||
defined(CONFIG_HAVE_ALIGNED_STRUCT_PAGE)
|
||||
if (system_has_cmpxchg_double() && (s->flags & SLAB_DEBUG_FLAGS) == 0)
|
||||
/* Enable fast mode */
|
||||
s->flags |= __CMPXCHG_DOUBLE;
|
||||
|
79
mm/swap.c
79
mm/swap.c
@ -23,7 +23,6 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/mm_inline.h>
|
||||
#include <linux/buffer_head.h> /* for try_to_release_page() */
|
||||
#include <linux/percpu_counter.h>
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/cpu.h>
|
||||
@ -54,7 +53,7 @@ static void __page_cache_release(struct page *page)
|
||||
spin_lock_irqsave(&zone->lru_lock, flags);
|
||||
VM_BUG_ON(!PageLRU(page));
|
||||
__ClearPageLRU(page);
|
||||
del_page_from_lru(zone, page);
|
||||
del_page_from_lru_list(zone, page, page_off_lru(page));
|
||||
spin_unlock_irqrestore(&zone->lru_lock, flags);
|
||||
}
|
||||
}
|
||||
@ -232,12 +231,14 @@ static void pagevec_lru_move_fn(struct pagevec *pvec,
|
||||
static void pagevec_move_tail_fn(struct page *page, void *arg)
|
||||
{
|
||||
int *pgmoved = arg;
|
||||
struct zone *zone = page_zone(page);
|
||||
|
||||
if (PageLRU(page) && !PageActive(page) && !PageUnevictable(page)) {
|
||||
enum lru_list lru = page_lru_base_type(page);
|
||||
list_move_tail(&page->lru, &zone->lru[lru].list);
|
||||
mem_cgroup_rotate_reclaimable_page(page);
|
||||
struct lruvec *lruvec;
|
||||
|
||||
lruvec = mem_cgroup_lru_move_lists(page_zone(page),
|
||||
page, lru, lru);
|
||||
list_move_tail(&page->lru, &lruvec->lists[lru]);
|
||||
(*pgmoved)++;
|
||||
}
|
||||
}
|
||||
@ -368,7 +369,6 @@ void mark_page_accessed(struct page *page)
|
||||
SetPageReferenced(page);
|
||||
}
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(mark_page_accessed);
|
||||
|
||||
void __lru_cache_add(struct page *page, enum lru_list lru)
|
||||
@ -377,7 +377,7 @@ void __lru_cache_add(struct page *page, enum lru_list lru)
|
||||
|
||||
page_cache_get(page);
|
||||
if (!pagevec_add(pvec, page))
|
||||
____pagevec_lru_add(pvec, lru);
|
||||
__pagevec_lru_add(pvec, lru);
|
||||
put_cpu_var(lru_add_pvecs);
|
||||
}
|
||||
EXPORT_SYMBOL(__lru_cache_add);
|
||||
@ -476,12 +476,13 @@ static void lru_deactivate_fn(struct page *page, void *arg)
|
||||
*/
|
||||
SetPageReclaim(page);
|
||||
} else {
|
||||
struct lruvec *lruvec;
|
||||
/*
|
||||
* The page's writeback ends up during pagevec
|
||||
* We moves tha page into tail of inactive.
|
||||
*/
|
||||
list_move_tail(&page->lru, &zone->lru[lru].list);
|
||||
mem_cgroup_rotate_reclaimable_page(page);
|
||||
lruvec = mem_cgroup_lru_move_lists(zone, page, lru, lru);
|
||||
list_move_tail(&page->lru, &lruvec->lists[lru]);
|
||||
__count_vm_event(PGROTATED);
|
||||
}
|
||||
|
||||
@ -504,7 +505,7 @@ static void drain_cpu_pagevecs(int cpu)
|
||||
for_each_lru(lru) {
|
||||
pvec = &pvecs[lru - LRU_BASE];
|
||||
if (pagevec_count(pvec))
|
||||
____pagevec_lru_add(pvec, lru);
|
||||
__pagevec_lru_add(pvec, lru);
|
||||
}
|
||||
|
||||
pvec = &per_cpu(lru_rotate_pvecs, cpu);
|
||||
@ -616,7 +617,7 @@ void release_pages(struct page **pages, int nr, int cold)
|
||||
}
|
||||
VM_BUG_ON(!PageLRU(page));
|
||||
__ClearPageLRU(page);
|
||||
del_page_from_lru(zone, page);
|
||||
del_page_from_lru_list(zone, page, page_off_lru(page));
|
||||
}
|
||||
|
||||
list_add(&page->lru, &pages_to_free);
|
||||
@ -644,9 +645,9 @@ void __pagevec_release(struct pagevec *pvec)
|
||||
release_pages(pvec->pages, pagevec_count(pvec), pvec->cold);
|
||||
pagevec_reinit(pvec);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(__pagevec_release);
|
||||
|
||||
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
|
||||
/* used by __split_huge_page_refcount() */
|
||||
void lru_add_page_tail(struct zone* zone,
|
||||
struct page *page, struct page *page_tail)
|
||||
@ -654,7 +655,6 @@ void lru_add_page_tail(struct zone* zone,
|
||||
int active;
|
||||
enum lru_list lru;
|
||||
const int file = 0;
|
||||
struct list_head *head;
|
||||
|
||||
VM_BUG_ON(!PageHead(page));
|
||||
VM_BUG_ON(PageCompound(page_tail));
|
||||
@ -673,18 +673,30 @@ void lru_add_page_tail(struct zone* zone,
|
||||
lru = LRU_INACTIVE_ANON;
|
||||
}
|
||||
update_page_reclaim_stat(zone, page_tail, file, active);
|
||||
if (likely(PageLRU(page)))
|
||||
head = page->lru.prev;
|
||||
else
|
||||
head = &zone->lru[lru].list;
|
||||
__add_page_to_lru_list(zone, page_tail, lru, head);
|
||||
} else {
|
||||
SetPageUnevictable(page_tail);
|
||||
add_page_to_lru_list(zone, page_tail, LRU_UNEVICTABLE);
|
||||
lru = LRU_UNEVICTABLE;
|
||||
}
|
||||
|
||||
if (likely(PageLRU(page)))
|
||||
list_add_tail(&page_tail->lru, &page->lru);
|
||||
else {
|
||||
struct list_head *list_head;
|
||||
/*
|
||||
* Head page has not yet been counted, as an hpage,
|
||||
* so we must account for each subpage individually.
|
||||
*
|
||||
* Use the standard add function to put page_tail on the list,
|
||||
* but then correct its position so they all end up in order.
|
||||
*/
|
||||
add_page_to_lru_list(zone, page_tail, lru);
|
||||
list_head = page_tail->lru.prev;
|
||||
list_move_tail(&page_tail->lru, list_head);
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
|
||||
|
||||
static void ____pagevec_lru_add_fn(struct page *page, void *arg)
|
||||
static void __pagevec_lru_add_fn(struct page *page, void *arg)
|
||||
{
|
||||
enum lru_list lru = (enum lru_list)arg;
|
||||
struct zone *zone = page_zone(page);
|
||||
@ -706,32 +718,13 @@ static void ____pagevec_lru_add_fn(struct page *page, void *arg)
|
||||
* Add the passed pages to the LRU, then drop the caller's refcount
|
||||
* on them. Reinitialises the caller's pagevec.
|
||||
*/
|
||||
void ____pagevec_lru_add(struct pagevec *pvec, enum lru_list lru)
|
||||
void __pagevec_lru_add(struct pagevec *pvec, enum lru_list lru)
|
||||
{
|
||||
VM_BUG_ON(is_unevictable_lru(lru));
|
||||
|
||||
pagevec_lru_move_fn(pvec, ____pagevec_lru_add_fn, (void *)lru);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(____pagevec_lru_add);
|
||||
|
||||
/*
|
||||
* Try to drop buffers from the pages in a pagevec
|
||||
*/
|
||||
void pagevec_strip(struct pagevec *pvec)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < pagevec_count(pvec); i++) {
|
||||
struct page *page = pvec->pages[i];
|
||||
|
||||
if (page_has_private(page) && trylock_page(page)) {
|
||||
if (page_has_private(page))
|
||||
try_to_release_page(page, 0);
|
||||
unlock_page(page);
|
||||
}
|
||||
}
|
||||
pagevec_lru_move_fn(pvec, __pagevec_lru_add_fn, (void *)lru);
|
||||
}
|
||||
EXPORT_SYMBOL(__pagevec_lru_add);
|
||||
|
||||
/**
|
||||
* pagevec_lookup - gang pagecache lookup
|
||||
@ -755,7 +748,6 @@ unsigned pagevec_lookup(struct pagevec *pvec, struct address_space *mapping,
|
||||
pvec->nr = find_get_pages(mapping, start, nr_pages, pvec->pages);
|
||||
return pagevec_count(pvec);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(pagevec_lookup);
|
||||
|
||||
unsigned pagevec_lookup_tag(struct pagevec *pvec, struct address_space *mapping,
|
||||
@ -765,7 +757,6 @@ unsigned pagevec_lookup_tag(struct pagevec *pvec, struct address_space *mapping,
|
||||
nr_pages, pvec->pages);
|
||||
return pagevec_count(pvec);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(pagevec_lookup_tag);
|
||||
|
||||
/*
|
||||
|
@ -300,6 +300,16 @@ struct page *read_swap_cache_async(swp_entry_t entry, gfp_t gfp_mask,
|
||||
new_page = alloc_page_vma(gfp_mask, vma, addr);
|
||||
if (!new_page)
|
||||
break; /* Out of memory */
|
||||
/*
|
||||
* The memcg-specific accounting when moving
|
||||
* pages around the LRU lists relies on the
|
||||
* page's owner (memcg) to be valid. Usually,
|
||||
* pages are assigned to a new owner before
|
||||
* being put on the LRU list, but since this
|
||||
* is not the case here, the stale owner from
|
||||
* a previous allocation cycle must be reset.
|
||||
*/
|
||||
mem_cgroup_reset_owner(new_page);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -847,12 +847,13 @@ unsigned int count_swap_pages(int type, int free)
|
||||
static int unuse_pte(struct vm_area_struct *vma, pmd_t *pmd,
|
||||
unsigned long addr, swp_entry_t entry, struct page *page)
|
||||
{
|
||||
struct mem_cgroup *ptr;
|
||||
struct mem_cgroup *memcg;
|
||||
spinlock_t *ptl;
|
||||
pte_t *pte;
|
||||
int ret = 1;
|
||||
|
||||
if (mem_cgroup_try_charge_swapin(vma->vm_mm, page, GFP_KERNEL, &ptr)) {
|
||||
if (mem_cgroup_try_charge_swapin(vma->vm_mm, page,
|
||||
GFP_KERNEL, &memcg)) {
|
||||
ret = -ENOMEM;
|
||||
goto out_nolock;
|
||||
}
|
||||
@ -860,7 +861,7 @@ static int unuse_pte(struct vm_area_struct *vma, pmd_t *pmd,
|
||||
pte = pte_offset_map_lock(vma->vm_mm, pmd, addr, &ptl);
|
||||
if (unlikely(!pte_same(*pte, swp_entry_to_pte(entry)))) {
|
||||
if (ret > 0)
|
||||
mem_cgroup_cancel_charge_swapin(ptr);
|
||||
mem_cgroup_cancel_charge_swapin(memcg);
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
@ -871,7 +872,7 @@ static int unuse_pte(struct vm_area_struct *vma, pmd_t *pmd,
|
||||
set_pte_at(vma->vm_mm, addr, pte,
|
||||
pte_mkold(mk_pte(page, vma->vm_page_prot)));
|
||||
page_add_anon_rmap(page, vma, addr);
|
||||
mem_cgroup_commit_charge_swapin(page, ptr);
|
||||
mem_cgroup_commit_charge_swapin(page, memcg);
|
||||
swap_free(entry);
|
||||
/*
|
||||
* Move the page to the active list so it is not
|
||||
|
@ -2378,7 +2378,7 @@ struct vm_struct **pcpu_get_vm_areas(const unsigned long *offsets,
|
||||
vms = kzalloc(sizeof(vms[0]) * nr_vms, GFP_KERNEL);
|
||||
vas = kzalloc(sizeof(vas[0]) * nr_vms, GFP_KERNEL);
|
||||
if (!vas || !vms)
|
||||
goto err_free;
|
||||
goto err_free2;
|
||||
|
||||
for (area = 0; area < nr_vms; area++) {
|
||||
vas[area] = kzalloc(sizeof(struct vmap_area), GFP_KERNEL);
|
||||
@ -2476,11 +2476,10 @@ found:
|
||||
|
||||
err_free:
|
||||
for (area = 0; area < nr_vms; area++) {
|
||||
if (vas)
|
||||
kfree(vas[area]);
|
||||
if (vms)
|
||||
kfree(vms[area]);
|
||||
kfree(vas[area]);
|
||||
kfree(vms[area]);
|
||||
}
|
||||
err_free2:
|
||||
kfree(vas);
|
||||
kfree(vms);
|
||||
return NULL;
|
||||
|
688
mm/vmscan.c
688
mm/vmscan.c
File diff suppressed because it is too large
Load Diff
@ -295,7 +295,7 @@ void __dec_zone_page_state(struct page *page, enum zone_stat_item item)
|
||||
}
|
||||
EXPORT_SYMBOL(__dec_zone_page_state);
|
||||
|
||||
#ifdef CONFIG_CMPXCHG_LOCAL
|
||||
#ifdef CONFIG_HAVE_CMPXCHG_LOCAL
|
||||
/*
|
||||
* If we have cmpxchg_local support then we do not need to incur the overhead
|
||||
* that comes with local_irq_save/restore if we use this_cpu_cmpxchg.
|
||||
|
11
tools/testing/selftests/Makefile
Normal file
11
tools/testing/selftests/Makefile
Normal file
@ -0,0 +1,11 @@
|
||||
TARGETS = breakpoints
|
||||
|
||||
all:
|
||||
for TARGET in $(TARGETS); do \
|
||||
make -C $$TARGET; \
|
||||
done;
|
||||
|
||||
clean:
|
||||
for TARGET in $(TARGETS); do \
|
||||
make -C $$TARGET clean; \
|
||||
done;
|
20
tools/testing/selftests/breakpoints/Makefile
Normal file
20
tools/testing/selftests/breakpoints/Makefile
Normal file
@ -0,0 +1,20 @@
|
||||
# Taken from perf makefile
|
||||
uname_M := $(shell uname -m 2>/dev/null || echo not)
|
||||
ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/i386/)
|
||||
ifeq ($(ARCH),i386)
|
||||
ARCH := x86
|
||||
endif
|
||||
ifeq ($(ARCH),x86_64)
|
||||
ARCH := x86
|
||||
endif
|
||||
|
||||
|
||||
all:
|
||||
ifeq ($(ARCH),x86)
|
||||
gcc breakpoint_test.c -o run_test
|
||||
else
|
||||
echo "Not an x86 target, can't build breakpoints selftests"
|
||||
endif
|
||||
|
||||
clean:
|
||||
rm -fr run_test
|
394
tools/testing/selftests/breakpoints/breakpoint_test.c
Normal file
394
tools/testing/selftests/breakpoints/breakpoint_test.c
Normal file
@ -0,0 +1,394 @@
|
||||
/*
|
||||
* Copyright (C) 2011 Red Hat, Inc., Frederic Weisbecker <fweisbec@redhat.com>
|
||||
*
|
||||
* Licensed under the terms of the GNU GPL License version 2
|
||||
*
|
||||
* Selftests for breakpoints (and more generally the do_debug() path) in x86.
|
||||
*/
|
||||
|
||||
|
||||
#include <sys/ptrace.h>
|
||||
#include <unistd.h>
|
||||
#include <stddef.h>
|
||||
#include <sys/user.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
|
||||
/* Breakpoint access modes */
|
||||
enum {
|
||||
BP_X = 1,
|
||||
BP_RW = 2,
|
||||
BP_W = 4,
|
||||
};
|
||||
|
||||
static pid_t child_pid;
|
||||
|
||||
/*
|
||||
* Ensures the child and parent are always "talking" about
|
||||
* the same test sequence. (ie: that we haven't forgotten
|
||||
* to call check_trapped() somewhere).
|
||||
*/
|
||||
static int nr_tests;
|
||||
|
||||
static void set_breakpoint_addr(void *addr, int n)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ptrace(PTRACE_POKEUSER, child_pid,
|
||||
offsetof(struct user, u_debugreg[n]), addr);
|
||||
if (ret) {
|
||||
perror("Can't set breakpoint addr\n");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
static void toggle_breakpoint(int n, int type, int len,
|
||||
int local, int global, int set)
|
||||
{
|
||||
int ret;
|
||||
|
||||
int xtype, xlen;
|
||||
unsigned long vdr7, dr7;
|
||||
|
||||
switch (type) {
|
||||
case BP_X:
|
||||
xtype = 0;
|
||||
break;
|
||||
case BP_W:
|
||||
xtype = 1;
|
||||
break;
|
||||
case BP_RW:
|
||||
xtype = 3;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (len) {
|
||||
case 1:
|
||||
xlen = 0;
|
||||
break;
|
||||
case 2:
|
||||
xlen = 4;
|
||||
break;
|
||||
case 4:
|
||||
xlen = 0xc;
|
||||
break;
|
||||
case 8:
|
||||
xlen = 8;
|
||||
break;
|
||||
}
|
||||
|
||||
dr7 = ptrace(PTRACE_PEEKUSER, child_pid,
|
||||
offsetof(struct user, u_debugreg[7]), 0);
|
||||
|
||||
vdr7 = (xlen | xtype) << 16;
|
||||
vdr7 <<= 4 * n;
|
||||
|
||||
if (local) {
|
||||
vdr7 |= 1 << (2 * n);
|
||||
vdr7 |= 1 << 8;
|
||||
}
|
||||
if (global) {
|
||||
vdr7 |= 2 << (2 * n);
|
||||
vdr7 |= 1 << 9;
|
||||
}
|
||||
|
||||
if (set)
|
||||
dr7 |= vdr7;
|
||||
else
|
||||
dr7 &= ~vdr7;
|
||||
|
||||
ret = ptrace(PTRACE_POKEUSER, child_pid,
|
||||
offsetof(struct user, u_debugreg[7]), dr7);
|
||||
if (ret) {
|
||||
perror("Can't set dr7");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Dummy variables to test read/write accesses */
|
||||
static unsigned long long dummy_var[4];
|
||||
|
||||
/* Dummy functions to test execution accesses */
|
||||
static void dummy_func(void) { }
|
||||
static void dummy_func1(void) { }
|
||||
static void dummy_func2(void) { }
|
||||
static void dummy_func3(void) { }
|
||||
|
||||
static void (*dummy_funcs[])(void) = {
|
||||
dummy_func,
|
||||
dummy_func1,
|
||||
dummy_func2,
|
||||
dummy_func3,
|
||||
};
|
||||
|
||||
static int trapped;
|
||||
|
||||
static void check_trapped(void)
|
||||
{
|
||||
/*
|
||||
* If we haven't trapped, wake up the parent
|
||||
* so that it notices the failure.
|
||||
*/
|
||||
if (!trapped)
|
||||
kill(getpid(), SIGUSR1);
|
||||
trapped = 0;
|
||||
|
||||
nr_tests++;
|
||||
}
|
||||
|
||||
static void write_var(int len)
|
||||
{
|
||||
char *pcval; short *psval; int *pival; long long *plval;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
switch (len) {
|
||||
case 1:
|
||||
pcval = (char *)&dummy_var[i];
|
||||
*pcval = 0xff;
|
||||
break;
|
||||
case 2:
|
||||
psval = (short *)&dummy_var[i];
|
||||
*psval = 0xffff;
|
||||
break;
|
||||
case 4:
|
||||
pival = (int *)&dummy_var[i];
|
||||
*pival = 0xffffffff;
|
||||
break;
|
||||
case 8:
|
||||
plval = (long long *)&dummy_var[i];
|
||||
*plval = 0xffffffffffffffffLL;
|
||||
break;
|
||||
}
|
||||
check_trapped();
|
||||
}
|
||||
}
|
||||
|
||||
static void read_var(int len)
|
||||
{
|
||||
char cval; short sval; int ival; long long lval;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
switch (len) {
|
||||
case 1:
|
||||
cval = *(char *)&dummy_var[i];
|
||||
break;
|
||||
case 2:
|
||||
sval = *(short *)&dummy_var[i];
|
||||
break;
|
||||
case 4:
|
||||
ival = *(int *)&dummy_var[i];
|
||||
break;
|
||||
case 8:
|
||||
lval = *(long long *)&dummy_var[i];
|
||||
break;
|
||||
}
|
||||
check_trapped();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Do the r/w/x accesses to trigger the breakpoints. And run
|
||||
* the usual traps.
|
||||
*/
|
||||
static void trigger_tests(void)
|
||||
{
|
||||
int len, local, global, i;
|
||||
char val;
|
||||
int ret;
|
||||
|
||||
ret = ptrace(PTRACE_TRACEME, 0, NULL, 0);
|
||||
if (ret) {
|
||||
perror("Can't be traced?\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Wake up father so that it sets up the first test */
|
||||
kill(getpid(), SIGUSR1);
|
||||
|
||||
/* Test instruction breakpoints */
|
||||
for (local = 0; local < 2; local++) {
|
||||
for (global = 0; global < 2; global++) {
|
||||
if (!local && !global)
|
||||
continue;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
dummy_funcs[i]();
|
||||
check_trapped();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Test write watchpoints */
|
||||
for (len = 1; len <= sizeof(long); len <<= 1) {
|
||||
for (local = 0; local < 2; local++) {
|
||||
for (global = 0; global < 2; global++) {
|
||||
if (!local && !global)
|
||||
continue;
|
||||
write_var(len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Test read/write watchpoints (on read accesses) */
|
||||
for (len = 1; len <= sizeof(long); len <<= 1) {
|
||||
for (local = 0; local < 2; local++) {
|
||||
for (global = 0; global < 2; global++) {
|
||||
if (!local && !global)
|
||||
continue;
|
||||
read_var(len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Icebp trap */
|
||||
asm(".byte 0xf1\n");
|
||||
check_trapped();
|
||||
|
||||
/* Int 3 trap */
|
||||
asm("int $3\n");
|
||||
check_trapped();
|
||||
|
||||
kill(getpid(), SIGUSR1);
|
||||
}
|
||||
|
||||
static void check_success(const char *msg)
|
||||
{
|
||||
const char *msg2;
|
||||
int child_nr_tests;
|
||||
int status;
|
||||
|
||||
/* Wait for the child to SIGTRAP */
|
||||
wait(&status);
|
||||
|
||||
msg2 = "Failed";
|
||||
|
||||
if (WSTOPSIG(status) == SIGTRAP) {
|
||||
child_nr_tests = ptrace(PTRACE_PEEKDATA, child_pid,
|
||||
&nr_tests, 0);
|
||||
if (child_nr_tests == nr_tests)
|
||||
msg2 = "Ok";
|
||||
if (ptrace(PTRACE_POKEDATA, child_pid, &trapped, 1)) {
|
||||
perror("Can't poke\n");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
nr_tests++;
|
||||
|
||||
printf("%s [%s]\n", msg, msg2);
|
||||
}
|
||||
|
||||
static void launch_instruction_breakpoints(char *buf, int local, int global)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
set_breakpoint_addr(dummy_funcs[i], i);
|
||||
toggle_breakpoint(i, BP_X, 1, local, global, 1);
|
||||
ptrace(PTRACE_CONT, child_pid, NULL, 0);
|
||||
sprintf(buf, "Test breakpoint %d with local: %d global: %d",
|
||||
i, local, global);
|
||||
check_success(buf);
|
||||
toggle_breakpoint(i, BP_X, 1, local, global, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void launch_watchpoints(char *buf, int mode, int len,
|
||||
int local, int global)
|
||||
{
|
||||
const char *mode_str;
|
||||
int i;
|
||||
|
||||
if (mode == BP_W)
|
||||
mode_str = "write";
|
||||
else
|
||||
mode_str = "read";
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
set_breakpoint_addr(&dummy_var[i], i);
|
||||
toggle_breakpoint(i, mode, len, local, global, 1);
|
||||
ptrace(PTRACE_CONT, child_pid, NULL, 0);
|
||||
sprintf(buf, "Test %s watchpoint %d with len: %d local: "
|
||||
"%d global: %d", mode_str, i, len, local, global);
|
||||
check_success(buf);
|
||||
toggle_breakpoint(i, mode, len, local, global, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the breakpoints and check the child successfully trigger them */
|
||||
static void launch_tests(void)
|
||||
{
|
||||
char buf[1024];
|
||||
int len, local, global, i;
|
||||
|
||||
/* Instruction breakpoints */
|
||||
for (local = 0; local < 2; local++) {
|
||||
for (global = 0; global < 2; global++) {
|
||||
if (!local && !global)
|
||||
continue;
|
||||
launch_instruction_breakpoints(buf, local, global);
|
||||
}
|
||||
}
|
||||
|
||||
/* Write watchpoint */
|
||||
for (len = 1; len <= sizeof(long); len <<= 1) {
|
||||
for (local = 0; local < 2; local++) {
|
||||
for (global = 0; global < 2; global++) {
|
||||
if (!local && !global)
|
||||
continue;
|
||||
launch_watchpoints(buf, BP_W, len,
|
||||
local, global);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Read-Write watchpoint */
|
||||
for (len = 1; len <= sizeof(long); len <<= 1) {
|
||||
for (local = 0; local < 2; local++) {
|
||||
for (global = 0; global < 2; global++) {
|
||||
if (!local && !global)
|
||||
continue;
|
||||
launch_watchpoints(buf, BP_RW, len,
|
||||
local, global);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Icebp traps */
|
||||
ptrace(PTRACE_CONT, child_pid, NULL, 0);
|
||||
check_success("Test icebp");
|
||||
|
||||
/* Int 3 traps */
|
||||
ptrace(PTRACE_CONT, child_pid, NULL, 0);
|
||||
check_success("Test int 3 trap");
|
||||
|
||||
ptrace(PTRACE_CONT, child_pid, NULL, 0);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
pid_t pid;
|
||||
int ret;
|
||||
|
||||
pid = fork();
|
||||
if (!pid) {
|
||||
trigger_tests();
|
||||
return 0;
|
||||
}
|
||||
|
||||
child_pid = pid;
|
||||
|
||||
wait(NULL);
|
||||
|
||||
launch_tests();
|
||||
|
||||
wait(NULL);
|
||||
|
||||
return 0;
|
||||
}
|
8
tools/testing/selftests/run_tests
Normal file
8
tools/testing/selftests/run_tests
Normal file
@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
TARGETS=breakpoints
|
||||
|
||||
for TARGET in $TARGETS
|
||||
do
|
||||
$TARGET/run_test
|
||||
done
|
Loading…
Reference in New Issue
Block a user