2005-04-17 02:20:36 +04:00
|
|
|
/*
|
|
|
|
* kernel/cpuset.c
|
|
|
|
*
|
|
|
|
* Processor and Memory placement constraints for sets of tasks.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2003 BULL SA.
|
2007-10-19 10:40:20 +04:00
|
|
|
* Copyright (C) 2004-2007 Silicon Graphics, Inc.
|
2007-10-19 10:39:39 +04:00
|
|
|
* Copyright (C) 2006 Google, Inc
|
2005-04-17 02:20:36 +04:00
|
|
|
*
|
|
|
|
* Portions derived from Patrick Mochel's sysfs code.
|
|
|
|
* sysfs is Copyright (c) 2001-3 Patrick Mochel
|
|
|
|
*
|
[PATCH] cpuset memory spread basic implementation
This patch provides the implementation and cpuset interface for an alternative
memory allocation policy that can be applied to certain kinds of memory
allocations, such as the page cache (file system buffers) and some slab caches
(such as inode caches).
The policy is called "memory spreading." If enabled, it spreads out these
kinds of memory allocations over all the nodes allowed to a task, instead of
preferring to place them on the node where the task is executing.
All other kinds of allocations, including anonymous pages for a tasks stack
and data regions, are not affected by this policy choice, and continue to be
allocated preferring the node local to execution, as modified by the NUMA
mempolicy.
There are two boolean flag files per cpuset that control where the kernel
allocates pages for the file system buffers and related in kernel data
structures. They are called 'memory_spread_page' and 'memory_spread_slab'.
If the per-cpuset boolean flag file 'memory_spread_page' is set, then the
kernel will spread the file system buffers (page cache) evenly over all the
nodes that the faulting task is allowed to use, instead of preferring to put
those pages on the node where the task is running.
If the per-cpuset boolean flag file 'memory_spread_slab' is set, then the
kernel will spread some file system related slab caches, such as for inodes
and dentries evenly over all the nodes that the faulting task is allowed to
use, instead of preferring to put those pages on the node where the task is
running.
The implementation is simple. Setting the cpuset flags 'memory_spread_page'
or 'memory_spread_cache' turns on the per-process flags PF_SPREAD_PAGE or
PF_SPREAD_SLAB, respectively, for each task that is in the cpuset or
subsequently joins that cpuset. In subsequent patches, the page allocation
calls for the affected page cache and slab caches are modified to perform an
inline check for these flags, and if set, a call to a new routine
cpuset_mem_spread_node() returns the node to prefer for the allocation.
The cpuset_mem_spread_node() routine is also simple. It uses the value of a
per-task rotor cpuset_mem_spread_rotor to select the next node in the current
tasks mems_allowed to prefer for the allocation.
This policy can provide substantial improvements for jobs that need to place
thread local data on the corresponding node, but that need to access large
file system data sets that need to be spread across the several nodes in the
jobs cpuset in order to fit. Without this patch, especially for jobs that
might have one thread reading in the data set, the memory allocation across
the nodes in the jobs cpuset can become very uneven.
A couple of Copyright year ranges are updated as well. And a couple of email
addresses that can be found in the MAINTAINERS file are removed.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-03-24 14:16:03 +03:00
|
|
|
* 2003-10-10 Written by Simon Derr.
|
2005-04-17 02:20:36 +04:00
|
|
|
* 2003-10-22 Updates by Stephen Hemminger.
|
[PATCH] cpuset memory spread basic implementation
This patch provides the implementation and cpuset interface for an alternative
memory allocation policy that can be applied to certain kinds of memory
allocations, such as the page cache (file system buffers) and some slab caches
(such as inode caches).
The policy is called "memory spreading." If enabled, it spreads out these
kinds of memory allocations over all the nodes allowed to a task, instead of
preferring to place them on the node where the task is executing.
All other kinds of allocations, including anonymous pages for a tasks stack
and data regions, are not affected by this policy choice, and continue to be
allocated preferring the node local to execution, as modified by the NUMA
mempolicy.
There are two boolean flag files per cpuset that control where the kernel
allocates pages for the file system buffers and related in kernel data
structures. They are called 'memory_spread_page' and 'memory_spread_slab'.
If the per-cpuset boolean flag file 'memory_spread_page' is set, then the
kernel will spread the file system buffers (page cache) evenly over all the
nodes that the faulting task is allowed to use, instead of preferring to put
those pages on the node where the task is running.
If the per-cpuset boolean flag file 'memory_spread_slab' is set, then the
kernel will spread some file system related slab caches, such as for inodes
and dentries evenly over all the nodes that the faulting task is allowed to
use, instead of preferring to put those pages on the node where the task is
running.
The implementation is simple. Setting the cpuset flags 'memory_spread_page'
or 'memory_spread_cache' turns on the per-process flags PF_SPREAD_PAGE or
PF_SPREAD_SLAB, respectively, for each task that is in the cpuset or
subsequently joins that cpuset. In subsequent patches, the page allocation
calls for the affected page cache and slab caches are modified to perform an
inline check for these flags, and if set, a call to a new routine
cpuset_mem_spread_node() returns the node to prefer for the allocation.
The cpuset_mem_spread_node() routine is also simple. It uses the value of a
per-task rotor cpuset_mem_spread_rotor to select the next node in the current
tasks mems_allowed to prefer for the allocation.
This policy can provide substantial improvements for jobs that need to place
thread local data on the corresponding node, but that need to access large
file system data sets that need to be spread across the several nodes in the
jobs cpuset in order to fit. Without this patch, especially for jobs that
might have one thread reading in the data set, the memory allocation across
the nodes in the jobs cpuset can become very uneven.
A couple of Copyright year ranges are updated as well. And a couple of email
addresses that can be found in the MAINTAINERS file are removed.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-03-24 14:16:03 +03:00
|
|
|
* 2004 May-July Rework by Paul Jackson.
|
2007-10-19 10:39:39 +04:00
|
|
|
* 2006 Rework by Paul Menage to use generic cgroups
|
2005-04-17 02:20:36 +04:00
|
|
|
*
|
|
|
|
* This file is subject to the terms and conditions of the GNU General Public
|
|
|
|
* License. See the file COPYING in the main directory of the Linux
|
|
|
|
* distribution for more details.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/cpu.h>
|
|
|
|
#include <linux/cpumask.h>
|
|
|
|
#include <linux/cpuset.h>
|
|
|
|
#include <linux/err.h>
|
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/file.h>
|
|
|
|
#include <linux/fs.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/interrupt.h>
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/kmod.h>
|
|
|
|
#include <linux/list.h>
|
[PATCH] cpusets: automatic numa mempolicy rebinding
This patch automatically updates a tasks NUMA mempolicy when its cpuset
memory placement changes. It does so within the context of the task,
without any need to support low level external mempolicy manipulation.
If a system is not using cpusets, or if running on a system with just the
root (all-encompassing) cpuset, then this remap is a no-op. Only when a
task is moved between cpusets, or a cpusets memory placement is changed
does the following apply. Otherwise, the main routine below,
rebind_policy() is not even called.
When mixing cpusets, scheduler affinity, and NUMA mempolicies, the
essential role of cpusets is to place jobs (several related tasks) on a set
of CPUs and Memory Nodes, the essential role of sched_setaffinity is to
manage a jobs processor placement within its allowed cpuset, and the
essential role of NUMA mempolicy (mbind, set_mempolicy) is to manage a jobs
memory placement within its allowed cpuset.
However, CPU affinity and NUMA memory placement are managed within the
kernel using absolute system wide numbering, not cpuset relative numbering.
This is ok until a job is migrated to a different cpuset, or what's the
same, a jobs cpuset is moved to different CPUs and Memory Nodes.
Then the CPU affinity and NUMA memory placement of the tasks in the job
need to be updated, to preserve their cpuset-relative position. This can
be done for CPU affinity using sched_setaffinity() from user code, as one
task can modify anothers CPU affinity. This cannot be done from an
external task for NUMA memory placement, as that can only be modified in
the context of the task using it.
However, it easy enough to remap a tasks NUMA mempolicy automatically when
a task is migrated, using the existing cpuset mechanism to trigger a
refresh of a tasks memory placement after its cpuset has changed. All that
is needed is the old and new nodemask, and notice to the task that it needs
to rebind its mempolicy. The tasks mems_allowed has the old mask, the
tasks cpuset has the new mask, and the existing
cpuset_update_current_mems_allowed() mechanism provides the notice. The
bitmap/cpumask/nodemask remap operators provide the cpuset relative
calculations.
This patch leaves open a couple of issues:
1) Updating vma and shmfs/tmpfs/hugetlbfs memory policies:
These mempolicies may reference nodes outside of those allowed to
the current task by its cpuset. Tasks are migrated as part of jobs,
which reside on what might be several cpusets in a subtree. When such
a job is migrated, all NUMA memory policy references to nodes within
that cpuset subtree should be translated, and references to any nodes
outside that subtree should be left untouched. A future patch will
provide the cpuset mechanism needed to mark such subtrees. With that
patch, we will be able to correctly migrate these other memory policies
across a job migration.
2) Updating cpuset, affinity and memory policies in user space:
This is harder. Any placement state stored in user space using
system-wide numbering will be invalidated across a migration. More
work will be required to provide user code with a migration-safe means
to manage its cpuset relative placement, while preserving the current
API's that pass system wide numbers, not cpuset relative numbers across
the kernel-user boundary.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-10-31 02:02:36 +03:00
|
|
|
#include <linux/mempolicy.h>
|
2005-04-17 02:20:36 +04:00
|
|
|
#include <linux/mm.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/mount.h>
|
|
|
|
#include <linux/namei.h>
|
|
|
|
#include <linux/pagemap.h>
|
|
|
|
#include <linux/proc_fs.h>
|
[PATCH] cpuset: use rcu directly optimization
Optimize the cpuset impact on page allocation, the most performance critical
cpuset hook in the kernel.
On each page allocation, the cpuset hook needs to check for a possible change
in the current tasks cpuset. It can now handle the common case, of no change,
without taking any spinlock or semaphore, thanks to RCU.
Convert a spinlock on the current task to an rcu_read_lock(), saving
approximately a memory barrier and an atomic op, depending on architecture.
This is done by adding rcu_assign_pointer() and synchronize_rcu() calls to the
write side of the task->cpuset pointer, in cpuset.c:attach_task(), to delay
freeing up a detached cpuset until after any critical sections referencing
that pointer.
Thanks to Andi Kleen, Nick Piggin and Eric Dumazet for ideas.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 12:02:02 +03:00
|
|
|
#include <linux/rcupdate.h>
|
2005-04-17 02:20:36 +04:00
|
|
|
#include <linux/sched.h>
|
|
|
|
#include <linux/seq_file.h>
|
2006-06-23 13:04:00 +04:00
|
|
|
#include <linux/security.h>
|
2005-04-17 02:20:36 +04:00
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/spinlock.h>
|
|
|
|
#include <linux/stat.h>
|
|
|
|
#include <linux/string.h>
|
|
|
|
#include <linux/time.h>
|
|
|
|
#include <linux/backing-dev.h>
|
|
|
|
#include <linux/sort.h>
|
|
|
|
|
|
|
|
#include <asm/uaccess.h>
|
|
|
|
#include <asm/atomic.h>
|
2006-03-23 14:00:18 +03:00
|
|
|
#include <linux/mutex.h>
|
2007-10-19 10:40:20 +04:00
|
|
|
#include <linux/kfifo.h>
|
2008-02-07 11:14:43 +03:00
|
|
|
#include <linux/workqueue.h>
|
|
|
|
#include <linux/cgroup.h>
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2006-01-08 12:01:57 +03:00
|
|
|
/*
|
|
|
|
* Tracks how many cpusets are currently defined in system.
|
|
|
|
* When there is only one cpuset (the root cpuset) we can
|
|
|
|
* short circuit some hooks.
|
|
|
|
*/
|
2006-01-08 12:02:03 +03:00
|
|
|
int number_of_cpusets __read_mostly;
|
2006-01-08 12:01:57 +03:00
|
|
|
|
2008-02-07 11:14:45 +03:00
|
|
|
/* Forward declare cgroup structures */
|
2007-10-19 10:39:39 +04:00
|
|
|
struct cgroup_subsys cpuset_subsys;
|
|
|
|
struct cpuset;
|
|
|
|
|
[PATCH] cpuset: memory pressure meter
Provide a simple per-cpuset metric of memory pressure, tracking the -rate-
that the tasks in a cpuset call try_to_free_pages(), the synchronous
(direct) memory reclaim code.
This enables batch managers monitoring jobs running in dedicated cpusets to
efficiently detect what level of memory pressure that job is causing.
This is useful both on tightly managed systems running a wide mix of
submitted jobs, which may choose to terminate or reprioritize jobs that are
trying to use more memory than allowed on the nodes assigned them, and with
tightly coupled, long running, massively parallel scientific computing jobs
that will dramatically fail to meet required performance goals if they
start to use more memory than allowed to them.
This patch just provides a very economical way for the batch manager to
monitor a cpuset for signs of memory pressure. It's up to the batch
manager or other user code to decide what to do about it and take action.
==> Unless this feature is enabled by writing "1" to the special file
/dev/cpuset/memory_pressure_enabled, the hook in the rebalance
code of __alloc_pages() for this metric reduces to simply noticing
that the cpuset_memory_pressure_enabled flag is zero. So only
systems that enable this feature will compute the metric.
Why a per-cpuset, running average:
Because this meter is per-cpuset, rather than per-task or mm, the
system load imposed by a batch scheduler monitoring this metric is
sharply reduced on large systems, because a scan of the tasklist can be
avoided on each set of queries.
Because this meter is a running average, instead of an accumulating
counter, a batch scheduler can detect memory pressure with a single
read, instead of having to read and accumulate results for a period of
time.
Because this meter is per-cpuset rather than per-task or mm, the
batch scheduler can obtain the key information, memory pressure in a
cpuset, with a single read, rather than having to query and accumulate
results over all the (dynamically changing) set of tasks in the cpuset.
A per-cpuset simple digital filter (requires a spinlock and 3 words of data
per-cpuset) is kept, and updated by any task attached to that cpuset, if it
enters the synchronous (direct) page reclaim code.
A per-cpuset file provides an integer number representing the recent
(half-life of 10 seconds) rate of direct page reclaims caused by the tasks
in the cpuset, in units of reclaims attempted per second, times 1000.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 12:01:49 +03:00
|
|
|
/* See "Frequency meter" comments, below. */
|
|
|
|
|
|
|
|
struct fmeter {
|
|
|
|
int cnt; /* unprocessed events count */
|
|
|
|
int val; /* most recent output value */
|
|
|
|
time_t time; /* clock (secs) when val computed */
|
|
|
|
spinlock_t lock; /* guards read or write of above */
|
|
|
|
};
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
struct cpuset {
|
2007-10-19 10:39:39 +04:00
|
|
|
struct cgroup_subsys_state css;
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
unsigned long flags; /* "unsigned long" so bitops work */
|
|
|
|
cpumask_t cpus_allowed; /* CPUs allowed to tasks in cpuset */
|
|
|
|
nodemask_t mems_allowed; /* Memory Nodes allowed to tasks */
|
|
|
|
|
|
|
|
struct cpuset *parent; /* my parent */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copy of global cpuset_mems_generation as of the most
|
|
|
|
* recent time this cpuset changed its mems_allowed.
|
|
|
|
*/
|
[PATCH] cpuset: memory pressure meter
Provide a simple per-cpuset metric of memory pressure, tracking the -rate-
that the tasks in a cpuset call try_to_free_pages(), the synchronous
(direct) memory reclaim code.
This enables batch managers monitoring jobs running in dedicated cpusets to
efficiently detect what level of memory pressure that job is causing.
This is useful both on tightly managed systems running a wide mix of
submitted jobs, which may choose to terminate or reprioritize jobs that are
trying to use more memory than allowed on the nodes assigned them, and with
tightly coupled, long running, massively parallel scientific computing jobs
that will dramatically fail to meet required performance goals if they
start to use more memory than allowed to them.
This patch just provides a very economical way for the batch manager to
monitor a cpuset for signs of memory pressure. It's up to the batch
manager or other user code to decide what to do about it and take action.
==> Unless this feature is enabled by writing "1" to the special file
/dev/cpuset/memory_pressure_enabled, the hook in the rebalance
code of __alloc_pages() for this metric reduces to simply noticing
that the cpuset_memory_pressure_enabled flag is zero. So only
systems that enable this feature will compute the metric.
Why a per-cpuset, running average:
Because this meter is per-cpuset, rather than per-task or mm, the
system load imposed by a batch scheduler monitoring this metric is
sharply reduced on large systems, because a scan of the tasklist can be
avoided on each set of queries.
Because this meter is a running average, instead of an accumulating
counter, a batch scheduler can detect memory pressure with a single
read, instead of having to read and accumulate results for a period of
time.
Because this meter is per-cpuset rather than per-task or mm, the
batch scheduler can obtain the key information, memory pressure in a
cpuset, with a single read, rather than having to query and accumulate
results over all the (dynamically changing) set of tasks in the cpuset.
A per-cpuset simple digital filter (requires a spinlock and 3 words of data
per-cpuset) is kept, and updated by any task attached to that cpuset, if it
enters the synchronous (direct) page reclaim code.
A per-cpuset file provides an integer number representing the recent
(half-life of 10 seconds) rate of direct page reclaims caused by the tasks
in the cpuset, in units of reclaims attempted per second, times 1000.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 12:01:49 +03:00
|
|
|
int mems_generation;
|
|
|
|
|
|
|
|
struct fmeter fmeter; /* memory_pressure filter */
|
2007-10-19 10:40:20 +04:00
|
|
|
|
|
|
|
/* partition number for rebuild_sched_domains() */
|
|
|
|
int pn;
|
2008-02-07 11:14:43 +03:00
|
|
|
|
2008-04-15 09:04:23 +04:00
|
|
|
/* for custom sched domain */
|
|
|
|
int relax_domain_level;
|
|
|
|
|
2008-02-07 11:14:43 +03:00
|
|
|
/* used for walking a cpuset heirarchy */
|
|
|
|
struct list_head stack_list;
|
2005-04-17 02:20:36 +04:00
|
|
|
};
|
|
|
|
|
2007-10-19 10:39:39 +04:00
|
|
|
/* Retrieve the cpuset for a cgroup */
|
|
|
|
static inline struct cpuset *cgroup_cs(struct cgroup *cont)
|
|
|
|
{
|
|
|
|
return container_of(cgroup_subsys_state(cont, cpuset_subsys_id),
|
|
|
|
struct cpuset, css);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Retrieve the cpuset for a task */
|
|
|
|
static inline struct cpuset *task_cs(struct task_struct *task)
|
|
|
|
{
|
|
|
|
return container_of(task_subsys_state(task, cpuset_subsys_id),
|
|
|
|
struct cpuset, css);
|
|
|
|
}
|
2008-02-07 11:14:43 +03:00
|
|
|
struct cpuset_hotplug_scanner {
|
|
|
|
struct cgroup_scanner scan;
|
|
|
|
struct cgroup *to;
|
|
|
|
};
|
2007-10-19 10:39:39 +04:00
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
/* bits in struct cpuset flags field */
|
|
|
|
typedef enum {
|
|
|
|
CS_CPU_EXCLUSIVE,
|
|
|
|
CS_MEM_EXCLUSIVE,
|
2008-04-29 12:00:26 +04:00
|
|
|
CS_MEM_HARDWALL,
|
[PATCH] cpusets: swap migration interface
Add a boolean "memory_migrate" to each cpuset, represented by a file
containing "0" or "1" in each directory below /dev/cpuset.
It defaults to false (file contains "0"). It can be set true by writing
"1" to the file.
If true, then anytime that a task is attached to the cpuset so marked, the
pages of that task will be moved to that cpuset, preserving, to the extent
practical, the cpuset-relative placement of the pages.
Also anytime that a cpuset so marked has its memory placement changed (by
writing to its "mems" file), the tasks in that cpuset will have their pages
moved to the cpusets new nodes, preserving, to the extent practical, the
cpuset-relative placement of the moved pages.
Signed-off-by: Paul Jackson <pj@sgi.com>
Cc: Christoph Lameter <christoph@lameter.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 12:00:56 +03:00
|
|
|
CS_MEMORY_MIGRATE,
|
2007-10-19 10:40:20 +04:00
|
|
|
CS_SCHED_LOAD_BALANCE,
|
[PATCH] cpuset memory spread basic implementation
This patch provides the implementation and cpuset interface for an alternative
memory allocation policy that can be applied to certain kinds of memory
allocations, such as the page cache (file system buffers) and some slab caches
(such as inode caches).
The policy is called "memory spreading." If enabled, it spreads out these
kinds of memory allocations over all the nodes allowed to a task, instead of
preferring to place them on the node where the task is executing.
All other kinds of allocations, including anonymous pages for a tasks stack
and data regions, are not affected by this policy choice, and continue to be
allocated preferring the node local to execution, as modified by the NUMA
mempolicy.
There are two boolean flag files per cpuset that control where the kernel
allocates pages for the file system buffers and related in kernel data
structures. They are called 'memory_spread_page' and 'memory_spread_slab'.
If the per-cpuset boolean flag file 'memory_spread_page' is set, then the
kernel will spread the file system buffers (page cache) evenly over all the
nodes that the faulting task is allowed to use, instead of preferring to put
those pages on the node where the task is running.
If the per-cpuset boolean flag file 'memory_spread_slab' is set, then the
kernel will spread some file system related slab caches, such as for inodes
and dentries evenly over all the nodes that the faulting task is allowed to
use, instead of preferring to put those pages on the node where the task is
running.
The implementation is simple. Setting the cpuset flags 'memory_spread_page'
or 'memory_spread_cache' turns on the per-process flags PF_SPREAD_PAGE or
PF_SPREAD_SLAB, respectively, for each task that is in the cpuset or
subsequently joins that cpuset. In subsequent patches, the page allocation
calls for the affected page cache and slab caches are modified to perform an
inline check for these flags, and if set, a call to a new routine
cpuset_mem_spread_node() returns the node to prefer for the allocation.
The cpuset_mem_spread_node() routine is also simple. It uses the value of a
per-task rotor cpuset_mem_spread_rotor to select the next node in the current
tasks mems_allowed to prefer for the allocation.
This policy can provide substantial improvements for jobs that need to place
thread local data on the corresponding node, but that need to access large
file system data sets that need to be spread across the several nodes in the
jobs cpuset in order to fit. Without this patch, especially for jobs that
might have one thread reading in the data set, the memory allocation across
the nodes in the jobs cpuset can become very uneven.
A couple of Copyright year ranges are updated as well. And a couple of email
addresses that can be found in the MAINTAINERS file are removed.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-03-24 14:16:03 +03:00
|
|
|
CS_SPREAD_PAGE,
|
|
|
|
CS_SPREAD_SLAB,
|
2005-04-17 02:20:36 +04:00
|
|
|
} cpuset_flagbits_t;
|
|
|
|
|
|
|
|
/* convenient tests for these bits */
|
|
|
|
static inline int is_cpu_exclusive(const struct cpuset *cs)
|
|
|
|
{
|
2006-03-24 14:16:00 +03:00
|
|
|
return test_bit(CS_CPU_EXCLUSIVE, &cs->flags);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline int is_mem_exclusive(const struct cpuset *cs)
|
|
|
|
{
|
2006-03-24 14:16:00 +03:00
|
|
|
return test_bit(CS_MEM_EXCLUSIVE, &cs->flags);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
2008-04-29 12:00:26 +04:00
|
|
|
static inline int is_mem_hardwall(const struct cpuset *cs)
|
|
|
|
{
|
|
|
|
return test_bit(CS_MEM_HARDWALL, &cs->flags);
|
|
|
|
}
|
|
|
|
|
2007-10-19 10:40:20 +04:00
|
|
|
static inline int is_sched_load_balance(const struct cpuset *cs)
|
|
|
|
{
|
|
|
|
return test_bit(CS_SCHED_LOAD_BALANCE, &cs->flags);
|
|
|
|
}
|
|
|
|
|
[PATCH] cpusets: swap migration interface
Add a boolean "memory_migrate" to each cpuset, represented by a file
containing "0" or "1" in each directory below /dev/cpuset.
It defaults to false (file contains "0"). It can be set true by writing
"1" to the file.
If true, then anytime that a task is attached to the cpuset so marked, the
pages of that task will be moved to that cpuset, preserving, to the extent
practical, the cpuset-relative placement of the pages.
Also anytime that a cpuset so marked has its memory placement changed (by
writing to its "mems" file), the tasks in that cpuset will have their pages
moved to the cpusets new nodes, preserving, to the extent practical, the
cpuset-relative placement of the moved pages.
Signed-off-by: Paul Jackson <pj@sgi.com>
Cc: Christoph Lameter <christoph@lameter.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 12:00:56 +03:00
|
|
|
static inline int is_memory_migrate(const struct cpuset *cs)
|
|
|
|
{
|
2006-03-24 14:16:00 +03:00
|
|
|
return test_bit(CS_MEMORY_MIGRATE, &cs->flags);
|
[PATCH] cpusets: swap migration interface
Add a boolean "memory_migrate" to each cpuset, represented by a file
containing "0" or "1" in each directory below /dev/cpuset.
It defaults to false (file contains "0"). It can be set true by writing
"1" to the file.
If true, then anytime that a task is attached to the cpuset so marked, the
pages of that task will be moved to that cpuset, preserving, to the extent
practical, the cpuset-relative placement of the pages.
Also anytime that a cpuset so marked has its memory placement changed (by
writing to its "mems" file), the tasks in that cpuset will have their pages
moved to the cpusets new nodes, preserving, to the extent practical, the
cpuset-relative placement of the moved pages.
Signed-off-by: Paul Jackson <pj@sgi.com>
Cc: Christoph Lameter <christoph@lameter.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 12:00:56 +03:00
|
|
|
}
|
|
|
|
|
[PATCH] cpuset memory spread basic implementation
This patch provides the implementation and cpuset interface for an alternative
memory allocation policy that can be applied to certain kinds of memory
allocations, such as the page cache (file system buffers) and some slab caches
(such as inode caches).
The policy is called "memory spreading." If enabled, it spreads out these
kinds of memory allocations over all the nodes allowed to a task, instead of
preferring to place them on the node where the task is executing.
All other kinds of allocations, including anonymous pages for a tasks stack
and data regions, are not affected by this policy choice, and continue to be
allocated preferring the node local to execution, as modified by the NUMA
mempolicy.
There are two boolean flag files per cpuset that control where the kernel
allocates pages for the file system buffers and related in kernel data
structures. They are called 'memory_spread_page' and 'memory_spread_slab'.
If the per-cpuset boolean flag file 'memory_spread_page' is set, then the
kernel will spread the file system buffers (page cache) evenly over all the
nodes that the faulting task is allowed to use, instead of preferring to put
those pages on the node where the task is running.
If the per-cpuset boolean flag file 'memory_spread_slab' is set, then the
kernel will spread some file system related slab caches, such as for inodes
and dentries evenly over all the nodes that the faulting task is allowed to
use, instead of preferring to put those pages on the node where the task is
running.
The implementation is simple. Setting the cpuset flags 'memory_spread_page'
or 'memory_spread_cache' turns on the per-process flags PF_SPREAD_PAGE or
PF_SPREAD_SLAB, respectively, for each task that is in the cpuset or
subsequently joins that cpuset. In subsequent patches, the page allocation
calls for the affected page cache and slab caches are modified to perform an
inline check for these flags, and if set, a call to a new routine
cpuset_mem_spread_node() returns the node to prefer for the allocation.
The cpuset_mem_spread_node() routine is also simple. It uses the value of a
per-task rotor cpuset_mem_spread_rotor to select the next node in the current
tasks mems_allowed to prefer for the allocation.
This policy can provide substantial improvements for jobs that need to place
thread local data on the corresponding node, but that need to access large
file system data sets that need to be spread across the several nodes in the
jobs cpuset in order to fit. Without this patch, especially for jobs that
might have one thread reading in the data set, the memory allocation across
the nodes in the jobs cpuset can become very uneven.
A couple of Copyright year ranges are updated as well. And a couple of email
addresses that can be found in the MAINTAINERS file are removed.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-03-24 14:16:03 +03:00
|
|
|
static inline int is_spread_page(const struct cpuset *cs)
|
|
|
|
{
|
|
|
|
return test_bit(CS_SPREAD_PAGE, &cs->flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int is_spread_slab(const struct cpuset *cs)
|
|
|
|
{
|
|
|
|
return test_bit(CS_SPREAD_SLAB, &cs->flags);
|
|
|
|
}
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
/*
|
2006-03-24 14:16:11 +03:00
|
|
|
* Increment this integer everytime any cpuset changes its
|
2005-04-17 02:20:36 +04:00
|
|
|
* mems_allowed value. Users of cpusets can track this generation
|
|
|
|
* number, and avoid having to lock and reload mems_allowed unless
|
|
|
|
* the cpuset they're using changes generation.
|
|
|
|
*
|
2008-02-07 11:14:45 +03:00
|
|
|
* A single, global generation is needed because cpuset_attach_task() could
|
2005-04-17 02:20:36 +04:00
|
|
|
* reattach a task to a different cpuset, which must not have its
|
|
|
|
* generation numbers aliased with those of that tasks previous cpuset.
|
|
|
|
*
|
|
|
|
* Generations are needed for mems_allowed because one task cannot
|
2008-02-07 11:14:45 +03:00
|
|
|
* modify another's memory placement. So we must enable every task,
|
2005-04-17 02:20:36 +04:00
|
|
|
* on every visit to __alloc_pages(), to efficiently check whether
|
|
|
|
* its current->cpuset->mems_allowed has changed, requiring an update
|
|
|
|
* of its current->mems_allowed.
|
2006-03-24 14:16:11 +03:00
|
|
|
*
|
2008-02-07 11:14:45 +03:00
|
|
|
* Since writes to cpuset_mems_generation are guarded by the cgroup lock
|
2006-03-24 14:16:11 +03:00
|
|
|
* there is no need to mark it atomic.
|
2005-04-17 02:20:36 +04:00
|
|
|
*/
|
2006-03-24 14:16:11 +03:00
|
|
|
static int cpuset_mems_generation;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
static struct cpuset top_cpuset = {
|
|
|
|
.flags = ((1 << CS_CPU_EXCLUSIVE) | (1 << CS_MEM_EXCLUSIVE)),
|
|
|
|
.cpus_allowed = CPU_MASK_ALL,
|
|
|
|
.mems_allowed = NODE_MASK_ALL,
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
2008-02-07 11:14:45 +03:00
|
|
|
* There are two global mutexes guarding cpuset structures. The first
|
|
|
|
* is the main control groups cgroup_mutex, accessed via
|
|
|
|
* cgroup_lock()/cgroup_unlock(). The second is the cpuset-specific
|
|
|
|
* callback_mutex, below. They can nest. It is ok to first take
|
|
|
|
* cgroup_mutex, then nest callback_mutex. We also require taking
|
|
|
|
* task_lock() when dereferencing a task's cpuset pointer. See "The
|
|
|
|
* task_lock() exception", at the end of this comment.
|
[PATCH] cpusets: dual semaphore locking overhaul
Overhaul cpuset locking. Replace single semaphore with two semaphores.
The suggestion to use two locks was made by Roman Zippel.
Both locks are global. Code that wants to modify cpusets must first
acquire the exclusive manage_sem, which allows them read-only access to
cpusets, and holds off other would-be modifiers. Before making actual
changes, the second semaphore, callback_sem must be acquired as well. Code
that needs only to query cpusets must acquire callback_sem, which is also a
global exclusive lock.
The earlier problems with double tripping are avoided, because it is
allowed for holders of manage_sem to nest the second callback_sem lock, and
only callback_sem is needed by code called from within __alloc_pages(),
where the double tripping had been possible.
This is not quite the same as a normal read/write semaphore, because
obtaining read-only access with intent to change must hold off other such
attempts, while allowing read-only access w/o such intention. Changing
cpusets involves several related checks and changes, which must be done
while allowing read-only queries (to avoid the double trip), but while
ensuring nothing changes (holding off other would be modifiers.)
This overhaul of cpuset locking also makes careful use of task_lock() to
guard access to the task->cpuset pointer, closing a couple of race
conditions noticed while reading this code (thanks, Roman). I've never
seen these races fail in any use or test.
See further the comments in the code.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-10-31 02:02:30 +03:00
|
|
|
*
|
2006-03-23 14:00:18 +03:00
|
|
|
* A task must hold both mutexes to modify cpusets. If a task
|
2008-02-07 11:14:45 +03:00
|
|
|
* holds cgroup_mutex, then it blocks others wanting that mutex,
|
2006-03-23 14:00:18 +03:00
|
|
|
* ensuring that it is the only task able to also acquire callback_mutex
|
[PATCH] cpusets: dual semaphore locking overhaul
Overhaul cpuset locking. Replace single semaphore with two semaphores.
The suggestion to use two locks was made by Roman Zippel.
Both locks are global. Code that wants to modify cpusets must first
acquire the exclusive manage_sem, which allows them read-only access to
cpusets, and holds off other would-be modifiers. Before making actual
changes, the second semaphore, callback_sem must be acquired as well. Code
that needs only to query cpusets must acquire callback_sem, which is also a
global exclusive lock.
The earlier problems with double tripping are avoided, because it is
allowed for holders of manage_sem to nest the second callback_sem lock, and
only callback_sem is needed by code called from within __alloc_pages(),
where the double tripping had been possible.
This is not quite the same as a normal read/write semaphore, because
obtaining read-only access with intent to change must hold off other such
attempts, while allowing read-only access w/o such intention. Changing
cpusets involves several related checks and changes, which must be done
while allowing read-only queries (to avoid the double trip), but while
ensuring nothing changes (holding off other would be modifiers.)
This overhaul of cpuset locking also makes careful use of task_lock() to
guard access to the task->cpuset pointer, closing a couple of race
conditions noticed while reading this code (thanks, Roman). I've never
seen these races fail in any use or test.
See further the comments in the code.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-10-31 02:02:30 +03:00
|
|
|
* and be able to modify cpusets. It can perform various checks on
|
|
|
|
* the cpuset structure first, knowing nothing will change. It can
|
2008-02-07 11:14:45 +03:00
|
|
|
* also allocate memory while just holding cgroup_mutex. While it is
|
[PATCH] cpusets: dual semaphore locking overhaul
Overhaul cpuset locking. Replace single semaphore with two semaphores.
The suggestion to use two locks was made by Roman Zippel.
Both locks are global. Code that wants to modify cpusets must first
acquire the exclusive manage_sem, which allows them read-only access to
cpusets, and holds off other would-be modifiers. Before making actual
changes, the second semaphore, callback_sem must be acquired as well. Code
that needs only to query cpusets must acquire callback_sem, which is also a
global exclusive lock.
The earlier problems with double tripping are avoided, because it is
allowed for holders of manage_sem to nest the second callback_sem lock, and
only callback_sem is needed by code called from within __alloc_pages(),
where the double tripping had been possible.
This is not quite the same as a normal read/write semaphore, because
obtaining read-only access with intent to change must hold off other such
attempts, while allowing read-only access w/o such intention. Changing
cpusets involves several related checks and changes, which must be done
while allowing read-only queries (to avoid the double trip), but while
ensuring nothing changes (holding off other would be modifiers.)
This overhaul of cpuset locking also makes careful use of task_lock() to
guard access to the task->cpuset pointer, closing a couple of race
conditions noticed while reading this code (thanks, Roman). I've never
seen these races fail in any use or test.
See further the comments in the code.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-10-31 02:02:30 +03:00
|
|
|
* performing these checks, various callback routines can briefly
|
2006-03-23 14:00:18 +03:00
|
|
|
* acquire callback_mutex to query cpusets. Once it is ready to make
|
|
|
|
* the changes, it takes callback_mutex, blocking everyone else.
|
[PATCH] cpusets: dual semaphore locking overhaul
Overhaul cpuset locking. Replace single semaphore with two semaphores.
The suggestion to use two locks was made by Roman Zippel.
Both locks are global. Code that wants to modify cpusets must first
acquire the exclusive manage_sem, which allows them read-only access to
cpusets, and holds off other would-be modifiers. Before making actual
changes, the second semaphore, callback_sem must be acquired as well. Code
that needs only to query cpusets must acquire callback_sem, which is also a
global exclusive lock.
The earlier problems with double tripping are avoided, because it is
allowed for holders of manage_sem to nest the second callback_sem lock, and
only callback_sem is needed by code called from within __alloc_pages(),
where the double tripping had been possible.
This is not quite the same as a normal read/write semaphore, because
obtaining read-only access with intent to change must hold off other such
attempts, while allowing read-only access w/o such intention. Changing
cpusets involves several related checks and changes, which must be done
while allowing read-only queries (to avoid the double trip), but while
ensuring nothing changes (holding off other would be modifiers.)
This overhaul of cpuset locking also makes careful use of task_lock() to
guard access to the task->cpuset pointer, closing a couple of race
conditions noticed while reading this code (thanks, Roman). I've never
seen these races fail in any use or test.
See further the comments in the code.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-10-31 02:02:30 +03:00
|
|
|
*
|
|
|
|
* Calls to the kernel memory allocator can not be made while holding
|
2006-03-23 14:00:18 +03:00
|
|
|
* callback_mutex, as that would risk double tripping on callback_mutex
|
[PATCH] cpusets: dual semaphore locking overhaul
Overhaul cpuset locking. Replace single semaphore with two semaphores.
The suggestion to use two locks was made by Roman Zippel.
Both locks are global. Code that wants to modify cpusets must first
acquire the exclusive manage_sem, which allows them read-only access to
cpusets, and holds off other would-be modifiers. Before making actual
changes, the second semaphore, callback_sem must be acquired as well. Code
that needs only to query cpusets must acquire callback_sem, which is also a
global exclusive lock.
The earlier problems with double tripping are avoided, because it is
allowed for holders of manage_sem to nest the second callback_sem lock, and
only callback_sem is needed by code called from within __alloc_pages(),
where the double tripping had been possible.
This is not quite the same as a normal read/write semaphore, because
obtaining read-only access with intent to change must hold off other such
attempts, while allowing read-only access w/o such intention. Changing
cpusets involves several related checks and changes, which must be done
while allowing read-only queries (to avoid the double trip), but while
ensuring nothing changes (holding off other would be modifiers.)
This overhaul of cpuset locking also makes careful use of task_lock() to
guard access to the task->cpuset pointer, closing a couple of race
conditions noticed while reading this code (thanks, Roman). I've never
seen these races fail in any use or test.
See further the comments in the code.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-10-31 02:02:30 +03:00
|
|
|
* from one of the callbacks into the cpuset code from within
|
|
|
|
* __alloc_pages().
|
|
|
|
*
|
2006-03-23 14:00:18 +03:00
|
|
|
* If a task is only holding callback_mutex, then it has read-only
|
[PATCH] cpusets: dual semaphore locking overhaul
Overhaul cpuset locking. Replace single semaphore with two semaphores.
The suggestion to use two locks was made by Roman Zippel.
Both locks are global. Code that wants to modify cpusets must first
acquire the exclusive manage_sem, which allows them read-only access to
cpusets, and holds off other would-be modifiers. Before making actual
changes, the second semaphore, callback_sem must be acquired as well. Code
that needs only to query cpusets must acquire callback_sem, which is also a
global exclusive lock.
The earlier problems with double tripping are avoided, because it is
allowed for holders of manage_sem to nest the second callback_sem lock, and
only callback_sem is needed by code called from within __alloc_pages(),
where the double tripping had been possible.
This is not quite the same as a normal read/write semaphore, because
obtaining read-only access with intent to change must hold off other such
attempts, while allowing read-only access w/o such intention. Changing
cpusets involves several related checks and changes, which must be done
while allowing read-only queries (to avoid the double trip), but while
ensuring nothing changes (holding off other would be modifiers.)
This overhaul of cpuset locking also makes careful use of task_lock() to
guard access to the task->cpuset pointer, closing a couple of race
conditions noticed while reading this code (thanks, Roman). I've never
seen these races fail in any use or test.
See further the comments in the code.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-10-31 02:02:30 +03:00
|
|
|
* access to cpusets.
|
|
|
|
*
|
|
|
|
* The task_struct fields mems_allowed and mems_generation may only
|
|
|
|
* be accessed in the context of that task, so require no locks.
|
|
|
|
*
|
|
|
|
* The cpuset_common_file_write handler for operations that modify
|
2008-02-07 11:14:45 +03:00
|
|
|
* the cpuset hierarchy holds cgroup_mutex across the entire operation,
|
[PATCH] cpusets: dual semaphore locking overhaul
Overhaul cpuset locking. Replace single semaphore with two semaphores.
The suggestion to use two locks was made by Roman Zippel.
Both locks are global. Code that wants to modify cpusets must first
acquire the exclusive manage_sem, which allows them read-only access to
cpusets, and holds off other would-be modifiers. Before making actual
changes, the second semaphore, callback_sem must be acquired as well. Code
that needs only to query cpusets must acquire callback_sem, which is also a
global exclusive lock.
The earlier problems with double tripping are avoided, because it is
allowed for holders of manage_sem to nest the second callback_sem lock, and
only callback_sem is needed by code called from within __alloc_pages(),
where the double tripping had been possible.
This is not quite the same as a normal read/write semaphore, because
obtaining read-only access with intent to change must hold off other such
attempts, while allowing read-only access w/o such intention. Changing
cpusets involves several related checks and changes, which must be done
while allowing read-only queries (to avoid the double trip), but while
ensuring nothing changes (holding off other would be modifiers.)
This overhaul of cpuset locking also makes careful use of task_lock() to
guard access to the task->cpuset pointer, closing a couple of race
conditions noticed while reading this code (thanks, Roman). I've never
seen these races fail in any use or test.
See further the comments in the code.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-10-31 02:02:30 +03:00
|
|
|
* single threading all such cpuset modifications across the system.
|
|
|
|
*
|
2006-03-23 14:00:18 +03:00
|
|
|
* The cpuset_common_file_read() handlers only hold callback_mutex across
|
[PATCH] cpusets: dual semaphore locking overhaul
Overhaul cpuset locking. Replace single semaphore with two semaphores.
The suggestion to use two locks was made by Roman Zippel.
Both locks are global. Code that wants to modify cpusets must first
acquire the exclusive manage_sem, which allows them read-only access to
cpusets, and holds off other would-be modifiers. Before making actual
changes, the second semaphore, callback_sem must be acquired as well. Code
that needs only to query cpusets must acquire callback_sem, which is also a
global exclusive lock.
The earlier problems with double tripping are avoided, because it is
allowed for holders of manage_sem to nest the second callback_sem lock, and
only callback_sem is needed by code called from within __alloc_pages(),
where the double tripping had been possible.
This is not quite the same as a normal read/write semaphore, because
obtaining read-only access with intent to change must hold off other such
attempts, while allowing read-only access w/o such intention. Changing
cpusets involves several related checks and changes, which must be done
while allowing read-only queries (to avoid the double trip), but while
ensuring nothing changes (holding off other would be modifiers.)
This overhaul of cpuset locking also makes careful use of task_lock() to
guard access to the task->cpuset pointer, closing a couple of race
conditions noticed while reading this code (thanks, Roman). I've never
seen these races fail in any use or test.
See further the comments in the code.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-10-31 02:02:30 +03:00
|
|
|
* small pieces of code, such as when reading out possibly multi-word
|
|
|
|
* cpumasks and nodemasks.
|
|
|
|
*
|
2008-02-07 11:14:45 +03:00
|
|
|
* Accessing a task's cpuset should be done in accordance with the
|
|
|
|
* guidelines for accessing subsystem state in kernel/cgroup.c
|
2005-04-17 02:20:36 +04:00
|
|
|
*/
|
|
|
|
|
2006-03-23 14:00:18 +03:00
|
|
|
static DEFINE_MUTEX(callback_mutex);
|
[PATCH] cpuset semaphore depth check deadlock fix
The cpusets-formalize-intermediate-gfp_kernel-containment patch
has a deadlock problem.
This patch was part of a set of four patches to make more
extensive use of the cpuset 'mem_exclusive' attribute to
manage kernel GFP_KERNEL memory allocations and to constrain
the out-of-memory (oom) killer.
A task that is changing cpusets in particular ways on a system
when it is very short of free memory could double trip over
the global cpuset_sem semaphore (get the lock and then deadlock
trying to get it again).
The second attempt to get cpuset_sem would be in the routine
cpuset_zone_allowed(). This was discovered by code inspection.
I can not reproduce the problem except with an artifically
hacked kernel and a specialized stress test.
In real life you cannot hit this unless you are manipulating
cpusets, and are very unlikely to hit it unless you are rapidly
modifying cpusets on a memory tight system. Even then it would
be a rare occurence.
If you did hit it, the task double tripping over cpuset_sem
would deadlock in the kernel, and any other task also trying
to manipulate cpusets would deadlock there too, on cpuset_sem.
Your batch manager would be wedged solid (if it was cpuset
savvy), but classic Unix shells and utilities would work well
enough to reboot the system.
The unusual condition that led to this bug is that unlike most
semaphores, cpuset_sem _can_ be acquired while in the page
allocation code, when __alloc_pages() calls cpuset_zone_allowed.
So it easy to mistakenly perform the following sequence:
1) task makes system call to alter a cpuset
2) take cpuset_sem
3) try to allocate memory
4) memory allocator, via cpuset_zone_allowed, trys to take cpuset_sem
5) deadlock
The reason that this is not a serious bug for most users
is that almost all calls to allocate memory don't require
taking cpuset_sem. Only some code paths off the beaten
track require taking cpuset_sem -- which is good. Taking
a global semaphore on the main code path for allocating
memory would not scale well.
This patch fixes this deadlock by wrapping the up() and down()
calls on cpuset_sem in kernel/cpuset.c with code that tracks
the nesting depth of the current task on that semaphore, and
only does the real down() if the task doesn't hold the lock
already, and only does the real up() if the nesting depth
(number of unmatched downs) is exactly one.
The previous required use of refresh_mems(), anytime that
the cpuset_sem semaphore was acquired and the code executed
while holding that semaphore might try to allocate memory, is
no longer required. Two refresh_mems() calls were removed
thanks to this. This is a good change, as failing to get
all the necessary refresh_mems() calls placed was a primary
source of bugs in this cpuset code. The only remaining call
to refresh_mems() is made while doing a memory allocation,
if certain task memory placement data needs to be updated
from its cpuset, due to the cpuset having been changed behind
the tasks back.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-09-10 11:26:06 +04:00
|
|
|
|
2007-10-19 10:39:39 +04:00
|
|
|
/* This is ugly, but preserves the userspace API for existing cpuset
|
|
|
|
* users. If someone tries to mount the "cpuset" filesystem, we
|
|
|
|
* silently switch it to mount "cgroup" instead */
|
[PATCH] VFS: Permit filesystem to override root dentry on mount
Extend the get_sb() filesystem operation to take an extra argument that
permits the VFS to pass in the target vfsmount that defines the mountpoint.
The filesystem is then required to manually set the superblock and root dentry
pointers. For most filesystems, this should be done with simple_set_mnt()
which will set the superblock pointer and then set the root dentry to the
superblock's s_root (as per the old default behaviour).
The get_sb() op now returns an integer as there's now no need to return the
superblock pointer.
This patch permits a superblock to be implicitly shared amongst several mount
points, such as can be done with NFS to avoid potential inode aliasing. In
such a case, simple_set_mnt() would not be called, and instead the mnt_root
and mnt_sb would be set directly.
The patch also makes the following changes:
(*) the get_sb_*() convenience functions in the core kernel now take a vfsmount
pointer argument and return an integer, so most filesystems have to change
very little.
(*) If one of the convenience function is not used, then get_sb() should
normally call simple_set_mnt() to instantiate the vfsmount. This will
always return 0, and so can be tail-called from get_sb().
(*) generic_shutdown_super() now calls shrink_dcache_sb() to clean up the
dcache upon superblock destruction rather than shrink_dcache_anon().
This is required because the superblock may now have multiple trees that
aren't actually bound to s_root, but that still need to be cleaned up. The
currently called functions assume that the whole tree is rooted at s_root,
and that anonymous dentries are not the roots of trees which results in
dentries being left unculled.
However, with the way NFS superblock sharing are currently set to be
implemented, these assumptions are violated: the root of the filesystem is
simply a dummy dentry and inode (the real inode for '/' may well be
inaccessible), and all the vfsmounts are rooted on anonymous[*] dentries
with child trees.
[*] Anonymous until discovered from another tree.
(*) The documentation has been adjusted, including the additional bit of
changing ext2_* into foo_* in the documentation.
[akpm@osdl.org: convert ipath_fs, do other stuff]
Signed-off-by: David Howells <dhowells@redhat.com>
Acked-by: Al Viro <viro@zeniv.linux.org.uk>
Cc: Nathan Scott <nathans@sgi.com>
Cc: Roland Dreier <rolandd@cisco.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-06-23 13:02:57 +04:00
|
|
|
static int cpuset_get_sb(struct file_system_type *fs_type,
|
|
|
|
int flags, const char *unused_dev_name,
|
|
|
|
void *data, struct vfsmount *mnt)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2007-10-19 10:39:39 +04:00
|
|
|
struct file_system_type *cgroup_fs = get_fs_type("cgroup");
|
|
|
|
int ret = -ENODEV;
|
|
|
|
if (cgroup_fs) {
|
|
|
|
char mountopts[] =
|
|
|
|
"cpuset,noprefix,"
|
|
|
|
"release_agent=/sbin/cpuset_release_agent";
|
|
|
|
ret = cgroup_fs->get_sb(cgroup_fs, flags,
|
|
|
|
unused_dev_name, mountopts, mnt);
|
|
|
|
put_filesystem(cgroup_fs);
|
|
|
|
}
|
|
|
|
return ret;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct file_system_type cpuset_fs_type = {
|
|
|
|
.name = "cpuset",
|
|
|
|
.get_sb = cpuset_get_sb,
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return in *pmask the portion of a cpusets's cpus_allowed that
|
|
|
|
* are online. If none are online, walk up the cpuset hierarchy
|
|
|
|
* until we find one that does have some online cpus. If we get
|
|
|
|
* all the way to the top and still haven't found any online cpus,
|
|
|
|
* return cpu_online_map. Or if passed a NULL cs from an exit'ing
|
|
|
|
* task, return cpu_online_map.
|
|
|
|
*
|
|
|
|
* One way or another, we guarantee to return some non-empty subset
|
|
|
|
* of cpu_online_map.
|
|
|
|
*
|
2006-03-23 14:00:18 +03:00
|
|
|
* Call with callback_mutex held.
|
2005-04-17 02:20:36 +04:00
|
|
|
*/
|
|
|
|
|
|
|
|
static void guarantee_online_cpus(const struct cpuset *cs, cpumask_t *pmask)
|
|
|
|
{
|
|
|
|
while (cs && !cpus_intersects(cs->cpus_allowed, cpu_online_map))
|
|
|
|
cs = cs->parent;
|
|
|
|
if (cs)
|
|
|
|
cpus_and(*pmask, cs->cpus_allowed, cpu_online_map);
|
|
|
|
else
|
|
|
|
*pmask = cpu_online_map;
|
|
|
|
BUG_ON(!cpus_intersects(*pmask, cpu_online_map));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return in *pmask the portion of a cpusets's mems_allowed that
|
2007-10-16 12:25:38 +04:00
|
|
|
* are online, with memory. If none are online with memory, walk
|
|
|
|
* up the cpuset hierarchy until we find one that does have some
|
|
|
|
* online mems. If we get all the way to the top and still haven't
|
|
|
|
* found any online mems, return node_states[N_HIGH_MEMORY].
|
2005-04-17 02:20:36 +04:00
|
|
|
*
|
|
|
|
* One way or another, we guarantee to return some non-empty subset
|
2007-10-16 12:25:38 +04:00
|
|
|
* of node_states[N_HIGH_MEMORY].
|
2005-04-17 02:20:36 +04:00
|
|
|
*
|
2006-03-23 14:00:18 +03:00
|
|
|
* Call with callback_mutex held.
|
2005-04-17 02:20:36 +04:00
|
|
|
*/
|
|
|
|
|
|
|
|
static void guarantee_online_mems(const struct cpuset *cs, nodemask_t *pmask)
|
|
|
|
{
|
2007-10-16 12:25:38 +04:00
|
|
|
while (cs && !nodes_intersects(cs->mems_allowed,
|
|
|
|
node_states[N_HIGH_MEMORY]))
|
2005-04-17 02:20:36 +04:00
|
|
|
cs = cs->parent;
|
|
|
|
if (cs)
|
2007-10-16 12:25:38 +04:00
|
|
|
nodes_and(*pmask, cs->mems_allowed,
|
|
|
|
node_states[N_HIGH_MEMORY]);
|
2005-04-17 02:20:36 +04:00
|
|
|
else
|
2007-10-16 12:25:38 +04:00
|
|
|
*pmask = node_states[N_HIGH_MEMORY];
|
|
|
|
BUG_ON(!nodes_intersects(*pmask, node_states[N_HIGH_MEMORY]));
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
2006-01-08 12:01:54 +03:00
|
|
|
/**
|
|
|
|
* cpuset_update_task_memory_state - update task memory placement
|
|
|
|
*
|
|
|
|
* If the current tasks cpusets mems_allowed changed behind our
|
|
|
|
* backs, update current->mems_allowed, mems_generation and task NUMA
|
|
|
|
* mempolicy to the new value.
|
[PATCH] cpusets: dual semaphore locking overhaul
Overhaul cpuset locking. Replace single semaphore with two semaphores.
The suggestion to use two locks was made by Roman Zippel.
Both locks are global. Code that wants to modify cpusets must first
acquire the exclusive manage_sem, which allows them read-only access to
cpusets, and holds off other would-be modifiers. Before making actual
changes, the second semaphore, callback_sem must be acquired as well. Code
that needs only to query cpusets must acquire callback_sem, which is also a
global exclusive lock.
The earlier problems with double tripping are avoided, because it is
allowed for holders of manage_sem to nest the second callback_sem lock, and
only callback_sem is needed by code called from within __alloc_pages(),
where the double tripping had been possible.
This is not quite the same as a normal read/write semaphore, because
obtaining read-only access with intent to change must hold off other such
attempts, while allowing read-only access w/o such intention. Changing
cpusets involves several related checks and changes, which must be done
while allowing read-only queries (to avoid the double trip), but while
ensuring nothing changes (holding off other would be modifiers.)
This overhaul of cpuset locking also makes careful use of task_lock() to
guard access to the task->cpuset pointer, closing a couple of race
conditions noticed while reading this code (thanks, Roman). I've never
seen these races fail in any use or test.
See further the comments in the code.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-10-31 02:02:30 +03:00
|
|
|
*
|
2006-01-08 12:01:54 +03:00
|
|
|
* Task mempolicy is updated by rebinding it relative to the
|
|
|
|
* current->cpuset if a task has its memory placement changed.
|
|
|
|
* Do not call this routine if in_interrupt().
|
|
|
|
*
|
2006-03-31 14:30:50 +04:00
|
|
|
* Call without callback_mutex or task_lock() held. May be
|
2008-02-07 11:14:45 +03:00
|
|
|
* called with or without cgroup_mutex held. Thanks in part to
|
|
|
|
* 'the_top_cpuset_hack', the task's cpuset pointer will never
|
2008-03-05 10:32:38 +03:00
|
|
|
* be NULL. This routine also might acquire callback_mutex during
|
|
|
|
* call.
|
[PATCH] cpusets: dual semaphore locking overhaul
Overhaul cpuset locking. Replace single semaphore with two semaphores.
The suggestion to use two locks was made by Roman Zippel.
Both locks are global. Code that wants to modify cpusets must first
acquire the exclusive manage_sem, which allows them read-only access to
cpusets, and holds off other would-be modifiers. Before making actual
changes, the second semaphore, callback_sem must be acquired as well. Code
that needs only to query cpusets must acquire callback_sem, which is also a
global exclusive lock.
The earlier problems with double tripping are avoided, because it is
allowed for holders of manage_sem to nest the second callback_sem lock, and
only callback_sem is needed by code called from within __alloc_pages(),
where the double tripping had been possible.
This is not quite the same as a normal read/write semaphore, because
obtaining read-only access with intent to change must hold off other such
attempts, while allowing read-only access w/o such intention. Changing
cpusets involves several related checks and changes, which must be done
while allowing read-only queries (to avoid the double trip), but while
ensuring nothing changes (holding off other would be modifiers.)
This overhaul of cpuset locking also makes careful use of task_lock() to
guard access to the task->cpuset pointer, closing a couple of race
conditions noticed while reading this code (thanks, Roman). I've never
seen these races fail in any use or test.
See further the comments in the code.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-10-31 02:02:30 +03:00
|
|
|
*
|
[PATCH] cpuset: use rcu directly optimization
Optimize the cpuset impact on page allocation, the most performance critical
cpuset hook in the kernel.
On each page allocation, the cpuset hook needs to check for a possible change
in the current tasks cpuset. It can now handle the common case, of no change,
without taking any spinlock or semaphore, thanks to RCU.
Convert a spinlock on the current task to an rcu_read_lock(), saving
approximately a memory barrier and an atomic op, depending on architecture.
This is done by adding rcu_assign_pointer() and synchronize_rcu() calls to the
write side of the task->cpuset pointer, in cpuset.c:attach_task(), to delay
freeing up a detached cpuset until after any critical sections referencing
that pointer.
Thanks to Andi Kleen, Nick Piggin and Eric Dumazet for ideas.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 12:02:02 +03:00
|
|
|
* Reading current->cpuset->mems_generation doesn't need task_lock
|
|
|
|
* to guard the current->cpuset derefence, because it is guarded
|
2008-02-07 11:14:45 +03:00
|
|
|
* from concurrent freeing of current->cpuset using RCU.
|
[PATCH] cpuset: use rcu directly optimization
Optimize the cpuset impact on page allocation, the most performance critical
cpuset hook in the kernel.
On each page allocation, the cpuset hook needs to check for a possible change
in the current tasks cpuset. It can now handle the common case, of no change,
without taking any spinlock or semaphore, thanks to RCU.
Convert a spinlock on the current task to an rcu_read_lock(), saving
approximately a memory barrier and an atomic op, depending on architecture.
This is done by adding rcu_assign_pointer() and synchronize_rcu() calls to the
write side of the task->cpuset pointer, in cpuset.c:attach_task(), to delay
freeing up a detached cpuset until after any critical sections referencing
that pointer.
Thanks to Andi Kleen, Nick Piggin and Eric Dumazet for ideas.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 12:02:02 +03:00
|
|
|
*
|
|
|
|
* The rcu_dereference() is technically probably not needed,
|
|
|
|
* as I don't actually mind if I see a new cpuset pointer but
|
|
|
|
* an old value of mems_generation. However this really only
|
|
|
|
* matters on alpha systems using cpusets heavily. If I dropped
|
|
|
|
* that rcu_dereference(), it would save them a memory barrier.
|
|
|
|
* For all other arch's, rcu_dereference is a no-op anyway, and for
|
|
|
|
* alpha systems not using cpusets, another planned optimization,
|
|
|
|
* avoiding the rcu critical section for tasks in the root cpuset
|
|
|
|
* which is statically allocated, so can't vanish, will make this
|
|
|
|
* irrelevant. Better to use RCU as intended, than to engage in
|
|
|
|
* some cute trick to save a memory barrier that is impossible to
|
|
|
|
* test, for alpha systems using cpusets heavily, which might not
|
|
|
|
* even exist.
|
[PATCH] cpusets: dual semaphore locking overhaul
Overhaul cpuset locking. Replace single semaphore with two semaphores.
The suggestion to use two locks was made by Roman Zippel.
Both locks are global. Code that wants to modify cpusets must first
acquire the exclusive manage_sem, which allows them read-only access to
cpusets, and holds off other would-be modifiers. Before making actual
changes, the second semaphore, callback_sem must be acquired as well. Code
that needs only to query cpusets must acquire callback_sem, which is also a
global exclusive lock.
The earlier problems with double tripping are avoided, because it is
allowed for holders of manage_sem to nest the second callback_sem lock, and
only callback_sem is needed by code called from within __alloc_pages(),
where the double tripping had been possible.
This is not quite the same as a normal read/write semaphore, because
obtaining read-only access with intent to change must hold off other such
attempts, while allowing read-only access w/o such intention. Changing
cpusets involves several related checks and changes, which must be done
while allowing read-only queries (to avoid the double trip), but while
ensuring nothing changes (holding off other would be modifiers.)
This overhaul of cpuset locking also makes careful use of task_lock() to
guard access to the task->cpuset pointer, closing a couple of race
conditions noticed while reading this code (thanks, Roman). I've never
seen these races fail in any use or test.
See further the comments in the code.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-10-31 02:02:30 +03:00
|
|
|
*
|
|
|
|
* This routine is needed to update the per-task mems_allowed data,
|
|
|
|
* within the tasks context, when it is trying to allocate memory
|
|
|
|
* (in various mm/mempolicy.c routines) and notices that some other
|
|
|
|
* task has been modifying its cpuset.
|
2005-04-17 02:20:36 +04:00
|
|
|
*/
|
|
|
|
|
2006-02-03 14:04:23 +03:00
|
|
|
void cpuset_update_task_memory_state(void)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
[PATCH] cpusets: dual semaphore locking overhaul
Overhaul cpuset locking. Replace single semaphore with two semaphores.
The suggestion to use two locks was made by Roman Zippel.
Both locks are global. Code that wants to modify cpusets must first
acquire the exclusive manage_sem, which allows them read-only access to
cpusets, and holds off other would-be modifiers. Before making actual
changes, the second semaphore, callback_sem must be acquired as well. Code
that needs only to query cpusets must acquire callback_sem, which is also a
global exclusive lock.
The earlier problems with double tripping are avoided, because it is
allowed for holders of manage_sem to nest the second callback_sem lock, and
only callback_sem is needed by code called from within __alloc_pages(),
where the double tripping had been possible.
This is not quite the same as a normal read/write semaphore, because
obtaining read-only access with intent to change must hold off other such
attempts, while allowing read-only access w/o such intention. Changing
cpusets involves several related checks and changes, which must be done
while allowing read-only queries (to avoid the double trip), but while
ensuring nothing changes (holding off other would be modifiers.)
This overhaul of cpuset locking also makes careful use of task_lock() to
guard access to the task->cpuset pointer, closing a couple of race
conditions noticed while reading this code (thanks, Roman). I've never
seen these races fail in any use or test.
See further the comments in the code.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-10-31 02:02:30 +03:00
|
|
|
int my_cpusets_mem_gen;
|
2006-01-08 12:01:54 +03:00
|
|
|
struct task_struct *tsk = current;
|
[PATCH] cpuset: use rcu directly optimization
Optimize the cpuset impact on page allocation, the most performance critical
cpuset hook in the kernel.
On each page allocation, the cpuset hook needs to check for a possible change
in the current tasks cpuset. It can now handle the common case, of no change,
without taking any spinlock or semaphore, thanks to RCU.
Convert a spinlock on the current task to an rcu_read_lock(), saving
approximately a memory barrier and an atomic op, depending on architecture.
This is done by adding rcu_assign_pointer() and synchronize_rcu() calls to the
write side of the task->cpuset pointer, in cpuset.c:attach_task(), to delay
freeing up a detached cpuset until after any critical sections referencing
that pointer.
Thanks to Andi Kleen, Nick Piggin and Eric Dumazet for ideas.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 12:02:02 +03:00
|
|
|
struct cpuset *cs;
|
[PATCH] cpusets: dual semaphore locking overhaul
Overhaul cpuset locking. Replace single semaphore with two semaphores.
The suggestion to use two locks was made by Roman Zippel.
Both locks are global. Code that wants to modify cpusets must first
acquire the exclusive manage_sem, which allows them read-only access to
cpusets, and holds off other would-be modifiers. Before making actual
changes, the second semaphore, callback_sem must be acquired as well. Code
that needs only to query cpusets must acquire callback_sem, which is also a
global exclusive lock.
The earlier problems with double tripping are avoided, because it is
allowed for holders of manage_sem to nest the second callback_sem lock, and
only callback_sem is needed by code called from within __alloc_pages(),
where the double tripping had been possible.
This is not quite the same as a normal read/write semaphore, because
obtaining read-only access with intent to change must hold off other such
attempts, while allowing read-only access w/o such intention. Changing
cpusets involves several related checks and changes, which must be done
while allowing read-only queries (to avoid the double trip), but while
ensuring nothing changes (holding off other would be modifiers.)
This overhaul of cpuset locking also makes careful use of task_lock() to
guard access to the task->cpuset pointer, closing a couple of race
conditions noticed while reading this code (thanks, Roman). I've never
seen these races fail in any use or test.
See further the comments in the code.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-10-31 02:02:30 +03:00
|
|
|
|
2007-10-19 10:39:39 +04:00
|
|
|
if (task_cs(tsk) == &top_cpuset) {
|
2006-01-08 12:02:04 +03:00
|
|
|
/* Don't need rcu for top_cpuset. It's never freed. */
|
|
|
|
my_cpusets_mem_gen = top_cpuset.mems_generation;
|
|
|
|
} else {
|
|
|
|
rcu_read_lock();
|
2007-10-19 10:39:39 +04:00
|
|
|
my_cpusets_mem_gen = task_cs(current)->mems_generation;
|
2006-01-08 12:02:04 +03:00
|
|
|
rcu_read_unlock();
|
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2006-01-08 12:01:54 +03:00
|
|
|
if (my_cpusets_mem_gen != tsk->cpuset_mems_generation) {
|
2006-03-23 14:00:18 +03:00
|
|
|
mutex_lock(&callback_mutex);
|
2006-01-08 12:01:54 +03:00
|
|
|
task_lock(tsk);
|
2007-10-19 10:39:39 +04:00
|
|
|
cs = task_cs(tsk); /* Maybe changed when task not locked */
|
2006-01-08 12:01:54 +03:00
|
|
|
guarantee_online_mems(cs, &tsk->mems_allowed);
|
|
|
|
tsk->cpuset_mems_generation = cs->mems_generation;
|
[PATCH] cpuset memory spread basic implementation
This patch provides the implementation and cpuset interface for an alternative
memory allocation policy that can be applied to certain kinds of memory
allocations, such as the page cache (file system buffers) and some slab caches
(such as inode caches).
The policy is called "memory spreading." If enabled, it spreads out these
kinds of memory allocations over all the nodes allowed to a task, instead of
preferring to place them on the node where the task is executing.
All other kinds of allocations, including anonymous pages for a tasks stack
and data regions, are not affected by this policy choice, and continue to be
allocated preferring the node local to execution, as modified by the NUMA
mempolicy.
There are two boolean flag files per cpuset that control where the kernel
allocates pages for the file system buffers and related in kernel data
structures. They are called 'memory_spread_page' and 'memory_spread_slab'.
If the per-cpuset boolean flag file 'memory_spread_page' is set, then the
kernel will spread the file system buffers (page cache) evenly over all the
nodes that the faulting task is allowed to use, instead of preferring to put
those pages on the node where the task is running.
If the per-cpuset boolean flag file 'memory_spread_slab' is set, then the
kernel will spread some file system related slab caches, such as for inodes
and dentries evenly over all the nodes that the faulting task is allowed to
use, instead of preferring to put those pages on the node where the task is
running.
The implementation is simple. Setting the cpuset flags 'memory_spread_page'
or 'memory_spread_cache' turns on the per-process flags PF_SPREAD_PAGE or
PF_SPREAD_SLAB, respectively, for each task that is in the cpuset or
subsequently joins that cpuset. In subsequent patches, the page allocation
calls for the affected page cache and slab caches are modified to perform an
inline check for these flags, and if set, a call to a new routine
cpuset_mem_spread_node() returns the node to prefer for the allocation.
The cpuset_mem_spread_node() routine is also simple. It uses the value of a
per-task rotor cpuset_mem_spread_rotor to select the next node in the current
tasks mems_allowed to prefer for the allocation.
This policy can provide substantial improvements for jobs that need to place
thread local data on the corresponding node, but that need to access large
file system data sets that need to be spread across the several nodes in the
jobs cpuset in order to fit. Without this patch, especially for jobs that
might have one thread reading in the data set, the memory allocation across
the nodes in the jobs cpuset can become very uneven.
A couple of Copyright year ranges are updated as well. And a couple of email
addresses that can be found in the MAINTAINERS file are removed.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-03-24 14:16:03 +03:00
|
|
|
if (is_spread_page(cs))
|
|
|
|
tsk->flags |= PF_SPREAD_PAGE;
|
|
|
|
else
|
|
|
|
tsk->flags &= ~PF_SPREAD_PAGE;
|
|
|
|
if (is_spread_slab(cs))
|
|
|
|
tsk->flags |= PF_SPREAD_SLAB;
|
|
|
|
else
|
|
|
|
tsk->flags &= ~PF_SPREAD_SLAB;
|
2006-01-08 12:01:54 +03:00
|
|
|
task_unlock(tsk);
|
2006-03-23 14:00:18 +03:00
|
|
|
mutex_unlock(&callback_mutex);
|
[PATCH] cpuset: numa_policy_rebind cleanup
Cleanup, reorganize and make more robust the mempolicy.c code to rebind
mempolicies relative to the containing cpuset after a tasks memory placement
changes.
The real motivator for this cleanup patch is to lay more groundwork for the
upcoming patch to correctly rebind NUMA mempolicies that are attached to vma's
after the containing cpuset memory placement changes.
NUMA mempolicies are constrained by the cpuset their task is a member of.
When either (1) a task is moved to a different cpuset, or (2) the 'mems'
mems_allowed of a cpuset is changed, then the NUMA mempolicies have embedded
node numbers (for MPOL_BIND, MPOL_INTERLEAVE and MPOL_PREFERRED) that need to
be recalculated, relative to their new cpuset placement.
The old code used an unreliable method of determining what was the old
mems_allowed constraining the mempolicy. It just looked at the tasks
mems_allowed value. This sort of worked with the present code, that just
rebinds the -task- mempolicy, and leaves any -vma- mempolicies broken,
referring to the old nodes. But in an upcoming patch, the vma mempolicies
will be rebound as well. Then the order in which the various task and vma
mempolicies are updated will no longer be deterministic, and one can no longer
count on the task->mems_allowed holding the old value for as long as needed.
It's not even clear if the current code was guaranteed to work reliably for
task mempolicies.
So I added a mems_allowed field to each mempolicy, stating exactly what
mems_allowed the policy is relative to, and updated synchronously and reliably
anytime that the mempolicy is rebound.
Also removed a useless wrapper routine, numa_policy_rebind(), and had its
caller, cpuset_update_task_memory_state(), call directly to the rewritten
policy_rebind() routine, and made that rebind routine extern instead of
static, and added a "mpol_" prefix to its name, making it
mpol_rebind_policy().
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 12:01:56 +03:00
|
|
|
mpol_rebind_task(tsk, &tsk->mems_allowed);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* is_cpuset_subset(p, q) - Is cpuset p a subset of cpuset q?
|
|
|
|
*
|
|
|
|
* One cpuset is a subset of another if all its allowed CPUs and
|
|
|
|
* Memory Nodes are a subset of the other, and its exclusive flags
|
2008-02-07 11:14:45 +03:00
|
|
|
* are only set if the other's are set. Call holding cgroup_mutex.
|
2005-04-17 02:20:36 +04:00
|
|
|
*/
|
|
|
|
|
|
|
|
static int is_cpuset_subset(const struct cpuset *p, const struct cpuset *q)
|
|
|
|
{
|
|
|
|
return cpus_subset(p->cpus_allowed, q->cpus_allowed) &&
|
|
|
|
nodes_subset(p->mems_allowed, q->mems_allowed) &&
|
|
|
|
is_cpu_exclusive(p) <= is_cpu_exclusive(q) &&
|
|
|
|
is_mem_exclusive(p) <= is_mem_exclusive(q);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* validate_change() - Used to validate that any proposed cpuset change
|
|
|
|
* follows the structural rules for cpusets.
|
|
|
|
*
|
|
|
|
* If we replaced the flag and mask values of the current cpuset
|
|
|
|
* (cur) with those values in the trial cpuset (trial), would
|
|
|
|
* our various subset and exclusive rules still be valid? Presumes
|
2008-02-07 11:14:45 +03:00
|
|
|
* cgroup_mutex held.
|
2005-04-17 02:20:36 +04:00
|
|
|
*
|
|
|
|
* 'cur' is the address of an actual, in-use cpuset. Operations
|
|
|
|
* such as list traversal that depend on the actual address of the
|
|
|
|
* cpuset in the list must use cur below, not trial.
|
|
|
|
*
|
|
|
|
* 'trial' is the address of bulk structure copy of cur, with
|
|
|
|
* perhaps one or more of the fields cpus_allowed, mems_allowed,
|
|
|
|
* or flags changed to new, trial values.
|
|
|
|
*
|
|
|
|
* Return 0 if valid, -errno if not.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int validate_change(const struct cpuset *cur, const struct cpuset *trial)
|
|
|
|
{
|
2007-10-19 10:39:39 +04:00
|
|
|
struct cgroup *cont;
|
2005-04-17 02:20:36 +04:00
|
|
|
struct cpuset *c, *par;
|
|
|
|
|
|
|
|
/* Each of our child cpusets must be a subset of us */
|
2007-10-19 10:39:39 +04:00
|
|
|
list_for_each_entry(cont, &cur->css.cgroup->children, sibling) {
|
|
|
|
if (!is_cpuset_subset(cgroup_cs(cont), trial))
|
2005-04-17 02:20:36 +04:00
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Remaining checks don't apply to root cpuset */
|
2006-12-07 07:36:15 +03:00
|
|
|
if (cur == &top_cpuset)
|
2005-04-17 02:20:36 +04:00
|
|
|
return 0;
|
|
|
|
|
2006-12-07 07:36:15 +03:00
|
|
|
par = cur->parent;
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
/* We must be a subset of our parent cpuset */
|
|
|
|
if (!is_cpuset_subset(trial, par))
|
|
|
|
return -EACCES;
|
|
|
|
|
2008-02-07 11:14:45 +03:00
|
|
|
/*
|
|
|
|
* If either I or some sibling (!= me) is exclusive, we can't
|
|
|
|
* overlap
|
|
|
|
*/
|
2007-10-19 10:39:39 +04:00
|
|
|
list_for_each_entry(cont, &par->css.cgroup->children, sibling) {
|
|
|
|
c = cgroup_cs(cont);
|
2005-04-17 02:20:36 +04:00
|
|
|
if ((is_cpu_exclusive(trial) || is_cpu_exclusive(c)) &&
|
|
|
|
c != cur &&
|
|
|
|
cpus_intersects(trial->cpus_allowed, c->cpus_allowed))
|
|
|
|
return -EINVAL;
|
|
|
|
if ((is_mem_exclusive(trial) || is_mem_exclusive(c)) &&
|
|
|
|
c != cur &&
|
|
|
|
nodes_intersects(trial->mems_allowed, c->mems_allowed))
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2007-10-19 10:40:21 +04:00
|
|
|
/* Cpusets with tasks can't have empty cpus_allowed or mems_allowed */
|
|
|
|
if (cgroup_task_count(cur->css.cgroup)) {
|
|
|
|
if (cpus_empty(trial->cpus_allowed) ||
|
|
|
|
nodes_empty(trial->mems_allowed)) {
|
|
|
|
return -ENOSPC;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-10-19 10:40:20 +04:00
|
|
|
/*
|
|
|
|
* Helper routine for rebuild_sched_domains().
|
|
|
|
* Do cpusets a, b have overlapping cpus_allowed masks?
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int cpusets_overlap(struct cpuset *a, struct cpuset *b)
|
|
|
|
{
|
|
|
|
return cpus_intersects(a->cpus_allowed, b->cpus_allowed);
|
|
|
|
}
|
|
|
|
|
2008-04-15 09:04:23 +04:00
|
|
|
static void
|
|
|
|
update_domain_attr(struct sched_domain_attr *dattr, struct cpuset *c)
|
|
|
|
{
|
|
|
|
if (!dattr)
|
|
|
|
return;
|
|
|
|
if (dattr->relax_domain_level < c->relax_domain_level)
|
|
|
|
dattr->relax_domain_level = c->relax_domain_level;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-10-19 10:40:20 +04:00
|
|
|
/*
|
|
|
|
* rebuild_sched_domains()
|
|
|
|
*
|
|
|
|
* If the flag 'sched_load_balance' of any cpuset with non-empty
|
|
|
|
* 'cpus' changes, or if the 'cpus' allowed changes in any cpuset
|
|
|
|
* which has that flag enabled, or if any cpuset with a non-empty
|
|
|
|
* 'cpus' is removed, then call this routine to rebuild the
|
|
|
|
* scheduler's dynamic sched domains.
|
|
|
|
*
|
|
|
|
* This routine builds a partial partition of the systems CPUs
|
|
|
|
* (the set of non-overlappping cpumask_t's in the array 'part'
|
|
|
|
* below), and passes that partial partition to the kernel/sched.c
|
|
|
|
* partition_sched_domains() routine, which will rebuild the
|
|
|
|
* schedulers load balancing domains (sched domains) as specified
|
|
|
|
* by that partial partition. A 'partial partition' is a set of
|
|
|
|
* non-overlapping subsets whose union is a subset of that set.
|
|
|
|
*
|
|
|
|
* See "What is sched_load_balance" in Documentation/cpusets.txt
|
|
|
|
* for a background explanation of this.
|
|
|
|
*
|
|
|
|
* Does not return errors, on the theory that the callers of this
|
|
|
|
* routine would rather not worry about failures to rebuild sched
|
|
|
|
* domains when operating in the severe memory shortage situations
|
|
|
|
* that could cause allocation failures below.
|
|
|
|
*
|
|
|
|
* Call with cgroup_mutex held. May take callback_mutex during
|
|
|
|
* call due to the kfifo_alloc() and kmalloc() calls. May nest
|
2008-01-25 23:08:02 +03:00
|
|
|
* a call to the get_online_cpus()/put_online_cpus() pair.
|
2007-10-19 10:40:20 +04:00
|
|
|
* Must not be called holding callback_mutex, because we must not
|
2008-01-25 23:08:02 +03:00
|
|
|
* call get_online_cpus() while holding callback_mutex. Elsewhere
|
|
|
|
* the kernel nests callback_mutex inside get_online_cpus() calls.
|
2007-10-19 10:40:20 +04:00
|
|
|
* So the reverse nesting would risk an ABBA deadlock.
|
|
|
|
*
|
|
|
|
* The three key local variables below are:
|
|
|
|
* q - a kfifo queue of cpuset pointers, used to implement a
|
|
|
|
* top-down scan of all cpusets. This scan loads a pointer
|
|
|
|
* to each cpuset marked is_sched_load_balance into the
|
|
|
|
* array 'csa'. For our purposes, rebuilding the schedulers
|
|
|
|
* sched domains, we can ignore !is_sched_load_balance cpusets.
|
|
|
|
* csa - (for CpuSet Array) Array of pointers to all the cpusets
|
|
|
|
* that need to be load balanced, for convenient iterative
|
|
|
|
* access by the subsequent code that finds the best partition,
|
|
|
|
* i.e the set of domains (subsets) of CPUs such that the
|
|
|
|
* cpus_allowed of every cpuset marked is_sched_load_balance
|
|
|
|
* is a subset of one of these domains, while there are as
|
|
|
|
* many such domains as possible, each as small as possible.
|
|
|
|
* doms - Conversion of 'csa' to an array of cpumasks, for passing to
|
|
|
|
* the kernel/sched.c routine partition_sched_domains() in a
|
|
|
|
* convenient format, that can be easily compared to the prior
|
|
|
|
* value to determine what partition elements (sched domains)
|
|
|
|
* were changed (added or removed.)
|
|
|
|
*
|
|
|
|
* Finding the best partition (set of domains):
|
|
|
|
* The triple nested loops below over i, j, k scan over the
|
|
|
|
* load balanced cpusets (using the array of cpuset pointers in
|
|
|
|
* csa[]) looking for pairs of cpusets that have overlapping
|
|
|
|
* cpus_allowed, but which don't have the same 'pn' partition
|
|
|
|
* number and gives them in the same partition number. It keeps
|
|
|
|
* looping on the 'restart' label until it can no longer find
|
|
|
|
* any such pairs.
|
|
|
|
*
|
|
|
|
* The union of the cpus_allowed masks from the set of
|
|
|
|
* all cpusets having the same 'pn' value then form the one
|
|
|
|
* element of the partition (one sched domain) to be passed to
|
|
|
|
* partition_sched_domains().
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void rebuild_sched_domains(void)
|
|
|
|
{
|
|
|
|
struct kfifo *q; /* queue of cpusets to be scanned */
|
|
|
|
struct cpuset *cp; /* scans q */
|
|
|
|
struct cpuset **csa; /* array of all cpuset ptrs */
|
|
|
|
int csn; /* how many cpuset ptrs in csa so far */
|
|
|
|
int i, j, k; /* indices for partition finding loops */
|
|
|
|
cpumask_t *doms; /* resulting partition; i.e. sched domains */
|
2008-04-15 09:04:23 +04:00
|
|
|
struct sched_domain_attr *dattr; /* attributes for custom domains */
|
2007-10-19 10:40:20 +04:00
|
|
|
int ndoms; /* number of sched domains in result */
|
|
|
|
int nslot; /* next empty doms[] cpumask_t slot */
|
|
|
|
|
|
|
|
q = NULL;
|
|
|
|
csa = NULL;
|
|
|
|
doms = NULL;
|
2008-04-15 09:04:23 +04:00
|
|
|
dattr = NULL;
|
2007-10-19 10:40:20 +04:00
|
|
|
|
|
|
|
/* Special case for the 99% of systems with one, full, sched domain */
|
|
|
|
if (is_sched_load_balance(&top_cpuset)) {
|
|
|
|
ndoms = 1;
|
|
|
|
doms = kmalloc(sizeof(cpumask_t), GFP_KERNEL);
|
|
|
|
if (!doms)
|
|
|
|
goto rebuild;
|
2008-04-15 09:04:23 +04:00
|
|
|
dattr = kmalloc(sizeof(struct sched_domain_attr), GFP_KERNEL);
|
|
|
|
if (dattr) {
|
|
|
|
*dattr = SD_ATTR_INIT;
|
|
|
|
update_domain_attr(dattr, &top_cpuset);
|
|
|
|
}
|
2007-10-19 10:40:20 +04:00
|
|
|
*doms = top_cpuset.cpus_allowed;
|
|
|
|
goto rebuild;
|
|
|
|
}
|
|
|
|
|
|
|
|
q = kfifo_alloc(number_of_cpusets * sizeof(cp), GFP_KERNEL, NULL);
|
|
|
|
if (IS_ERR(q))
|
|
|
|
goto done;
|
|
|
|
csa = kmalloc(number_of_cpusets * sizeof(cp), GFP_KERNEL);
|
|
|
|
if (!csa)
|
|
|
|
goto done;
|
|
|
|
csn = 0;
|
|
|
|
|
|
|
|
cp = &top_cpuset;
|
|
|
|
__kfifo_put(q, (void *)&cp, sizeof(cp));
|
|
|
|
while (__kfifo_get(q, (void *)&cp, sizeof(cp))) {
|
|
|
|
struct cgroup *cont;
|
|
|
|
struct cpuset *child; /* scans child cpusets of cp */
|
|
|
|
if (is_sched_load_balance(cp))
|
|
|
|
csa[csn++] = cp;
|
|
|
|
list_for_each_entry(cont, &cp->css.cgroup->children, sibling) {
|
|
|
|
child = cgroup_cs(cont);
|
|
|
|
__kfifo_put(q, (void *)&child, sizeof(cp));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < csn; i++)
|
|
|
|
csa[i]->pn = i;
|
|
|
|
ndoms = csn;
|
|
|
|
|
|
|
|
restart:
|
|
|
|
/* Find the best partition (set of sched domains) */
|
|
|
|
for (i = 0; i < csn; i++) {
|
|
|
|
struct cpuset *a = csa[i];
|
|
|
|
int apn = a->pn;
|
|
|
|
|
|
|
|
for (j = 0; j < csn; j++) {
|
|
|
|
struct cpuset *b = csa[j];
|
|
|
|
int bpn = b->pn;
|
|
|
|
|
|
|
|
if (apn != bpn && cpusets_overlap(a, b)) {
|
|
|
|
for (k = 0; k < csn; k++) {
|
|
|
|
struct cpuset *c = csa[k];
|
|
|
|
|
|
|
|
if (c->pn == bpn)
|
|
|
|
c->pn = apn;
|
|
|
|
}
|
|
|
|
ndoms--; /* one less element */
|
|
|
|
goto restart;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Convert <csn, csa> to <ndoms, doms> */
|
|
|
|
doms = kmalloc(ndoms * sizeof(cpumask_t), GFP_KERNEL);
|
|
|
|
if (!doms)
|
|
|
|
goto rebuild;
|
2008-04-15 09:04:23 +04:00
|
|
|
dattr = kmalloc(ndoms * sizeof(struct sched_domain_attr), GFP_KERNEL);
|
2007-10-19 10:40:20 +04:00
|
|
|
|
|
|
|
for (nslot = 0, i = 0; i < csn; i++) {
|
|
|
|
struct cpuset *a = csa[i];
|
|
|
|
int apn = a->pn;
|
|
|
|
|
|
|
|
if (apn >= 0) {
|
|
|
|
cpumask_t *dp = doms + nslot;
|
|
|
|
|
|
|
|
if (nslot == ndoms) {
|
|
|
|
static int warnings = 10;
|
|
|
|
if (warnings) {
|
|
|
|
printk(KERN_WARNING
|
|
|
|
"rebuild_sched_domains confused:"
|
|
|
|
" nslot %d, ndoms %d, csn %d, i %d,"
|
|
|
|
" apn %d\n",
|
|
|
|
nslot, ndoms, csn, i, apn);
|
|
|
|
warnings--;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
cpus_clear(*dp);
|
2008-04-15 09:04:23 +04:00
|
|
|
if (dattr)
|
|
|
|
*(dattr + nslot) = SD_ATTR_INIT;
|
2007-10-19 10:40:20 +04:00
|
|
|
for (j = i; j < csn; j++) {
|
|
|
|
struct cpuset *b = csa[j];
|
|
|
|
|
|
|
|
if (apn == b->pn) {
|
|
|
|
cpus_or(*dp, *dp, b->cpus_allowed);
|
|
|
|
b->pn = -1;
|
2008-04-15 09:04:23 +04:00
|
|
|
update_domain_attr(dattr, b);
|
2007-10-19 10:40:20 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
nslot++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
BUG_ON(nslot != ndoms);
|
|
|
|
|
|
|
|
rebuild:
|
|
|
|
/* Have scheduler rebuild sched domains */
|
2008-01-25 23:08:02 +03:00
|
|
|
get_online_cpus();
|
2008-04-15 09:04:23 +04:00
|
|
|
partition_sched_domains(ndoms, doms, dattr);
|
2008-01-25 23:08:02 +03:00
|
|
|
put_online_cpus();
|
2007-10-19 10:40:20 +04:00
|
|
|
|
|
|
|
done:
|
|
|
|
if (q && !IS_ERR(q))
|
|
|
|
kfifo_free(q);
|
|
|
|
kfree(csa);
|
|
|
|
/* Don't kfree(doms) -- partition_sched_domains() does that. */
|
2008-04-15 09:04:23 +04:00
|
|
|
/* Don't kfree(dattr) -- partition_sched_domains() does that. */
|
2007-10-19 10:40:20 +04:00
|
|
|
}
|
|
|
|
|
2007-10-19 10:40:22 +04:00
|
|
|
static inline int started_after_time(struct task_struct *t1,
|
|
|
|
struct timespec *time,
|
|
|
|
struct task_struct *t2)
|
|
|
|
{
|
|
|
|
int start_diff = timespec_compare(&t1->start_time, time);
|
|
|
|
if (start_diff > 0) {
|
|
|
|
return 1;
|
|
|
|
} else if (start_diff < 0) {
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Arbitrarily, if two processes started at the same
|
|
|
|
* time, we'll say that the lower pointer value
|
|
|
|
* started first. Note that t2 may have exited by now
|
|
|
|
* so this may not be a valid pointer any longer, but
|
|
|
|
* that's fine - it still serves to distinguish
|
|
|
|
* between two tasks started (effectively)
|
|
|
|
* simultaneously.
|
|
|
|
*/
|
|
|
|
return t1 > t2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int started_after(void *p1, void *p2)
|
|
|
|
{
|
|
|
|
struct task_struct *t1 = p1;
|
|
|
|
struct task_struct *t2 = p2;
|
|
|
|
return started_after_time(t1, &t2->start_time, t2);
|
|
|
|
}
|
|
|
|
|
2008-02-07 11:14:44 +03:00
|
|
|
/**
|
|
|
|
* cpuset_test_cpumask - test a task's cpus_allowed versus its cpuset's
|
|
|
|
* @tsk: task to test
|
|
|
|
* @scan: struct cgroup_scanner contained in its struct cpuset_hotplug_scanner
|
|
|
|
*
|
2008-02-07 11:14:45 +03:00
|
|
|
* Call with cgroup_mutex held. May take callback_mutex during call.
|
2008-02-07 11:14:44 +03:00
|
|
|
* Called for each task in a cgroup by cgroup_scan_tasks().
|
|
|
|
* Return nonzero if this tasks's cpus_allowed mask should be changed (in other
|
|
|
|
* words, if its mask is not equal to its cpuset's mask).
|
[PATCH] cpusets: dual semaphore locking overhaul
Overhaul cpuset locking. Replace single semaphore with two semaphores.
The suggestion to use two locks was made by Roman Zippel.
Both locks are global. Code that wants to modify cpusets must first
acquire the exclusive manage_sem, which allows them read-only access to
cpusets, and holds off other would-be modifiers. Before making actual
changes, the second semaphore, callback_sem must be acquired as well. Code
that needs only to query cpusets must acquire callback_sem, which is also a
global exclusive lock.
The earlier problems with double tripping are avoided, because it is
allowed for holders of manage_sem to nest the second callback_sem lock, and
only callback_sem is needed by code called from within __alloc_pages(),
where the double tripping had been possible.
This is not quite the same as a normal read/write semaphore, because
obtaining read-only access with intent to change must hold off other such
attempts, while allowing read-only access w/o such intention. Changing
cpusets involves several related checks and changes, which must be done
while allowing read-only queries (to avoid the double trip), but while
ensuring nothing changes (holding off other would be modifiers.)
This overhaul of cpuset locking also makes careful use of task_lock() to
guard access to the task->cpuset pointer, closing a couple of race
conditions noticed while reading this code (thanks, Roman). I've never
seen these races fail in any use or test.
See further the comments in the code.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-10-31 02:02:30 +03:00
|
|
|
*/
|
2008-04-29 12:00:25 +04:00
|
|
|
static int cpuset_test_cpumask(struct task_struct *tsk,
|
|
|
|
struct cgroup_scanner *scan)
|
2008-02-07 11:14:44 +03:00
|
|
|
{
|
|
|
|
return !cpus_equal(tsk->cpus_allowed,
|
|
|
|
(cgroup_cs(scan->cg))->cpus_allowed);
|
|
|
|
}
|
[PATCH] cpusets: dual semaphore locking overhaul
Overhaul cpuset locking. Replace single semaphore with two semaphores.
The suggestion to use two locks was made by Roman Zippel.
Both locks are global. Code that wants to modify cpusets must first
acquire the exclusive manage_sem, which allows them read-only access to
cpusets, and holds off other would-be modifiers. Before making actual
changes, the second semaphore, callback_sem must be acquired as well. Code
that needs only to query cpusets must acquire callback_sem, which is also a
global exclusive lock.
The earlier problems with double tripping are avoided, because it is
allowed for holders of manage_sem to nest the second callback_sem lock, and
only callback_sem is needed by code called from within __alloc_pages(),
where the double tripping had been possible.
This is not quite the same as a normal read/write semaphore, because
obtaining read-only access with intent to change must hold off other such
attempts, while allowing read-only access w/o such intention. Changing
cpusets involves several related checks and changes, which must be done
while allowing read-only queries (to avoid the double trip), but while
ensuring nothing changes (holding off other would be modifiers.)
This overhaul of cpuset locking also makes careful use of task_lock() to
guard access to the task->cpuset pointer, closing a couple of race
conditions noticed while reading this code (thanks, Roman). I've never
seen these races fail in any use or test.
See further the comments in the code.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-10-31 02:02:30 +03:00
|
|
|
|
2008-02-07 11:14:44 +03:00
|
|
|
/**
|
|
|
|
* cpuset_change_cpumask - make a task's cpus_allowed the same as its cpuset's
|
|
|
|
* @tsk: task to test
|
|
|
|
* @scan: struct cgroup_scanner containing the cgroup of the task
|
|
|
|
*
|
|
|
|
* Called by cgroup_scan_tasks() for each task in a cgroup whose
|
|
|
|
* cpus_allowed mask needs to be changed.
|
|
|
|
*
|
|
|
|
* We don't need to re-check for the cgroup/cpuset membership, since we're
|
|
|
|
* holding cgroup_lock() at this point.
|
|
|
|
*/
|
2008-04-29 12:00:25 +04:00
|
|
|
static void cpuset_change_cpumask(struct task_struct *tsk,
|
|
|
|
struct cgroup_scanner *scan)
|
2008-02-07 11:14:44 +03:00
|
|
|
{
|
2008-04-05 05:11:07 +04:00
|
|
|
set_cpus_allowed_ptr(tsk, &((cgroup_cs(scan->cg))->cpus_allowed));
|
2008-02-07 11:14:44 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* update_cpumask - update the cpus_allowed mask of a cpuset and all tasks in it
|
|
|
|
* @cs: the cpuset to consider
|
|
|
|
* @buf: buffer of cpu numbers written to this cpuset
|
|
|
|
*/
|
2005-04-17 02:20:36 +04:00
|
|
|
static int update_cpumask(struct cpuset *cs, char *buf)
|
|
|
|
{
|
|
|
|
struct cpuset trialcs;
|
2008-02-07 11:14:44 +03:00
|
|
|
struct cgroup_scanner scan;
|
2007-10-19 10:40:22 +04:00
|
|
|
struct ptr_heap heap;
|
2008-02-07 11:14:44 +03:00
|
|
|
int retval;
|
|
|
|
int is_load_balanced;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
[PATCH] cpuset: top_cpuset tracks hotplug changes to cpu_online_map
Change the list of cpus allowed to tasks in the top (root) cpuset to
dynamically track what cpus are online, using a CPU hotplug notifier. Make
this top cpus file read-only.
On systems that have cpusets configured in their kernel, but that aren't
actively using cpusets (for some distros, this covers the majority of
systems) all tasks end up in the top cpuset.
If that system does support CPU hotplug, then these tasks cannot make use
of CPUs that are added after system boot, because the CPUs are not allowed
in the top cpuset. This is a surprising regression over earlier kernels
that didn't have cpusets enabled.
In order to keep the behaviour of cpusets consistent between systems
actively making use of them and systems not using them, this patch changes
the behaviour of the 'cpus' file in the top (root) cpuset, making it read
only, and making it automatically track the value of cpu_online_map. Thus
tasks in the top cpuset will have automatic use of hot plugged CPUs allowed
by their cpuset.
Thanks to Anton Blanchard and Nathan Lynch for reporting this problem,
driving the fix, and earlier versions of this patch.
Signed-off-by: Paul Jackson <pj@sgi.com>
Cc: Nathan Lynch <ntl@pobox.com>
Cc: Anton Blanchard <anton@samba.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-08-27 12:23:51 +04:00
|
|
|
/* top_cpuset.cpus_allowed tracks cpu_online_map; it's read-only */
|
|
|
|
if (cs == &top_cpuset)
|
|
|
|
return -EACCES;
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
trialcs = *cs;
|
2007-05-08 11:31:43 +04:00
|
|
|
|
|
|
|
/*
|
hotplug cpu: move tasks in empty cpusets to parent various other fixes
Various minor formatting and comment tweaks to Cliff Wickman's
[PATCH_3_of_3]_cpusets__update_cpumask_revision.patch
I had had "iff", meaning "if and only if" in a comment. However, except for
ancient mathematicians, the abbreviation "iff" was a tad too cryptic. Cliff
changed it to "if", presumably figuring that the "iff" was a typo. However,
it was the "only if" half of the conjunction that was most interesting.
Reword to emphasis the "only if" aspect.
The locking comment for remove_tasks_in_empty_cpuset() was wrong; it said
callback_mutex had to be held on entry. The opposite is true.
Several mentions of attach_task() in comments needed to be
changed to cgroup_attach_task().
A comment about notify_on_release was no longer relevant,
as the line of code it had commented, namely:
set_bit(CS_RELEASED_RESOURCE, &parent->flags);
is no longer present in that place in the cpuset.c code.
Similarly a comment about notify_on_release before the
scan_for_empty_cpusets() routine was no longer relevant.
Removed extra parentheses and unnecessary return statement.
Renamed attach_task() to cpuset_attach() in various comments.
Removed comment about not needing memory migration, as it seems the migration
is done anyway, via the cpuset_attach() callback from cgroup_attach_task().
Signed-off-by: Paul Jackson <pj@sgi.com>
Acked-by: Cliff Wickman <cpw@sgi.com>
Cc: David Rientjes <rientjes@google.com>
Cc: Paul Menage <menage@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2008-02-07 11:14:46 +03:00
|
|
|
* An empty cpus_allowed is ok only if the cpuset has no tasks.
|
2007-10-19 10:40:21 +04:00
|
|
|
* Since cpulist_parse() fails on an empty mask, we special case
|
|
|
|
* that parsing. The validate_change() call ensures that cpusets
|
|
|
|
* with tasks have cpus.
|
2007-05-08 11:31:43 +04:00
|
|
|
*/
|
2007-10-19 10:40:21 +04:00
|
|
|
buf = strstrip(buf);
|
|
|
|
if (!*buf) {
|
2007-05-08 11:31:43 +04:00
|
|
|
cpus_clear(trialcs.cpus_allowed);
|
|
|
|
} else {
|
|
|
|
retval = cpulist_parse(buf, trialcs.cpus_allowed);
|
|
|
|
if (retval < 0)
|
|
|
|
return retval;
|
2008-06-06 09:46:32 +04:00
|
|
|
|
|
|
|
if (!cpus_subset(trialcs.cpus_allowed, cpu_online_map))
|
|
|
|
return -EINVAL;
|
2007-05-08 11:31:43 +04:00
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
retval = validate_change(cs, &trialcs);
|
2005-06-26 01:57:34 +04:00
|
|
|
if (retval < 0)
|
|
|
|
return retval;
|
2007-10-19 10:40:20 +04:00
|
|
|
|
2007-10-19 10:40:22 +04:00
|
|
|
/* Nothing to do if the cpus didn't change */
|
|
|
|
if (cpus_equal(cs->cpus_allowed, trialcs.cpus_allowed))
|
|
|
|
return 0;
|
2008-02-07 11:14:44 +03:00
|
|
|
|
2007-10-19 10:40:22 +04:00
|
|
|
retval = heap_init(&heap, PAGE_SIZE, GFP_KERNEL, &started_after);
|
|
|
|
if (retval)
|
|
|
|
return retval;
|
|
|
|
|
2007-10-19 10:40:20 +04:00
|
|
|
is_load_balanced = is_sched_load_balance(&trialcs);
|
|
|
|
|
2006-03-23 14:00:18 +03:00
|
|
|
mutex_lock(&callback_mutex);
|
2005-06-26 01:57:34 +04:00
|
|
|
cs->cpus_allowed = trialcs.cpus_allowed;
|
2006-03-23 14:00:18 +03:00
|
|
|
mutex_unlock(&callback_mutex);
|
2007-10-19 10:40:20 +04:00
|
|
|
|
2007-10-19 10:40:22 +04:00
|
|
|
/*
|
|
|
|
* Scan tasks in the cpuset, and update the cpumasks of any
|
2008-02-07 11:14:44 +03:00
|
|
|
* that need an update.
|
2007-10-19 10:40:22 +04:00
|
|
|
*/
|
2008-02-07 11:14:44 +03:00
|
|
|
scan.cg = cs->css.cgroup;
|
|
|
|
scan.test_task = cpuset_test_cpumask;
|
|
|
|
scan.process_task = cpuset_change_cpumask;
|
|
|
|
scan.heap = &heap;
|
|
|
|
cgroup_scan_tasks(&scan);
|
2007-10-19 10:40:22 +04:00
|
|
|
heap_free(&heap);
|
2008-02-07 11:14:44 +03:00
|
|
|
|
2007-10-19 10:40:22 +04:00
|
|
|
if (is_load_balanced)
|
2007-10-19 10:40:20 +04:00
|
|
|
rebuild_sched_domains();
|
2005-06-26 01:57:34 +04:00
|
|
|
return 0;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
2006-03-31 14:30:52 +04:00
|
|
|
/*
|
|
|
|
* cpuset_migrate_mm
|
|
|
|
*
|
|
|
|
* Migrate memory region from one set of nodes to another.
|
|
|
|
*
|
|
|
|
* Temporarilly set tasks mems_allowed to target nodes of migration,
|
|
|
|
* so that the migration code can allocate pages on these nodes.
|
|
|
|
*
|
2008-02-07 11:14:45 +03:00
|
|
|
* Call holding cgroup_mutex, so current's cpuset won't change
|
hotplug cpu: move tasks in empty cpusets to parent various other fixes
Various minor formatting and comment tweaks to Cliff Wickman's
[PATCH_3_of_3]_cpusets__update_cpumask_revision.patch
I had had "iff", meaning "if and only if" in a comment. However, except for
ancient mathematicians, the abbreviation "iff" was a tad too cryptic. Cliff
changed it to "if", presumably figuring that the "iff" was a typo. However,
it was the "only if" half of the conjunction that was most interesting.
Reword to emphasis the "only if" aspect.
The locking comment for remove_tasks_in_empty_cpuset() was wrong; it said
callback_mutex had to be held on entry. The opposite is true.
Several mentions of attach_task() in comments needed to be
changed to cgroup_attach_task().
A comment about notify_on_release was no longer relevant,
as the line of code it had commented, namely:
set_bit(CS_RELEASED_RESOURCE, &parent->flags);
is no longer present in that place in the cpuset.c code.
Similarly a comment about notify_on_release before the
scan_for_empty_cpusets() routine was no longer relevant.
Removed extra parentheses and unnecessary return statement.
Renamed attach_task() to cpuset_attach() in various comments.
Removed comment about not needing memory migration, as it seems the migration
is done anyway, via the cpuset_attach() callback from cgroup_attach_task().
Signed-off-by: Paul Jackson <pj@sgi.com>
Acked-by: Cliff Wickman <cpw@sgi.com>
Cc: David Rientjes <rientjes@google.com>
Cc: Paul Menage <menage@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2008-02-07 11:14:46 +03:00
|
|
|
* during this call, as manage_mutex holds off any cpuset_attach()
|
2006-03-31 14:30:52 +04:00
|
|
|
* calls. Therefore we don't need to take task_lock around the
|
|
|
|
* call to guarantee_online_mems(), as we know no one is changing
|
2008-02-07 11:14:45 +03:00
|
|
|
* our task's cpuset.
|
2006-03-31 14:30:52 +04:00
|
|
|
*
|
|
|
|
* Hold callback_mutex around the two modifications of our tasks
|
|
|
|
* mems_allowed to synchronize with cpuset_mems_allowed().
|
|
|
|
*
|
|
|
|
* While the mm_struct we are migrating is typically from some
|
|
|
|
* other task, the task_struct mems_allowed that we are hacking
|
|
|
|
* is for our current task, which must allocate new pages for that
|
|
|
|
* migrating memory region.
|
|
|
|
*
|
|
|
|
* We call cpuset_update_task_memory_state() before hacking
|
|
|
|
* our tasks mems_allowed, so that we are assured of being in
|
|
|
|
* sync with our tasks cpuset, and in particular, callbacks to
|
|
|
|
* cpuset_update_task_memory_state() from nested page allocations
|
|
|
|
* won't see any mismatch of our cpuset and task mems_generation
|
|
|
|
* values, so won't overwrite our hacked tasks mems_allowed
|
|
|
|
* nodemask.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void cpuset_migrate_mm(struct mm_struct *mm, const nodemask_t *from,
|
|
|
|
const nodemask_t *to)
|
|
|
|
{
|
|
|
|
struct task_struct *tsk = current;
|
|
|
|
|
|
|
|
cpuset_update_task_memory_state();
|
|
|
|
|
|
|
|
mutex_lock(&callback_mutex);
|
|
|
|
tsk->mems_allowed = *to;
|
|
|
|
mutex_unlock(&callback_mutex);
|
|
|
|
|
|
|
|
do_migrate_pages(mm, from, to, MPOL_MF_MOVE_ALL);
|
|
|
|
|
|
|
|
mutex_lock(&callback_mutex);
|
2007-10-19 10:39:39 +04:00
|
|
|
guarantee_online_mems(task_cs(tsk),&tsk->mems_allowed);
|
2006-03-31 14:30:52 +04:00
|
|
|
mutex_unlock(&callback_mutex);
|
|
|
|
}
|
|
|
|
|
[PATCH] cpusets: dual semaphore locking overhaul
Overhaul cpuset locking. Replace single semaphore with two semaphores.
The suggestion to use two locks was made by Roman Zippel.
Both locks are global. Code that wants to modify cpusets must first
acquire the exclusive manage_sem, which allows them read-only access to
cpusets, and holds off other would-be modifiers. Before making actual
changes, the second semaphore, callback_sem must be acquired as well. Code
that needs only to query cpusets must acquire callback_sem, which is also a
global exclusive lock.
The earlier problems with double tripping are avoided, because it is
allowed for holders of manage_sem to nest the second callback_sem lock, and
only callback_sem is needed by code called from within __alloc_pages(),
where the double tripping had been possible.
This is not quite the same as a normal read/write semaphore, because
obtaining read-only access with intent to change must hold off other such
attempts, while allowing read-only access w/o such intention. Changing
cpusets involves several related checks and changes, which must be done
while allowing read-only queries (to avoid the double trip), but while
ensuring nothing changes (holding off other would be modifiers.)
This overhaul of cpuset locking also makes careful use of task_lock() to
guard access to the task->cpuset pointer, closing a couple of race
conditions noticed while reading this code (thanks, Roman). I've never
seen these races fail in any use or test.
See further the comments in the code.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-10-31 02:02:30 +03:00
|
|
|
/*
|
[PATCH] cpuset: rebind vma mempolicies fix
Fix more of longstanding bug in cpuset/mempolicy interaction.
NUMA mempolicies (mm/mempolicy.c) are constrained by the current tasks cpuset
to just the Memory Nodes allowed by that cpuset. The kernel maintains
internal state for each mempolicy, tracking what nodes are used for the
MPOL_INTERLEAVE, MPOL_BIND or MPOL_PREFERRED policies.
When a tasks cpuset memory placement changes, whether because the cpuset
changed, or because the task was attached to a different cpuset, then the
tasks mempolicies have to be rebound to the new cpuset placement, so as to
preserve the cpuset-relative numbering of the nodes in that policy.
An earlier fix handled such mempolicy rebinding for mempolicies attached to a
task.
This fix rebinds mempolicies attached to vma's (address ranges in a tasks
address space.) Due to the need to hold the task->mm->mmap_sem semaphore while
updating vma's, the rebinding of vma mempolicies has to be done when the
cpuset memory placement is changed, at which time mmap_sem can be safely
acquired. The tasks mempolicy is rebound later, when the task next attempts
to allocate memory and notices that its task->cpuset_mems_generation is
out-of-date with its cpusets mems_generation.
Because walking the tasklist to find all tasks attached to a changing cpuset
requires holding tasklist_lock, a spinlock, one cannot update the vma's of the
affected tasks while doing the tasklist scan. In general, one cannot acquire
a semaphore (which can sleep) while already holding a spinlock (such as
tasklist_lock). So a list of mm references has to be built up during the
tasklist scan, then the tasklist lock dropped, then for each mm, its mmap_sem
acquired, and the vma's in that mm rebound.
Once the tasklist lock is dropped, affected tasks may fork new tasks, before
their mm's are rebound. A kernel global 'cpuset_being_rebound' is set to
point to the cpuset being rebound (there can only be one; cpuset modifications
are done under a global 'manage_sem' semaphore), and the mpol_copy code that
is used to copy a tasks mempolicies during fork catches such forking tasks,
and ensures their children are also rebound.
When a task is moved to a different cpuset, it is easier, as there is only one
task involved. It's mm->vma's are scanned, using the same
mpol_rebind_policy() as used above.
It may happen that both the mpol_copy hook and the update done via the
tasklist scan update the same mm twice. This is ok, as the mempolicies of
each vma in an mm keep track of what mems_allowed they are relative to, and
safely no-op a second request to rebind to the same nodes.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 12:01:59 +03:00
|
|
|
* Handle user request to change the 'mems' memory placement
|
|
|
|
* of a cpuset. Needs to validate the request, update the
|
|
|
|
* cpusets mems_allowed and mems_generation, and for each
|
2006-01-08 12:02:00 +03:00
|
|
|
* task in the cpuset, rebind any vma mempolicies and if
|
|
|
|
* the cpuset is marked 'memory_migrate', migrate the tasks
|
|
|
|
* pages to the new memory.
|
[PATCH] cpuset: rebind vma mempolicies fix
Fix more of longstanding bug in cpuset/mempolicy interaction.
NUMA mempolicies (mm/mempolicy.c) are constrained by the current tasks cpuset
to just the Memory Nodes allowed by that cpuset. The kernel maintains
internal state for each mempolicy, tracking what nodes are used for the
MPOL_INTERLEAVE, MPOL_BIND or MPOL_PREFERRED policies.
When a tasks cpuset memory placement changes, whether because the cpuset
changed, or because the task was attached to a different cpuset, then the
tasks mempolicies have to be rebound to the new cpuset placement, so as to
preserve the cpuset-relative numbering of the nodes in that policy.
An earlier fix handled such mempolicy rebinding for mempolicies attached to a
task.
This fix rebinds mempolicies attached to vma's (address ranges in a tasks
address space.) Due to the need to hold the task->mm->mmap_sem semaphore while
updating vma's, the rebinding of vma mempolicies has to be done when the
cpuset memory placement is changed, at which time mmap_sem can be safely
acquired. The tasks mempolicy is rebound later, when the task next attempts
to allocate memory and notices that its task->cpuset_mems_generation is
out-of-date with its cpusets mems_generation.
Because walking the tasklist to find all tasks attached to a changing cpuset
requires holding tasklist_lock, a spinlock, one cannot update the vma's of the
affected tasks while doing the tasklist scan. In general, one cannot acquire
a semaphore (which can sleep) while already holding a spinlock (such as
tasklist_lock). So a list of mm references has to be built up during the
tasklist scan, then the tasklist lock dropped, then for each mm, its mmap_sem
acquired, and the vma's in that mm rebound.
Once the tasklist lock is dropped, affected tasks may fork new tasks, before
their mm's are rebound. A kernel global 'cpuset_being_rebound' is set to
point to the cpuset being rebound (there can only be one; cpuset modifications
are done under a global 'manage_sem' semaphore), and the mpol_copy code that
is used to copy a tasks mempolicies during fork catches such forking tasks,
and ensures their children are also rebound.
When a task is moved to a different cpuset, it is easier, as there is only one
task involved. It's mm->vma's are scanned, using the same
mpol_rebind_policy() as used above.
It may happen that both the mpol_copy hook and the update done via the
tasklist scan update the same mm twice. This is ok, as the mempolicies of
each vma in an mm keep track of what mems_allowed they are relative to, and
safely no-op a second request to rebind to the same nodes.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 12:01:59 +03:00
|
|
|
*
|
2008-02-07 11:14:45 +03:00
|
|
|
* Call with cgroup_mutex held. May take callback_mutex during call.
|
[PATCH] cpuset: rebind vma mempolicies fix
Fix more of longstanding bug in cpuset/mempolicy interaction.
NUMA mempolicies (mm/mempolicy.c) are constrained by the current tasks cpuset
to just the Memory Nodes allowed by that cpuset. The kernel maintains
internal state for each mempolicy, tracking what nodes are used for the
MPOL_INTERLEAVE, MPOL_BIND or MPOL_PREFERRED policies.
When a tasks cpuset memory placement changes, whether because the cpuset
changed, or because the task was attached to a different cpuset, then the
tasks mempolicies have to be rebound to the new cpuset placement, so as to
preserve the cpuset-relative numbering of the nodes in that policy.
An earlier fix handled such mempolicy rebinding for mempolicies attached to a
task.
This fix rebinds mempolicies attached to vma's (address ranges in a tasks
address space.) Due to the need to hold the task->mm->mmap_sem semaphore while
updating vma's, the rebinding of vma mempolicies has to be done when the
cpuset memory placement is changed, at which time mmap_sem can be safely
acquired. The tasks mempolicy is rebound later, when the task next attempts
to allocate memory and notices that its task->cpuset_mems_generation is
out-of-date with its cpusets mems_generation.
Because walking the tasklist to find all tasks attached to a changing cpuset
requires holding tasklist_lock, a spinlock, one cannot update the vma's of the
affected tasks while doing the tasklist scan. In general, one cannot acquire
a semaphore (which can sleep) while already holding a spinlock (such as
tasklist_lock). So a list of mm references has to be built up during the
tasklist scan, then the tasklist lock dropped, then for each mm, its mmap_sem
acquired, and the vma's in that mm rebound.
Once the tasklist lock is dropped, affected tasks may fork new tasks, before
their mm's are rebound. A kernel global 'cpuset_being_rebound' is set to
point to the cpuset being rebound (there can only be one; cpuset modifications
are done under a global 'manage_sem' semaphore), and the mpol_copy code that
is used to copy a tasks mempolicies during fork catches such forking tasks,
and ensures their children are also rebound.
When a task is moved to a different cpuset, it is easier, as there is only one
task involved. It's mm->vma's are scanned, using the same
mpol_rebind_policy() as used above.
It may happen that both the mpol_copy hook and the update done via the
tasklist scan update the same mm twice. This is ok, as the mempolicies of
each vma in an mm keep track of what mems_allowed they are relative to, and
safely no-op a second request to rebind to the same nodes.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 12:01:59 +03:00
|
|
|
* Will take tasklist_lock, scan tasklist for tasks in cpuset cs,
|
|
|
|
* lock each such tasks mm->mmap_sem, scan its vma's and rebind
|
|
|
|
* their mempolicies to the cpusets new mems_allowed.
|
[PATCH] cpusets: dual semaphore locking overhaul
Overhaul cpuset locking. Replace single semaphore with two semaphores.
The suggestion to use two locks was made by Roman Zippel.
Both locks are global. Code that wants to modify cpusets must first
acquire the exclusive manage_sem, which allows them read-only access to
cpusets, and holds off other would-be modifiers. Before making actual
changes, the second semaphore, callback_sem must be acquired as well. Code
that needs only to query cpusets must acquire callback_sem, which is also a
global exclusive lock.
The earlier problems with double tripping are avoided, because it is
allowed for holders of manage_sem to nest the second callback_sem lock, and
only callback_sem is needed by code called from within __alloc_pages(),
where the double tripping had been possible.
This is not quite the same as a normal read/write semaphore, because
obtaining read-only access with intent to change must hold off other such
attempts, while allowing read-only access w/o such intention. Changing
cpusets involves several related checks and changes, which must be done
while allowing read-only queries (to avoid the double trip), but while
ensuring nothing changes (holding off other would be modifiers.)
This overhaul of cpuset locking also makes careful use of task_lock() to
guard access to the task->cpuset pointer, closing a couple of race
conditions noticed while reading this code (thanks, Roman). I've never
seen these races fail in any use or test.
See further the comments in the code.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-10-31 02:02:30 +03:00
|
|
|
*/
|
|
|
|
|
2007-10-19 10:39:39 +04:00
|
|
|
static void *cpuset_being_rebound;
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
static int update_nodemask(struct cpuset *cs, char *buf)
|
|
|
|
{
|
|
|
|
struct cpuset trialcs;
|
2006-01-08 12:02:00 +03:00
|
|
|
nodemask_t oldmem;
|
2007-10-19 10:39:39 +04:00
|
|
|
struct task_struct *p;
|
[PATCH] cpuset: rebind vma mempolicies fix
Fix more of longstanding bug in cpuset/mempolicy interaction.
NUMA mempolicies (mm/mempolicy.c) are constrained by the current tasks cpuset
to just the Memory Nodes allowed by that cpuset. The kernel maintains
internal state for each mempolicy, tracking what nodes are used for the
MPOL_INTERLEAVE, MPOL_BIND or MPOL_PREFERRED policies.
When a tasks cpuset memory placement changes, whether because the cpuset
changed, or because the task was attached to a different cpuset, then the
tasks mempolicies have to be rebound to the new cpuset placement, so as to
preserve the cpuset-relative numbering of the nodes in that policy.
An earlier fix handled such mempolicy rebinding for mempolicies attached to a
task.
This fix rebinds mempolicies attached to vma's (address ranges in a tasks
address space.) Due to the need to hold the task->mm->mmap_sem semaphore while
updating vma's, the rebinding of vma mempolicies has to be done when the
cpuset memory placement is changed, at which time mmap_sem can be safely
acquired. The tasks mempolicy is rebound later, when the task next attempts
to allocate memory and notices that its task->cpuset_mems_generation is
out-of-date with its cpusets mems_generation.
Because walking the tasklist to find all tasks attached to a changing cpuset
requires holding tasklist_lock, a spinlock, one cannot update the vma's of the
affected tasks while doing the tasklist scan. In general, one cannot acquire
a semaphore (which can sleep) while already holding a spinlock (such as
tasklist_lock). So a list of mm references has to be built up during the
tasklist scan, then the tasklist lock dropped, then for each mm, its mmap_sem
acquired, and the vma's in that mm rebound.
Once the tasklist lock is dropped, affected tasks may fork new tasks, before
their mm's are rebound. A kernel global 'cpuset_being_rebound' is set to
point to the cpuset being rebound (there can only be one; cpuset modifications
are done under a global 'manage_sem' semaphore), and the mpol_copy code that
is used to copy a tasks mempolicies during fork catches such forking tasks,
and ensures their children are also rebound.
When a task is moved to a different cpuset, it is easier, as there is only one
task involved. It's mm->vma's are scanned, using the same
mpol_rebind_policy() as used above.
It may happen that both the mpol_copy hook and the update done via the
tasklist scan update the same mm twice. This is ok, as the mempolicies of
each vma in an mm keep track of what mems_allowed they are relative to, and
safely no-op a second request to rebind to the same nodes.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 12:01:59 +03:00
|
|
|
struct mm_struct **mmarray;
|
|
|
|
int i, n, ntasks;
|
2006-01-08 12:02:00 +03:00
|
|
|
int migrate;
|
[PATCH] cpuset: rebind vma mempolicies fix
Fix more of longstanding bug in cpuset/mempolicy interaction.
NUMA mempolicies (mm/mempolicy.c) are constrained by the current tasks cpuset
to just the Memory Nodes allowed by that cpuset. The kernel maintains
internal state for each mempolicy, tracking what nodes are used for the
MPOL_INTERLEAVE, MPOL_BIND or MPOL_PREFERRED policies.
When a tasks cpuset memory placement changes, whether because the cpuset
changed, or because the task was attached to a different cpuset, then the
tasks mempolicies have to be rebound to the new cpuset placement, so as to
preserve the cpuset-relative numbering of the nodes in that policy.
An earlier fix handled such mempolicy rebinding for mempolicies attached to a
task.
This fix rebinds mempolicies attached to vma's (address ranges in a tasks
address space.) Due to the need to hold the task->mm->mmap_sem semaphore while
updating vma's, the rebinding of vma mempolicies has to be done when the
cpuset memory placement is changed, at which time mmap_sem can be safely
acquired. The tasks mempolicy is rebound later, when the task next attempts
to allocate memory and notices that its task->cpuset_mems_generation is
out-of-date with its cpusets mems_generation.
Because walking the tasklist to find all tasks attached to a changing cpuset
requires holding tasklist_lock, a spinlock, one cannot update the vma's of the
affected tasks while doing the tasklist scan. In general, one cannot acquire
a semaphore (which can sleep) while already holding a spinlock (such as
tasklist_lock). So a list of mm references has to be built up during the
tasklist scan, then the tasklist lock dropped, then for each mm, its mmap_sem
acquired, and the vma's in that mm rebound.
Once the tasklist lock is dropped, affected tasks may fork new tasks, before
their mm's are rebound. A kernel global 'cpuset_being_rebound' is set to
point to the cpuset being rebound (there can only be one; cpuset modifications
are done under a global 'manage_sem' semaphore), and the mpol_copy code that
is used to copy a tasks mempolicies during fork catches such forking tasks,
and ensures their children are also rebound.
When a task is moved to a different cpuset, it is easier, as there is only one
task involved. It's mm->vma's are scanned, using the same
mpol_rebind_policy() as used above.
It may happen that both the mpol_copy hook and the update done via the
tasklist scan update the same mm twice. This is ok, as the mempolicies of
each vma in an mm keep track of what mems_allowed they are relative to, and
safely no-op a second request to rebind to the same nodes.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 12:01:59 +03:00
|
|
|
int fudge;
|
2005-04-17 02:20:36 +04:00
|
|
|
int retval;
|
2007-10-19 10:39:39 +04:00
|
|
|
struct cgroup_iter it;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2007-10-16 12:25:38 +04:00
|
|
|
/*
|
|
|
|
* top_cpuset.mems_allowed tracks node_stats[N_HIGH_MEMORY];
|
|
|
|
* it's read-only
|
|
|
|
*/
|
[PATCH] cpuset: top_cpuset tracks hotplug changes to node_online_map
Change the list of memory nodes allowed to tasks in the top (root) nodeset
to dynamically track what cpus are online, using a call to a cpuset hook
from the memory hotplug code. Make this top cpus file read-only.
On systems that have cpusets configured in their kernel, but that aren't
actively using cpusets (for some distros, this covers the majority of
systems) all tasks end up in the top cpuset.
If that system does support memory hotplug, then these tasks cannot make
use of memory nodes that are added after system boot, because the memory
nodes are not allowed in the top cpuset. This is a surprising regression
over earlier kernels that didn't have cpusets enabled.
One key motivation for this change is to remain consistent with the
behaviour for the top_cpuset's 'cpus', which is also read-only, and which
automatically tracks the cpu_online_map.
This change also has the minor benefit that it fixes a long standing,
little noticed, minor bug in cpusets. The cpuset performance tweak to
short circuit the cpuset_zone_allowed() check on systems with just a single
cpuset (see 'number_of_cpusets', in linux/cpuset.h) meant that simply
changing the 'mems' of the top_cpuset had no affect, even though the change
(the write system call) appeared to succeed. With the following change,
that write to the 'mems' file fails -EACCES, and the 'mems' file stubbornly
refuses to be changed via user space writes. Thus no one should be mislead
into thinking they've changed the top_cpusets's 'mems' when in affect they
haven't.
In order to keep the behaviour of cpusets consistent between systems
actively making use of them and systems not using them, this patch changes
the behaviour of the 'mems' file in the top (root) cpuset, making it read
only, and making it automatically track the value of node_online_map. Thus
tasks in the top cpuset will have automatic use of hot plugged memory nodes
allowed by their cpuset.
[akpm@osdl.org: build fix]
[bunk@stusta.de: build fix]
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Adrian Bunk <bunk@stusta.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-09-29 13:01:16 +04:00
|
|
|
if (cs == &top_cpuset)
|
|
|
|
return -EACCES;
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
trialcs = *cs;
|
2007-05-08 11:31:43 +04:00
|
|
|
|
|
|
|
/*
|
2007-10-19 10:40:21 +04:00
|
|
|
* An empty mems_allowed is ok iff there are no tasks in the cpuset.
|
|
|
|
* Since nodelist_parse() fails on an empty mask, we special case
|
|
|
|
* that parsing. The validate_change() call ensures that cpusets
|
|
|
|
* with tasks have memory.
|
2007-05-08 11:31:43 +04:00
|
|
|
*/
|
2007-10-19 10:40:21 +04:00
|
|
|
buf = strstrip(buf);
|
|
|
|
if (!*buf) {
|
2007-05-08 11:31:43 +04:00
|
|
|
nodes_clear(trialcs.mems_allowed);
|
|
|
|
} else {
|
|
|
|
retval = nodelist_parse(buf, trialcs.mems_allowed);
|
|
|
|
if (retval < 0)
|
|
|
|
goto done;
|
2008-06-06 09:46:32 +04:00
|
|
|
|
|
|
|
if (!nodes_subset(trialcs.mems_allowed,
|
|
|
|
node_states[N_HIGH_MEMORY]))
|
|
|
|
return -EINVAL;
|
2007-05-08 11:31:43 +04:00
|
|
|
}
|
2006-01-08 12:02:00 +03:00
|
|
|
oldmem = cs->mems_allowed;
|
|
|
|
if (nodes_equal(oldmem, trialcs.mems_allowed)) {
|
|
|
|
retval = 0; /* Too easy - nothing to do */
|
|
|
|
goto done;
|
|
|
|
}
|
2006-01-08 12:01:52 +03:00
|
|
|
retval = validate_change(cs, &trialcs);
|
|
|
|
if (retval < 0)
|
|
|
|
goto done;
|
|
|
|
|
2006-03-23 14:00:18 +03:00
|
|
|
mutex_lock(&callback_mutex);
|
2006-01-08 12:01:52 +03:00
|
|
|
cs->mems_allowed = trialcs.mems_allowed;
|
2006-03-24 14:16:11 +03:00
|
|
|
cs->mems_generation = cpuset_mems_generation++;
|
2006-03-23 14:00:18 +03:00
|
|
|
mutex_unlock(&callback_mutex);
|
2006-01-08 12:01:52 +03:00
|
|
|
|
2008-04-28 13:13:09 +04:00
|
|
|
cpuset_being_rebound = cs; /* causes mpol_dup() rebind */
|
[PATCH] cpuset: rebind vma mempolicies fix
Fix more of longstanding bug in cpuset/mempolicy interaction.
NUMA mempolicies (mm/mempolicy.c) are constrained by the current tasks cpuset
to just the Memory Nodes allowed by that cpuset. The kernel maintains
internal state for each mempolicy, tracking what nodes are used for the
MPOL_INTERLEAVE, MPOL_BIND or MPOL_PREFERRED policies.
When a tasks cpuset memory placement changes, whether because the cpuset
changed, or because the task was attached to a different cpuset, then the
tasks mempolicies have to be rebound to the new cpuset placement, so as to
preserve the cpuset-relative numbering of the nodes in that policy.
An earlier fix handled such mempolicy rebinding for mempolicies attached to a
task.
This fix rebinds mempolicies attached to vma's (address ranges in a tasks
address space.) Due to the need to hold the task->mm->mmap_sem semaphore while
updating vma's, the rebinding of vma mempolicies has to be done when the
cpuset memory placement is changed, at which time mmap_sem can be safely
acquired. The tasks mempolicy is rebound later, when the task next attempts
to allocate memory and notices that its task->cpuset_mems_generation is
out-of-date with its cpusets mems_generation.
Because walking the tasklist to find all tasks attached to a changing cpuset
requires holding tasklist_lock, a spinlock, one cannot update the vma's of the
affected tasks while doing the tasklist scan. In general, one cannot acquire
a semaphore (which can sleep) while already holding a spinlock (such as
tasklist_lock). So a list of mm references has to be built up during the
tasklist scan, then the tasklist lock dropped, then for each mm, its mmap_sem
acquired, and the vma's in that mm rebound.
Once the tasklist lock is dropped, affected tasks may fork new tasks, before
their mm's are rebound. A kernel global 'cpuset_being_rebound' is set to
point to the cpuset being rebound (there can only be one; cpuset modifications
are done under a global 'manage_sem' semaphore), and the mpol_copy code that
is used to copy a tasks mempolicies during fork catches such forking tasks,
and ensures their children are also rebound.
When a task is moved to a different cpuset, it is easier, as there is only one
task involved. It's mm->vma's are scanned, using the same
mpol_rebind_policy() as used above.
It may happen that both the mpol_copy hook and the update done via the
tasklist scan update the same mm twice. This is ok, as the mempolicies of
each vma in an mm keep track of what mems_allowed they are relative to, and
safely no-op a second request to rebind to the same nodes.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 12:01:59 +03:00
|
|
|
|
|
|
|
fudge = 10; /* spare mmarray[] slots */
|
|
|
|
fudge += cpus_weight(cs->cpus_allowed); /* imagine one fork-bomb/cpu */
|
|
|
|
retval = -ENOMEM;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Allocate mmarray[] to hold mm reference for each task
|
|
|
|
* in cpuset cs. Can't kmalloc GFP_KERNEL while holding
|
|
|
|
* tasklist_lock. We could use GFP_ATOMIC, but with a
|
|
|
|
* few more lines of code, we can retry until we get a big
|
|
|
|
* enough mmarray[] w/o using GFP_ATOMIC.
|
|
|
|
*/
|
|
|
|
while (1) {
|
2007-10-19 10:39:39 +04:00
|
|
|
ntasks = cgroup_task_count(cs->css.cgroup); /* guess */
|
[PATCH] cpuset: rebind vma mempolicies fix
Fix more of longstanding bug in cpuset/mempolicy interaction.
NUMA mempolicies (mm/mempolicy.c) are constrained by the current tasks cpuset
to just the Memory Nodes allowed by that cpuset. The kernel maintains
internal state for each mempolicy, tracking what nodes are used for the
MPOL_INTERLEAVE, MPOL_BIND or MPOL_PREFERRED policies.
When a tasks cpuset memory placement changes, whether because the cpuset
changed, or because the task was attached to a different cpuset, then the
tasks mempolicies have to be rebound to the new cpuset placement, so as to
preserve the cpuset-relative numbering of the nodes in that policy.
An earlier fix handled such mempolicy rebinding for mempolicies attached to a
task.
This fix rebinds mempolicies attached to vma's (address ranges in a tasks
address space.) Due to the need to hold the task->mm->mmap_sem semaphore while
updating vma's, the rebinding of vma mempolicies has to be done when the
cpuset memory placement is changed, at which time mmap_sem can be safely
acquired. The tasks mempolicy is rebound later, when the task next attempts
to allocate memory and notices that its task->cpuset_mems_generation is
out-of-date with its cpusets mems_generation.
Because walking the tasklist to find all tasks attached to a changing cpuset
requires holding tasklist_lock, a spinlock, one cannot update the vma's of the
affected tasks while doing the tasklist scan. In general, one cannot acquire
a semaphore (which can sleep) while already holding a spinlock (such as
tasklist_lock). So a list of mm references has to be built up during the
tasklist scan, then the tasklist lock dropped, then for each mm, its mmap_sem
acquired, and the vma's in that mm rebound.
Once the tasklist lock is dropped, affected tasks may fork new tasks, before
their mm's are rebound. A kernel global 'cpuset_being_rebound' is set to
point to the cpuset being rebound (there can only be one; cpuset modifications
are done under a global 'manage_sem' semaphore), and the mpol_copy code that
is used to copy a tasks mempolicies during fork catches such forking tasks,
and ensures their children are also rebound.
When a task is moved to a different cpuset, it is easier, as there is only one
task involved. It's mm->vma's are scanned, using the same
mpol_rebind_policy() as used above.
It may happen that both the mpol_copy hook and the update done via the
tasklist scan update the same mm twice. This is ok, as the mempolicies of
each vma in an mm keep track of what mems_allowed they are relative to, and
safely no-op a second request to rebind to the same nodes.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 12:01:59 +03:00
|
|
|
ntasks += fudge;
|
|
|
|
mmarray = kmalloc(ntasks * sizeof(*mmarray), GFP_KERNEL);
|
|
|
|
if (!mmarray)
|
|
|
|
goto done;
|
2007-07-16 10:40:11 +04:00
|
|
|
read_lock(&tasklist_lock); /* block fork */
|
2007-10-19 10:39:39 +04:00
|
|
|
if (cgroup_task_count(cs->css.cgroup) <= ntasks)
|
[PATCH] cpuset: rebind vma mempolicies fix
Fix more of longstanding bug in cpuset/mempolicy interaction.
NUMA mempolicies (mm/mempolicy.c) are constrained by the current tasks cpuset
to just the Memory Nodes allowed by that cpuset. The kernel maintains
internal state for each mempolicy, tracking what nodes are used for the
MPOL_INTERLEAVE, MPOL_BIND or MPOL_PREFERRED policies.
When a tasks cpuset memory placement changes, whether because the cpuset
changed, or because the task was attached to a different cpuset, then the
tasks mempolicies have to be rebound to the new cpuset placement, so as to
preserve the cpuset-relative numbering of the nodes in that policy.
An earlier fix handled such mempolicy rebinding for mempolicies attached to a
task.
This fix rebinds mempolicies attached to vma's (address ranges in a tasks
address space.) Due to the need to hold the task->mm->mmap_sem semaphore while
updating vma's, the rebinding of vma mempolicies has to be done when the
cpuset memory placement is changed, at which time mmap_sem can be safely
acquired. The tasks mempolicy is rebound later, when the task next attempts
to allocate memory and notices that its task->cpuset_mems_generation is
out-of-date with its cpusets mems_generation.
Because walking the tasklist to find all tasks attached to a changing cpuset
requires holding tasklist_lock, a spinlock, one cannot update the vma's of the
affected tasks while doing the tasklist scan. In general, one cannot acquire
a semaphore (which can sleep) while already holding a spinlock (such as
tasklist_lock). So a list of mm references has to be built up during the
tasklist scan, then the tasklist lock dropped, then for each mm, its mmap_sem
acquired, and the vma's in that mm rebound.
Once the tasklist lock is dropped, affected tasks may fork new tasks, before
their mm's are rebound. A kernel global 'cpuset_being_rebound' is set to
point to the cpuset being rebound (there can only be one; cpuset modifications
are done under a global 'manage_sem' semaphore), and the mpol_copy code that
is used to copy a tasks mempolicies during fork catches such forking tasks,
and ensures their children are also rebound.
When a task is moved to a different cpuset, it is easier, as there is only one
task involved. It's mm->vma's are scanned, using the same
mpol_rebind_policy() as used above.
It may happen that both the mpol_copy hook and the update done via the
tasklist scan update the same mm twice. This is ok, as the mempolicies of
each vma in an mm keep track of what mems_allowed they are relative to, and
safely no-op a second request to rebind to the same nodes.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 12:01:59 +03:00
|
|
|
break; /* got enough */
|
2007-07-16 10:40:11 +04:00
|
|
|
read_unlock(&tasklist_lock); /* try again */
|
[PATCH] cpuset: rebind vma mempolicies fix
Fix more of longstanding bug in cpuset/mempolicy interaction.
NUMA mempolicies (mm/mempolicy.c) are constrained by the current tasks cpuset
to just the Memory Nodes allowed by that cpuset. The kernel maintains
internal state for each mempolicy, tracking what nodes are used for the
MPOL_INTERLEAVE, MPOL_BIND or MPOL_PREFERRED policies.
When a tasks cpuset memory placement changes, whether because the cpuset
changed, or because the task was attached to a different cpuset, then the
tasks mempolicies have to be rebound to the new cpuset placement, so as to
preserve the cpuset-relative numbering of the nodes in that policy.
An earlier fix handled such mempolicy rebinding for mempolicies attached to a
task.
This fix rebinds mempolicies attached to vma's (address ranges in a tasks
address space.) Due to the need to hold the task->mm->mmap_sem semaphore while
updating vma's, the rebinding of vma mempolicies has to be done when the
cpuset memory placement is changed, at which time mmap_sem can be safely
acquired. The tasks mempolicy is rebound later, when the task next attempts
to allocate memory and notices that its task->cpuset_mems_generation is
out-of-date with its cpusets mems_generation.
Because walking the tasklist to find all tasks attached to a changing cpuset
requires holding tasklist_lock, a spinlock, one cannot update the vma's of the
affected tasks while doing the tasklist scan. In general, one cannot acquire
a semaphore (which can sleep) while already holding a spinlock (such as
tasklist_lock). So a list of mm references has to be built up during the
tasklist scan, then the tasklist lock dropped, then for each mm, its mmap_sem
acquired, and the vma's in that mm rebound.
Once the tasklist lock is dropped, affected tasks may fork new tasks, before
their mm's are rebound. A kernel global 'cpuset_being_rebound' is set to
point to the cpuset being rebound (there can only be one; cpuset modifications
are done under a global 'manage_sem' semaphore), and the mpol_copy code that
is used to copy a tasks mempolicies during fork catches such forking tasks,
and ensures their children are also rebound.
When a task is moved to a different cpuset, it is easier, as there is only one
task involved. It's mm->vma's are scanned, using the same
mpol_rebind_policy() as used above.
It may happen that both the mpol_copy hook and the update done via the
tasklist scan update the same mm twice. This is ok, as the mempolicies of
each vma in an mm keep track of what mems_allowed they are relative to, and
safely no-op a second request to rebind to the same nodes.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 12:01:59 +03:00
|
|
|
kfree(mmarray);
|
|
|
|
}
|
|
|
|
|
|
|
|
n = 0;
|
|
|
|
|
|
|
|
/* Load up mmarray[] with mm reference for each task in cpuset. */
|
2007-10-19 10:39:39 +04:00
|
|
|
cgroup_iter_start(cs->css.cgroup, &it);
|
|
|
|
while ((p = cgroup_iter_next(cs->css.cgroup, &it))) {
|
[PATCH] cpuset: rebind vma mempolicies fix
Fix more of longstanding bug in cpuset/mempolicy interaction.
NUMA mempolicies (mm/mempolicy.c) are constrained by the current tasks cpuset
to just the Memory Nodes allowed by that cpuset. The kernel maintains
internal state for each mempolicy, tracking what nodes are used for the
MPOL_INTERLEAVE, MPOL_BIND or MPOL_PREFERRED policies.
When a tasks cpuset memory placement changes, whether because the cpuset
changed, or because the task was attached to a different cpuset, then the
tasks mempolicies have to be rebound to the new cpuset placement, so as to
preserve the cpuset-relative numbering of the nodes in that policy.
An earlier fix handled such mempolicy rebinding for mempolicies attached to a
task.
This fix rebinds mempolicies attached to vma's (address ranges in a tasks
address space.) Due to the need to hold the task->mm->mmap_sem semaphore while
updating vma's, the rebinding of vma mempolicies has to be done when the
cpuset memory placement is changed, at which time mmap_sem can be safely
acquired. The tasks mempolicy is rebound later, when the task next attempts
to allocate memory and notices that its task->cpuset_mems_generation is
out-of-date with its cpusets mems_generation.
Because walking the tasklist to find all tasks attached to a changing cpuset
requires holding tasklist_lock, a spinlock, one cannot update the vma's of the
affected tasks while doing the tasklist scan. In general, one cannot acquire
a semaphore (which can sleep) while already holding a spinlock (such as
tasklist_lock). So a list of mm references has to be built up during the
tasklist scan, then the tasklist lock dropped, then for each mm, its mmap_sem
acquired, and the vma's in that mm rebound.
Once the tasklist lock is dropped, affected tasks may fork new tasks, before
their mm's are rebound. A kernel global 'cpuset_being_rebound' is set to
point to the cpuset being rebound (there can only be one; cpuset modifications
are done under a global 'manage_sem' semaphore), and the mpol_copy code that
is used to copy a tasks mempolicies during fork catches such forking tasks,
and ensures their children are also rebound.
When a task is moved to a different cpuset, it is easier, as there is only one
task involved. It's mm->vma's are scanned, using the same
mpol_rebind_policy() as used above.
It may happen that both the mpol_copy hook and the update done via the
tasklist scan update the same mm twice. This is ok, as the mempolicies of
each vma in an mm keep track of what mems_allowed they are relative to, and
safely no-op a second request to rebind to the same nodes.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 12:01:59 +03:00
|
|
|
struct mm_struct *mm;
|
|
|
|
|
|
|
|
if (n >= ntasks) {
|
|
|
|
printk(KERN_WARNING
|
|
|
|
"Cpuset mempolicy rebind incomplete.\n");
|
2007-10-19 10:39:39 +04:00
|
|
|
break;
|
[PATCH] cpuset: rebind vma mempolicies fix
Fix more of longstanding bug in cpuset/mempolicy interaction.
NUMA mempolicies (mm/mempolicy.c) are constrained by the current tasks cpuset
to just the Memory Nodes allowed by that cpuset. The kernel maintains
internal state for each mempolicy, tracking what nodes are used for the
MPOL_INTERLEAVE, MPOL_BIND or MPOL_PREFERRED policies.
When a tasks cpuset memory placement changes, whether because the cpuset
changed, or because the task was attached to a different cpuset, then the
tasks mempolicies have to be rebound to the new cpuset placement, so as to
preserve the cpuset-relative numbering of the nodes in that policy.
An earlier fix handled such mempolicy rebinding for mempolicies attached to a
task.
This fix rebinds mempolicies attached to vma's (address ranges in a tasks
address space.) Due to the need to hold the task->mm->mmap_sem semaphore while
updating vma's, the rebinding of vma mempolicies has to be done when the
cpuset memory placement is changed, at which time mmap_sem can be safely
acquired. The tasks mempolicy is rebound later, when the task next attempts
to allocate memory and notices that its task->cpuset_mems_generation is
out-of-date with its cpusets mems_generation.
Because walking the tasklist to find all tasks attached to a changing cpuset
requires holding tasklist_lock, a spinlock, one cannot update the vma's of the
affected tasks while doing the tasklist scan. In general, one cannot acquire
a semaphore (which can sleep) while already holding a spinlock (such as
tasklist_lock). So a list of mm references has to be built up during the
tasklist scan, then the tasklist lock dropped, then for each mm, its mmap_sem
acquired, and the vma's in that mm rebound.
Once the tasklist lock is dropped, affected tasks may fork new tasks, before
their mm's are rebound. A kernel global 'cpuset_being_rebound' is set to
point to the cpuset being rebound (there can only be one; cpuset modifications
are done under a global 'manage_sem' semaphore), and the mpol_copy code that
is used to copy a tasks mempolicies during fork catches such forking tasks,
and ensures their children are also rebound.
When a task is moved to a different cpuset, it is easier, as there is only one
task involved. It's mm->vma's are scanned, using the same
mpol_rebind_policy() as used above.
It may happen that both the mpol_copy hook and the update done via the
tasklist scan update the same mm twice. This is ok, as the mempolicies of
each vma in an mm keep track of what mems_allowed they are relative to, and
safely no-op a second request to rebind to the same nodes.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 12:01:59 +03:00
|
|
|
}
|
|
|
|
mm = get_task_mm(p);
|
|
|
|
if (!mm)
|
|
|
|
continue;
|
|
|
|
mmarray[n++] = mm;
|
2007-10-19 10:39:39 +04:00
|
|
|
}
|
|
|
|
cgroup_iter_end(cs->css.cgroup, &it);
|
2007-07-16 10:40:11 +04:00
|
|
|
read_unlock(&tasklist_lock);
|
[PATCH] cpuset: rebind vma mempolicies fix
Fix more of longstanding bug in cpuset/mempolicy interaction.
NUMA mempolicies (mm/mempolicy.c) are constrained by the current tasks cpuset
to just the Memory Nodes allowed by that cpuset. The kernel maintains
internal state for each mempolicy, tracking what nodes are used for the
MPOL_INTERLEAVE, MPOL_BIND or MPOL_PREFERRED policies.
When a tasks cpuset memory placement changes, whether because the cpuset
changed, or because the task was attached to a different cpuset, then the
tasks mempolicies have to be rebound to the new cpuset placement, so as to
preserve the cpuset-relative numbering of the nodes in that policy.
An earlier fix handled such mempolicy rebinding for mempolicies attached to a
task.
This fix rebinds mempolicies attached to vma's (address ranges in a tasks
address space.) Due to the need to hold the task->mm->mmap_sem semaphore while
updating vma's, the rebinding of vma mempolicies has to be done when the
cpuset memory placement is changed, at which time mmap_sem can be safely
acquired. The tasks mempolicy is rebound later, when the task next attempts
to allocate memory and notices that its task->cpuset_mems_generation is
out-of-date with its cpusets mems_generation.
Because walking the tasklist to find all tasks attached to a changing cpuset
requires holding tasklist_lock, a spinlock, one cannot update the vma's of the
affected tasks while doing the tasklist scan. In general, one cannot acquire
a semaphore (which can sleep) while already holding a spinlock (such as
tasklist_lock). So a list of mm references has to be built up during the
tasklist scan, then the tasklist lock dropped, then for each mm, its mmap_sem
acquired, and the vma's in that mm rebound.
Once the tasklist lock is dropped, affected tasks may fork new tasks, before
their mm's are rebound. A kernel global 'cpuset_being_rebound' is set to
point to the cpuset being rebound (there can only be one; cpuset modifications
are done under a global 'manage_sem' semaphore), and the mpol_copy code that
is used to copy a tasks mempolicies during fork catches such forking tasks,
and ensures their children are also rebound.
When a task is moved to a different cpuset, it is easier, as there is only one
task involved. It's mm->vma's are scanned, using the same
mpol_rebind_policy() as used above.
It may happen that both the mpol_copy hook and the update done via the
tasklist scan update the same mm twice. This is ok, as the mempolicies of
each vma in an mm keep track of what mems_allowed they are relative to, and
safely no-op a second request to rebind to the same nodes.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 12:01:59 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Now that we've dropped the tasklist spinlock, we can
|
|
|
|
* rebind the vma mempolicies of each mm in mmarray[] to their
|
|
|
|
* new cpuset, and release that mm. The mpol_rebind_mm()
|
|
|
|
* call takes mmap_sem, which we couldn't take while holding
|
2008-04-28 13:13:09 +04:00
|
|
|
* tasklist_lock. Forks can happen again now - the mpol_dup()
|
[PATCH] cpuset: rebind vma mempolicies fix
Fix more of longstanding bug in cpuset/mempolicy interaction.
NUMA mempolicies (mm/mempolicy.c) are constrained by the current tasks cpuset
to just the Memory Nodes allowed by that cpuset. The kernel maintains
internal state for each mempolicy, tracking what nodes are used for the
MPOL_INTERLEAVE, MPOL_BIND or MPOL_PREFERRED policies.
When a tasks cpuset memory placement changes, whether because the cpuset
changed, or because the task was attached to a different cpuset, then the
tasks mempolicies have to be rebound to the new cpuset placement, so as to
preserve the cpuset-relative numbering of the nodes in that policy.
An earlier fix handled such mempolicy rebinding for mempolicies attached to a
task.
This fix rebinds mempolicies attached to vma's (address ranges in a tasks
address space.) Due to the need to hold the task->mm->mmap_sem semaphore while
updating vma's, the rebinding of vma mempolicies has to be done when the
cpuset memory placement is changed, at which time mmap_sem can be safely
acquired. The tasks mempolicy is rebound later, when the task next attempts
to allocate memory and notices that its task->cpuset_mems_generation is
out-of-date with its cpusets mems_generation.
Because walking the tasklist to find all tasks attached to a changing cpuset
requires holding tasklist_lock, a spinlock, one cannot update the vma's of the
affected tasks while doing the tasklist scan. In general, one cannot acquire
a semaphore (which can sleep) while already holding a spinlock (such as
tasklist_lock). So a list of mm references has to be built up during the
tasklist scan, then the tasklist lock dropped, then for each mm, its mmap_sem
acquired, and the vma's in that mm rebound.
Once the tasklist lock is dropped, affected tasks may fork new tasks, before
their mm's are rebound. A kernel global 'cpuset_being_rebound' is set to
point to the cpuset being rebound (there can only be one; cpuset modifications
are done under a global 'manage_sem' semaphore), and the mpol_copy code that
is used to copy a tasks mempolicies during fork catches such forking tasks,
and ensures their children are also rebound.
When a task is moved to a different cpuset, it is easier, as there is only one
task involved. It's mm->vma's are scanned, using the same
mpol_rebind_policy() as used above.
It may happen that both the mpol_copy hook and the update done via the
tasklist scan update the same mm twice. This is ok, as the mempolicies of
each vma in an mm keep track of what mems_allowed they are relative to, and
safely no-op a second request to rebind to the same nodes.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 12:01:59 +03:00
|
|
|
* cpuset_being_rebound check will catch such forks, and rebind
|
|
|
|
* their vma mempolicies too. Because we still hold the global
|
2008-02-07 11:14:45 +03:00
|
|
|
* cgroup_mutex, we know that no other rebind effort will
|
[PATCH] cpuset: rebind vma mempolicies fix
Fix more of longstanding bug in cpuset/mempolicy interaction.
NUMA mempolicies (mm/mempolicy.c) are constrained by the current tasks cpuset
to just the Memory Nodes allowed by that cpuset. The kernel maintains
internal state for each mempolicy, tracking what nodes are used for the
MPOL_INTERLEAVE, MPOL_BIND or MPOL_PREFERRED policies.
When a tasks cpuset memory placement changes, whether because the cpuset
changed, or because the task was attached to a different cpuset, then the
tasks mempolicies have to be rebound to the new cpuset placement, so as to
preserve the cpuset-relative numbering of the nodes in that policy.
An earlier fix handled such mempolicy rebinding for mempolicies attached to a
task.
This fix rebinds mempolicies attached to vma's (address ranges in a tasks
address space.) Due to the need to hold the task->mm->mmap_sem semaphore while
updating vma's, the rebinding of vma mempolicies has to be done when the
cpuset memory placement is changed, at which time mmap_sem can be safely
acquired. The tasks mempolicy is rebound later, when the task next attempts
to allocate memory and notices that its task->cpuset_mems_generation is
out-of-date with its cpusets mems_generation.
Because walking the tasklist to find all tasks attached to a changing cpuset
requires holding tasklist_lock, a spinlock, one cannot update the vma's of the
affected tasks while doing the tasklist scan. In general, one cannot acquire
a semaphore (which can sleep) while already holding a spinlock (such as
tasklist_lock). So a list of mm references has to be built up during the
tasklist scan, then the tasklist lock dropped, then for each mm, its mmap_sem
acquired, and the vma's in that mm rebound.
Once the tasklist lock is dropped, affected tasks may fork new tasks, before
their mm's are rebound. A kernel global 'cpuset_being_rebound' is set to
point to the cpuset being rebound (there can only be one; cpuset modifications
are done under a global 'manage_sem' semaphore), and the mpol_copy code that
is used to copy a tasks mempolicies during fork catches such forking tasks,
and ensures their children are also rebound.
When a task is moved to a different cpuset, it is easier, as there is only one
task involved. It's mm->vma's are scanned, using the same
mpol_rebind_policy() as used above.
It may happen that both the mpol_copy hook and the update done via the
tasklist scan update the same mm twice. This is ok, as the mempolicies of
each vma in an mm keep track of what mems_allowed they are relative to, and
safely no-op a second request to rebind to the same nodes.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 12:01:59 +03:00
|
|
|
* be contending for the global variable cpuset_being_rebound.
|
|
|
|
* It's ok if we rebind the same mm twice; mpol_rebind_mm()
|
2006-01-08 12:02:00 +03:00
|
|
|
* is idempotent. Also migrate pages in each mm to new nodes.
|
[PATCH] cpuset: rebind vma mempolicies fix
Fix more of longstanding bug in cpuset/mempolicy interaction.
NUMA mempolicies (mm/mempolicy.c) are constrained by the current tasks cpuset
to just the Memory Nodes allowed by that cpuset. The kernel maintains
internal state for each mempolicy, tracking what nodes are used for the
MPOL_INTERLEAVE, MPOL_BIND or MPOL_PREFERRED policies.
When a tasks cpuset memory placement changes, whether because the cpuset
changed, or because the task was attached to a different cpuset, then the
tasks mempolicies have to be rebound to the new cpuset placement, so as to
preserve the cpuset-relative numbering of the nodes in that policy.
An earlier fix handled such mempolicy rebinding for mempolicies attached to a
task.
This fix rebinds mempolicies attached to vma's (address ranges in a tasks
address space.) Due to the need to hold the task->mm->mmap_sem semaphore while
updating vma's, the rebinding of vma mempolicies has to be done when the
cpuset memory placement is changed, at which time mmap_sem can be safely
acquired. The tasks mempolicy is rebound later, when the task next attempts
to allocate memory and notices that its task->cpuset_mems_generation is
out-of-date with its cpusets mems_generation.
Because walking the tasklist to find all tasks attached to a changing cpuset
requires holding tasklist_lock, a spinlock, one cannot update the vma's of the
affected tasks while doing the tasklist scan. In general, one cannot acquire
a semaphore (which can sleep) while already holding a spinlock (such as
tasklist_lock). So a list of mm references has to be built up during the
tasklist scan, then the tasklist lock dropped, then for each mm, its mmap_sem
acquired, and the vma's in that mm rebound.
Once the tasklist lock is dropped, affected tasks may fork new tasks, before
their mm's are rebound. A kernel global 'cpuset_being_rebound' is set to
point to the cpuset being rebound (there can only be one; cpuset modifications
are done under a global 'manage_sem' semaphore), and the mpol_copy code that
is used to copy a tasks mempolicies during fork catches such forking tasks,
and ensures their children are also rebound.
When a task is moved to a different cpuset, it is easier, as there is only one
task involved. It's mm->vma's are scanned, using the same
mpol_rebind_policy() as used above.
It may happen that both the mpol_copy hook and the update done via the
tasklist scan update the same mm twice. This is ok, as the mempolicies of
each vma in an mm keep track of what mems_allowed they are relative to, and
safely no-op a second request to rebind to the same nodes.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 12:01:59 +03:00
|
|
|
*/
|
2006-01-08 12:02:00 +03:00
|
|
|
migrate = is_memory_migrate(cs);
|
[PATCH] cpuset: rebind vma mempolicies fix
Fix more of longstanding bug in cpuset/mempolicy interaction.
NUMA mempolicies (mm/mempolicy.c) are constrained by the current tasks cpuset
to just the Memory Nodes allowed by that cpuset. The kernel maintains
internal state for each mempolicy, tracking what nodes are used for the
MPOL_INTERLEAVE, MPOL_BIND or MPOL_PREFERRED policies.
When a tasks cpuset memory placement changes, whether because the cpuset
changed, or because the task was attached to a different cpuset, then the
tasks mempolicies have to be rebound to the new cpuset placement, so as to
preserve the cpuset-relative numbering of the nodes in that policy.
An earlier fix handled such mempolicy rebinding for mempolicies attached to a
task.
This fix rebinds mempolicies attached to vma's (address ranges in a tasks
address space.) Due to the need to hold the task->mm->mmap_sem semaphore while
updating vma's, the rebinding of vma mempolicies has to be done when the
cpuset memory placement is changed, at which time mmap_sem can be safely
acquired. The tasks mempolicy is rebound later, when the task next attempts
to allocate memory and notices that its task->cpuset_mems_generation is
out-of-date with its cpusets mems_generation.
Because walking the tasklist to find all tasks attached to a changing cpuset
requires holding tasklist_lock, a spinlock, one cannot update the vma's of the
affected tasks while doing the tasklist scan. In general, one cannot acquire
a semaphore (which can sleep) while already holding a spinlock (such as
tasklist_lock). So a list of mm references has to be built up during the
tasklist scan, then the tasklist lock dropped, then for each mm, its mmap_sem
acquired, and the vma's in that mm rebound.
Once the tasklist lock is dropped, affected tasks may fork new tasks, before
their mm's are rebound. A kernel global 'cpuset_being_rebound' is set to
point to the cpuset being rebound (there can only be one; cpuset modifications
are done under a global 'manage_sem' semaphore), and the mpol_copy code that
is used to copy a tasks mempolicies during fork catches such forking tasks,
and ensures their children are also rebound.
When a task is moved to a different cpuset, it is easier, as there is only one
task involved. It's mm->vma's are scanned, using the same
mpol_rebind_policy() as used above.
It may happen that both the mpol_copy hook and the update done via the
tasklist scan update the same mm twice. This is ok, as the mempolicies of
each vma in an mm keep track of what mems_allowed they are relative to, and
safely no-op a second request to rebind to the same nodes.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 12:01:59 +03:00
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
struct mm_struct *mm = mmarray[i];
|
|
|
|
|
|
|
|
mpol_rebind_mm(mm, &cs->mems_allowed);
|
2006-03-31 14:30:52 +04:00
|
|
|
if (migrate)
|
|
|
|
cpuset_migrate_mm(mm, &oldmem, &cs->mems_allowed);
|
[PATCH] cpuset: rebind vma mempolicies fix
Fix more of longstanding bug in cpuset/mempolicy interaction.
NUMA mempolicies (mm/mempolicy.c) are constrained by the current tasks cpuset
to just the Memory Nodes allowed by that cpuset. The kernel maintains
internal state for each mempolicy, tracking what nodes are used for the
MPOL_INTERLEAVE, MPOL_BIND or MPOL_PREFERRED policies.
When a tasks cpuset memory placement changes, whether because the cpuset
changed, or because the task was attached to a different cpuset, then the
tasks mempolicies have to be rebound to the new cpuset placement, so as to
preserve the cpuset-relative numbering of the nodes in that policy.
An earlier fix handled such mempolicy rebinding for mempolicies attached to a
task.
This fix rebinds mempolicies attached to vma's (address ranges in a tasks
address space.) Due to the need to hold the task->mm->mmap_sem semaphore while
updating vma's, the rebinding of vma mempolicies has to be done when the
cpuset memory placement is changed, at which time mmap_sem can be safely
acquired. The tasks mempolicy is rebound later, when the task next attempts
to allocate memory and notices that its task->cpuset_mems_generation is
out-of-date with its cpusets mems_generation.
Because walking the tasklist to find all tasks attached to a changing cpuset
requires holding tasklist_lock, a spinlock, one cannot update the vma's of the
affected tasks while doing the tasklist scan. In general, one cannot acquire
a semaphore (which can sleep) while already holding a spinlock (such as
tasklist_lock). So a list of mm references has to be built up during the
tasklist scan, then the tasklist lock dropped, then for each mm, its mmap_sem
acquired, and the vma's in that mm rebound.
Once the tasklist lock is dropped, affected tasks may fork new tasks, before
their mm's are rebound. A kernel global 'cpuset_being_rebound' is set to
point to the cpuset being rebound (there can only be one; cpuset modifications
are done under a global 'manage_sem' semaphore), and the mpol_copy code that
is used to copy a tasks mempolicies during fork catches such forking tasks,
and ensures their children are also rebound.
When a task is moved to a different cpuset, it is easier, as there is only one
task involved. It's mm->vma's are scanned, using the same
mpol_rebind_policy() as used above.
It may happen that both the mpol_copy hook and the update done via the
tasklist scan update the same mm twice. This is ok, as the mempolicies of
each vma in an mm keep track of what mems_allowed they are relative to, and
safely no-op a second request to rebind to the same nodes.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 12:01:59 +03:00
|
|
|
mmput(mm);
|
|
|
|
}
|
|
|
|
|
2008-02-07 11:14:45 +03:00
|
|
|
/* We're done rebinding vmas to this cpuset's new mems_allowed. */
|
[PATCH] cpuset: rebind vma mempolicies fix
Fix more of longstanding bug in cpuset/mempolicy interaction.
NUMA mempolicies (mm/mempolicy.c) are constrained by the current tasks cpuset
to just the Memory Nodes allowed by that cpuset. The kernel maintains
internal state for each mempolicy, tracking what nodes are used for the
MPOL_INTERLEAVE, MPOL_BIND or MPOL_PREFERRED policies.
When a tasks cpuset memory placement changes, whether because the cpuset
changed, or because the task was attached to a different cpuset, then the
tasks mempolicies have to be rebound to the new cpuset placement, so as to
preserve the cpuset-relative numbering of the nodes in that policy.
An earlier fix handled such mempolicy rebinding for mempolicies attached to a
task.
This fix rebinds mempolicies attached to vma's (address ranges in a tasks
address space.) Due to the need to hold the task->mm->mmap_sem semaphore while
updating vma's, the rebinding of vma mempolicies has to be done when the
cpuset memory placement is changed, at which time mmap_sem can be safely
acquired. The tasks mempolicy is rebound later, when the task next attempts
to allocate memory and notices that its task->cpuset_mems_generation is
out-of-date with its cpusets mems_generation.
Because walking the tasklist to find all tasks attached to a changing cpuset
requires holding tasklist_lock, a spinlock, one cannot update the vma's of the
affected tasks while doing the tasklist scan. In general, one cannot acquire
a semaphore (which can sleep) while already holding a spinlock (such as
tasklist_lock). So a list of mm references has to be built up during the
tasklist scan, then the tasklist lock dropped, then for each mm, its mmap_sem
acquired, and the vma's in that mm rebound.
Once the tasklist lock is dropped, affected tasks may fork new tasks, before
their mm's are rebound. A kernel global 'cpuset_being_rebound' is set to
point to the cpuset being rebound (there can only be one; cpuset modifications
are done under a global 'manage_sem' semaphore), and the mpol_copy code that
is used to copy a tasks mempolicies during fork catches such forking tasks,
and ensures their children are also rebound.
When a task is moved to a different cpuset, it is easier, as there is only one
task involved. It's mm->vma's are scanned, using the same
mpol_rebind_policy() as used above.
It may happen that both the mpol_copy hook and the update done via the
tasklist scan update the same mm twice. This is ok, as the mempolicies of
each vma in an mm keep track of what mems_allowed they are relative to, and
safely no-op a second request to rebind to the same nodes.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 12:01:59 +03:00
|
|
|
kfree(mmarray);
|
2007-10-19 10:39:39 +04:00
|
|
|
cpuset_being_rebound = NULL;
|
[PATCH] cpuset: rebind vma mempolicies fix
Fix more of longstanding bug in cpuset/mempolicy interaction.
NUMA mempolicies (mm/mempolicy.c) are constrained by the current tasks cpuset
to just the Memory Nodes allowed by that cpuset. The kernel maintains
internal state for each mempolicy, tracking what nodes are used for the
MPOL_INTERLEAVE, MPOL_BIND or MPOL_PREFERRED policies.
When a tasks cpuset memory placement changes, whether because the cpuset
changed, or because the task was attached to a different cpuset, then the
tasks mempolicies have to be rebound to the new cpuset placement, so as to
preserve the cpuset-relative numbering of the nodes in that policy.
An earlier fix handled such mempolicy rebinding for mempolicies attached to a
task.
This fix rebinds mempolicies attached to vma's (address ranges in a tasks
address space.) Due to the need to hold the task->mm->mmap_sem semaphore while
updating vma's, the rebinding of vma mempolicies has to be done when the
cpuset memory placement is changed, at which time mmap_sem can be safely
acquired. The tasks mempolicy is rebound later, when the task next attempts
to allocate memory and notices that its task->cpuset_mems_generation is
out-of-date with its cpusets mems_generation.
Because walking the tasklist to find all tasks attached to a changing cpuset
requires holding tasklist_lock, a spinlock, one cannot update the vma's of the
affected tasks while doing the tasklist scan. In general, one cannot acquire
a semaphore (which can sleep) while already holding a spinlock (such as
tasklist_lock). So a list of mm references has to be built up during the
tasklist scan, then the tasklist lock dropped, then for each mm, its mmap_sem
acquired, and the vma's in that mm rebound.
Once the tasklist lock is dropped, affected tasks may fork new tasks, before
their mm's are rebound. A kernel global 'cpuset_being_rebound' is set to
point to the cpuset being rebound (there can only be one; cpuset modifications
are done under a global 'manage_sem' semaphore), and the mpol_copy code that
is used to copy a tasks mempolicies during fork catches such forking tasks,
and ensures their children are also rebound.
When a task is moved to a different cpuset, it is easier, as there is only one
task involved. It's mm->vma's are scanned, using the same
mpol_rebind_policy() as used above.
It may happen that both the mpol_copy hook and the update done via the
tasklist scan update the same mm twice. This is ok, as the mempolicies of
each vma in an mm keep track of what mems_allowed they are relative to, and
safely no-op a second request to rebind to the same nodes.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 12:01:59 +03:00
|
|
|
retval = 0;
|
2006-01-08 12:01:52 +03:00
|
|
|
done:
|
2005-04-17 02:20:36 +04:00
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2007-10-19 10:39:39 +04:00
|
|
|
int current_cpuset_is_being_rebound(void)
|
|
|
|
{
|
|
|
|
return task_cs(current) == cpuset_being_rebound;
|
|
|
|
}
|
|
|
|
|
2008-05-07 07:42:41 +04:00
|
|
|
static int update_relax_domain_level(struct cpuset *cs, s64 val)
|
2008-04-15 09:04:23 +04:00
|
|
|
{
|
2008-05-13 06:27:17 +04:00
|
|
|
if (val < -1 || val >= SD_LV_MAX)
|
|
|
|
return -EINVAL;
|
2008-04-15 09:04:23 +04:00
|
|
|
|
|
|
|
if (val != cs->relax_domain_level) {
|
|
|
|
cs->relax_domain_level = val;
|
|
|
|
rebuild_sched_domains();
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
/*
|
|
|
|
* update_flag - read a 0 or a 1 in a file and update associated flag
|
2008-04-29 12:00:26 +04:00
|
|
|
* bit: the bit to update (see cpuset_flagbits_t)
|
|
|
|
* cs: the cpuset to update
|
|
|
|
* turning_on: whether the flag is being set or cleared
|
[PATCH] cpusets: dual semaphore locking overhaul
Overhaul cpuset locking. Replace single semaphore with two semaphores.
The suggestion to use two locks was made by Roman Zippel.
Both locks are global. Code that wants to modify cpusets must first
acquire the exclusive manage_sem, which allows them read-only access to
cpusets, and holds off other would-be modifiers. Before making actual
changes, the second semaphore, callback_sem must be acquired as well. Code
that needs only to query cpusets must acquire callback_sem, which is also a
global exclusive lock.
The earlier problems with double tripping are avoided, because it is
allowed for holders of manage_sem to nest the second callback_sem lock, and
only callback_sem is needed by code called from within __alloc_pages(),
where the double tripping had been possible.
This is not quite the same as a normal read/write semaphore, because
obtaining read-only access with intent to change must hold off other such
attempts, while allowing read-only access w/o such intention. Changing
cpusets involves several related checks and changes, which must be done
while allowing read-only queries (to avoid the double trip), but while
ensuring nothing changes (holding off other would be modifiers.)
This overhaul of cpuset locking also makes careful use of task_lock() to
guard access to the task->cpuset pointer, closing a couple of race
conditions noticed while reading this code (thanks, Roman). I've never
seen these races fail in any use or test.
See further the comments in the code.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-10-31 02:02:30 +03:00
|
|
|
*
|
2008-02-07 11:14:45 +03:00
|
|
|
* Call with cgroup_mutex held.
|
2005-04-17 02:20:36 +04:00
|
|
|
*/
|
|
|
|
|
2008-04-29 12:00:00 +04:00
|
|
|
static int update_flag(cpuset_flagbits_t bit, struct cpuset *cs,
|
|
|
|
int turning_on)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
struct cpuset trialcs;
|
cpuset: remove sched domain hooks from cpusets
Remove the cpuset hooks that defined sched domains depending on the setting
of the 'cpu_exclusive' flag.
The cpu_exclusive flag can only be set on a child if it is set on the
parent.
This made that flag painfully unsuitable for use as a flag defining a
partitioning of a system.
It was entirely unobvious to a cpuset user what partitioning of sched
domains they would be causing when they set that one cpu_exclusive bit on
one cpuset, because it depended on what CPUs were in the remainder of that
cpusets siblings and child cpusets, after subtracting out other
cpu_exclusive cpusets.
Furthermore, there was no way on production systems to query the
result.
Using the cpu_exclusive flag for this was simply wrong from the get go.
Fortunately, it was sufficiently borked that so far as I know, almost no
successful use has been made of this. One real time group did use it to
affectively isolate CPUs from any load balancing efforts. They are willing
to adapt to alternative mechanisms for this, such as someway to manipulate
the list of isolated CPUs on a running system. They can do without this
present cpu_exclusive based mechanism while we develop an alternative.
There is a real risk, to the best of my understanding, of users
accidentally setting up a partitioned scheduler domains, inhibiting desired
load balancing across all their CPUs, due to the nonobvious (from the
cpuset perspective) side affects of the cpu_exclusive flag.
Furthermore, since there was no way on a running system to see what one was
doing with sched domains, this change will be invisible to any using code.
Unless they have real insight to the scheduler load balancing choices, they
will be unable to detect that this change has been made in the kernel's
behaviour.
Initial discussion on lkml of this patch has generated much comment. My
(probably controversial) take on that discussion is that it has reached a
rough concensus that the current cpuset cpu_exclusive mechanism for
defining sched domains is borked. There is no concensus on the
replacement. But since we can remove this mechanism, and since its
continued presence risks causing unwanted partitioning of the schedulers
load balancing, we should remove it while we can, as we proceed to work the
replacement scheduler domain mechanisms.
Signed-off-by: Paul Jackson <pj@sgi.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Nick Piggin <nickpiggin@yahoo.com.au>
Cc: Christoph Lameter <clameter@engr.sgi.com>
Cc: Dinakar Guniguntala <dino@in.ibm.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-10-16 12:27:43 +04:00
|
|
|
int err;
|
2007-10-19 10:40:20 +04:00
|
|
|
int cpus_nonempty, balance_flag_changed;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
trialcs = *cs;
|
|
|
|
if (turning_on)
|
|
|
|
set_bit(bit, &trialcs.flags);
|
|
|
|
else
|
|
|
|
clear_bit(bit, &trialcs.flags);
|
|
|
|
|
|
|
|
err = validate_change(cs, &trialcs);
|
2005-06-26 01:57:34 +04:00
|
|
|
if (err < 0)
|
|
|
|
return err;
|
2007-10-19 10:40:20 +04:00
|
|
|
|
|
|
|
cpus_nonempty = !cpus_empty(trialcs.cpus_allowed);
|
|
|
|
balance_flag_changed = (is_sched_load_balance(cs) !=
|
|
|
|
is_sched_load_balance(&trialcs));
|
|
|
|
|
2006-03-23 14:00:18 +03:00
|
|
|
mutex_lock(&callback_mutex);
|
2006-12-07 07:36:15 +03:00
|
|
|
cs->flags = trialcs.flags;
|
2006-03-23 14:00:18 +03:00
|
|
|
mutex_unlock(&callback_mutex);
|
2005-06-26 01:57:34 +04:00
|
|
|
|
2007-10-19 10:40:20 +04:00
|
|
|
if (cpus_nonempty && balance_flag_changed)
|
|
|
|
rebuild_sched_domains();
|
|
|
|
|
2005-06-26 01:57:34 +04:00
|
|
|
return 0;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
[PATCH] cpuset: memory pressure meter
Provide a simple per-cpuset metric of memory pressure, tracking the -rate-
that the tasks in a cpuset call try_to_free_pages(), the synchronous
(direct) memory reclaim code.
This enables batch managers monitoring jobs running in dedicated cpusets to
efficiently detect what level of memory pressure that job is causing.
This is useful both on tightly managed systems running a wide mix of
submitted jobs, which may choose to terminate or reprioritize jobs that are
trying to use more memory than allowed on the nodes assigned them, and with
tightly coupled, long running, massively parallel scientific computing jobs
that will dramatically fail to meet required performance goals if they
start to use more memory than allowed to them.
This patch just provides a very economical way for the batch manager to
monitor a cpuset for signs of memory pressure. It's up to the batch
manager or other user code to decide what to do about it and take action.
==> Unless this feature is enabled by writing "1" to the special file
/dev/cpuset/memory_pressure_enabled, the hook in the rebalance
code of __alloc_pages() for this metric reduces to simply noticing
that the cpuset_memory_pressure_enabled flag is zero. So only
systems that enable this feature will compute the metric.
Why a per-cpuset, running average:
Because this meter is per-cpuset, rather than per-task or mm, the
system load imposed by a batch scheduler monitoring this metric is
sharply reduced on large systems, because a scan of the tasklist can be
avoided on each set of queries.
Because this meter is a running average, instead of an accumulating
counter, a batch scheduler can detect memory pressure with a single
read, instead of having to read and accumulate results for a period of
time.
Because this meter is per-cpuset rather than per-task or mm, the
batch scheduler can obtain the key information, memory pressure in a
cpuset, with a single read, rather than having to query and accumulate
results over all the (dynamically changing) set of tasks in the cpuset.
A per-cpuset simple digital filter (requires a spinlock and 3 words of data
per-cpuset) is kept, and updated by any task attached to that cpuset, if it
enters the synchronous (direct) page reclaim code.
A per-cpuset file provides an integer number representing the recent
(half-life of 10 seconds) rate of direct page reclaims caused by the tasks
in the cpuset, in units of reclaims attempted per second, times 1000.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 12:01:49 +03:00
|
|
|
/*
|
2006-06-30 20:27:16 +04:00
|
|
|
* Frequency meter - How fast is some event occurring?
|
[PATCH] cpuset: memory pressure meter
Provide a simple per-cpuset metric of memory pressure, tracking the -rate-
that the tasks in a cpuset call try_to_free_pages(), the synchronous
(direct) memory reclaim code.
This enables batch managers monitoring jobs running in dedicated cpusets to
efficiently detect what level of memory pressure that job is causing.
This is useful both on tightly managed systems running a wide mix of
submitted jobs, which may choose to terminate or reprioritize jobs that are
trying to use more memory than allowed on the nodes assigned them, and with
tightly coupled, long running, massively parallel scientific computing jobs
that will dramatically fail to meet required performance goals if they
start to use more memory than allowed to them.
This patch just provides a very economical way for the batch manager to
monitor a cpuset for signs of memory pressure. It's up to the batch
manager or other user code to decide what to do about it and take action.
==> Unless this feature is enabled by writing "1" to the special file
/dev/cpuset/memory_pressure_enabled, the hook in the rebalance
code of __alloc_pages() for this metric reduces to simply noticing
that the cpuset_memory_pressure_enabled flag is zero. So only
systems that enable this feature will compute the metric.
Why a per-cpuset, running average:
Because this meter is per-cpuset, rather than per-task or mm, the
system load imposed by a batch scheduler monitoring this metric is
sharply reduced on large systems, because a scan of the tasklist can be
avoided on each set of queries.
Because this meter is a running average, instead of an accumulating
counter, a batch scheduler can detect memory pressure with a single
read, instead of having to read and accumulate results for a period of
time.
Because this meter is per-cpuset rather than per-task or mm, the
batch scheduler can obtain the key information, memory pressure in a
cpuset, with a single read, rather than having to query and accumulate
results over all the (dynamically changing) set of tasks in the cpuset.
A per-cpuset simple digital filter (requires a spinlock and 3 words of data
per-cpuset) is kept, and updated by any task attached to that cpuset, if it
enters the synchronous (direct) page reclaim code.
A per-cpuset file provides an integer number representing the recent
(half-life of 10 seconds) rate of direct page reclaims caused by the tasks
in the cpuset, in units of reclaims attempted per second, times 1000.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 12:01:49 +03:00
|
|
|
*
|
|
|
|
* These routines manage a digitally filtered, constant time based,
|
|
|
|
* event frequency meter. There are four routines:
|
|
|
|
* fmeter_init() - initialize a frequency meter.
|
|
|
|
* fmeter_markevent() - called each time the event happens.
|
|
|
|
* fmeter_getrate() - returns the recent rate of such events.
|
|
|
|
* fmeter_update() - internal routine used to update fmeter.
|
|
|
|
*
|
|
|
|
* A common data structure is passed to each of these routines,
|
|
|
|
* which is used to keep track of the state required to manage the
|
|
|
|
* frequency meter and its digital filter.
|
|
|
|
*
|
|
|
|
* The filter works on the number of events marked per unit time.
|
|
|
|
* The filter is single-pole low-pass recursive (IIR). The time unit
|
|
|
|
* is 1 second. Arithmetic is done using 32-bit integers scaled to
|
|
|
|
* simulate 3 decimal digits of precision (multiplied by 1000).
|
|
|
|
*
|
|
|
|
* With an FM_COEF of 933, and a time base of 1 second, the filter
|
|
|
|
* has a half-life of 10 seconds, meaning that if the events quit
|
|
|
|
* happening, then the rate returned from the fmeter_getrate()
|
|
|
|
* will be cut in half each 10 seconds, until it converges to zero.
|
|
|
|
*
|
|
|
|
* It is not worth doing a real infinitely recursive filter. If more
|
|
|
|
* than FM_MAXTICKS ticks have elapsed since the last filter event,
|
|
|
|
* just compute FM_MAXTICKS ticks worth, by which point the level
|
|
|
|
* will be stable.
|
|
|
|
*
|
|
|
|
* Limit the count of unprocessed events to FM_MAXCNT, so as to avoid
|
|
|
|
* arithmetic overflow in the fmeter_update() routine.
|
|
|
|
*
|
|
|
|
* Given the simple 32 bit integer arithmetic used, this meter works
|
|
|
|
* best for reporting rates between one per millisecond (msec) and
|
|
|
|
* one per 32 (approx) seconds. At constant rates faster than one
|
|
|
|
* per msec it maxes out at values just under 1,000,000. At constant
|
|
|
|
* rates between one per msec, and one per second it will stabilize
|
|
|
|
* to a value N*1000, where N is the rate of events per second.
|
|
|
|
* At constant rates between one per second and one per 32 seconds,
|
|
|
|
* it will be choppy, moving up on the seconds that have an event,
|
|
|
|
* and then decaying until the next event. At rates slower than
|
|
|
|
* about one in 32 seconds, it decays all the way back to zero between
|
|
|
|
* each event.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define FM_COEF 933 /* coefficient for half-life of 10 secs */
|
|
|
|
#define FM_MAXTICKS ((time_t)99) /* useless computing more ticks than this */
|
|
|
|
#define FM_MAXCNT 1000000 /* limit cnt to avoid overflow */
|
|
|
|
#define FM_SCALE 1000 /* faux fixed point scale */
|
|
|
|
|
|
|
|
/* Initialize a frequency meter */
|
|
|
|
static void fmeter_init(struct fmeter *fmp)
|
|
|
|
{
|
|
|
|
fmp->cnt = 0;
|
|
|
|
fmp->val = 0;
|
|
|
|
fmp->time = 0;
|
|
|
|
spin_lock_init(&fmp->lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Internal meter update - process cnt events and update value */
|
|
|
|
static void fmeter_update(struct fmeter *fmp)
|
|
|
|
{
|
|
|
|
time_t now = get_seconds();
|
|
|
|
time_t ticks = now - fmp->time;
|
|
|
|
|
|
|
|
if (ticks == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
ticks = min(FM_MAXTICKS, ticks);
|
|
|
|
while (ticks-- > 0)
|
|
|
|
fmp->val = (FM_COEF * fmp->val) / FM_SCALE;
|
|
|
|
fmp->time = now;
|
|
|
|
|
|
|
|
fmp->val += ((FM_SCALE - FM_COEF) * fmp->cnt) / FM_SCALE;
|
|
|
|
fmp->cnt = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Process any previous ticks, then bump cnt by one (times scale). */
|
|
|
|
static void fmeter_markevent(struct fmeter *fmp)
|
|
|
|
{
|
|
|
|
spin_lock(&fmp->lock);
|
|
|
|
fmeter_update(fmp);
|
|
|
|
fmp->cnt = min(FM_MAXCNT, fmp->cnt + FM_SCALE);
|
|
|
|
spin_unlock(&fmp->lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Process any previous ticks, then return current value. */
|
|
|
|
static int fmeter_getrate(struct fmeter *fmp)
|
|
|
|
{
|
|
|
|
int val;
|
|
|
|
|
|
|
|
spin_lock(&fmp->lock);
|
|
|
|
fmeter_update(fmp);
|
|
|
|
val = fmp->val;
|
|
|
|
spin_unlock(&fmp->lock);
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
2008-02-07 11:14:45 +03:00
|
|
|
/* Called by cgroups to determine if a cpuset is usable; cgroup_mutex held */
|
2007-10-19 10:39:39 +04:00
|
|
|
static int cpuset_can_attach(struct cgroup_subsys *ss,
|
|
|
|
struct cgroup *cont, struct task_struct *tsk)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2007-10-19 10:39:39 +04:00
|
|
|
struct cpuset *cs = cgroup_cs(cont);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
if (cpus_empty(cs->cpus_allowed) || nodes_empty(cs->mems_allowed))
|
|
|
|
return -ENOSPC;
|
|
|
|
|
2007-10-19 10:39:39 +04:00
|
|
|
return security_task_setscheduler(tsk, 0, NULL);
|
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2007-10-19 10:39:39 +04:00
|
|
|
static void cpuset_attach(struct cgroup_subsys *ss,
|
|
|
|
struct cgroup *cont, struct cgroup *oldcont,
|
|
|
|
struct task_struct *tsk)
|
|
|
|
{
|
|
|
|
cpumask_t cpus;
|
|
|
|
nodemask_t from, to;
|
|
|
|
struct mm_struct *mm;
|
|
|
|
struct cpuset *cs = cgroup_cs(cont);
|
|
|
|
struct cpuset *oldcs = cgroup_cs(oldcont);
|
2006-06-23 13:04:00 +04:00
|
|
|
|
2006-03-23 14:00:18 +03:00
|
|
|
mutex_lock(&callback_mutex);
|
2005-04-17 02:20:36 +04:00
|
|
|
guarantee_online_cpus(cs, &cpus);
|
2008-04-05 05:11:07 +04:00
|
|
|
set_cpus_allowed_ptr(tsk, &cpus);
|
2007-10-19 10:39:39 +04:00
|
|
|
mutex_unlock(&callback_mutex);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
[PATCH] cpusets: swap migration interface
Add a boolean "memory_migrate" to each cpuset, represented by a file
containing "0" or "1" in each directory below /dev/cpuset.
It defaults to false (file contains "0"). It can be set true by writing
"1" to the file.
If true, then anytime that a task is attached to the cpuset so marked, the
pages of that task will be moved to that cpuset, preserving, to the extent
practical, the cpuset-relative placement of the pages.
Also anytime that a cpuset so marked has its memory placement changed (by
writing to its "mems" file), the tasks in that cpuset will have their pages
moved to the cpusets new nodes, preserving, to the extent practical, the
cpuset-relative placement of the moved pages.
Signed-off-by: Paul Jackson <pj@sgi.com>
Cc: Christoph Lameter <christoph@lameter.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 12:00:56 +03:00
|
|
|
from = oldcs->mems_allowed;
|
|
|
|
to = cs->mems_allowed;
|
[PATCH] cpuset: rebind vma mempolicies fix
Fix more of longstanding bug in cpuset/mempolicy interaction.
NUMA mempolicies (mm/mempolicy.c) are constrained by the current tasks cpuset
to just the Memory Nodes allowed by that cpuset. The kernel maintains
internal state for each mempolicy, tracking what nodes are used for the
MPOL_INTERLEAVE, MPOL_BIND or MPOL_PREFERRED policies.
When a tasks cpuset memory placement changes, whether because the cpuset
changed, or because the task was attached to a different cpuset, then the
tasks mempolicies have to be rebound to the new cpuset placement, so as to
preserve the cpuset-relative numbering of the nodes in that policy.
An earlier fix handled such mempolicy rebinding for mempolicies attached to a
task.
This fix rebinds mempolicies attached to vma's (address ranges in a tasks
address space.) Due to the need to hold the task->mm->mmap_sem semaphore while
updating vma's, the rebinding of vma mempolicies has to be done when the
cpuset memory placement is changed, at which time mmap_sem can be safely
acquired. The tasks mempolicy is rebound later, when the task next attempts
to allocate memory and notices that its task->cpuset_mems_generation is
out-of-date with its cpusets mems_generation.
Because walking the tasklist to find all tasks attached to a changing cpuset
requires holding tasklist_lock, a spinlock, one cannot update the vma's of the
affected tasks while doing the tasklist scan. In general, one cannot acquire
a semaphore (which can sleep) while already holding a spinlock (such as
tasklist_lock). So a list of mm references has to be built up during the
tasklist scan, then the tasklist lock dropped, then for each mm, its mmap_sem
acquired, and the vma's in that mm rebound.
Once the tasklist lock is dropped, affected tasks may fork new tasks, before
their mm's are rebound. A kernel global 'cpuset_being_rebound' is set to
point to the cpuset being rebound (there can only be one; cpuset modifications
are done under a global 'manage_sem' semaphore), and the mpol_copy code that
is used to copy a tasks mempolicies during fork catches such forking tasks,
and ensures their children are also rebound.
When a task is moved to a different cpuset, it is easier, as there is only one
task involved. It's mm->vma's are scanned, using the same
mpol_rebind_policy() as used above.
It may happen that both the mpol_copy hook and the update done via the
tasklist scan update the same mm twice. This is ok, as the mempolicies of
each vma in an mm keep track of what mems_allowed they are relative to, and
safely no-op a second request to rebind to the same nodes.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 12:01:59 +03:00
|
|
|
mm = get_task_mm(tsk);
|
|
|
|
if (mm) {
|
|
|
|
mpol_rebind_mm(mm, &to);
|
2006-03-31 14:30:51 +04:00
|
|
|
if (is_memory_migrate(cs))
|
2006-03-31 14:30:52 +04:00
|
|
|
cpuset_migrate_mm(mm, &from, &to);
|
[PATCH] cpuset: rebind vma mempolicies fix
Fix more of longstanding bug in cpuset/mempolicy interaction.
NUMA mempolicies (mm/mempolicy.c) are constrained by the current tasks cpuset
to just the Memory Nodes allowed by that cpuset. The kernel maintains
internal state for each mempolicy, tracking what nodes are used for the
MPOL_INTERLEAVE, MPOL_BIND or MPOL_PREFERRED policies.
When a tasks cpuset memory placement changes, whether because the cpuset
changed, or because the task was attached to a different cpuset, then the
tasks mempolicies have to be rebound to the new cpuset placement, so as to
preserve the cpuset-relative numbering of the nodes in that policy.
An earlier fix handled such mempolicy rebinding for mempolicies attached to a
task.
This fix rebinds mempolicies attached to vma's (address ranges in a tasks
address space.) Due to the need to hold the task->mm->mmap_sem semaphore while
updating vma's, the rebinding of vma mempolicies has to be done when the
cpuset memory placement is changed, at which time mmap_sem can be safely
acquired. The tasks mempolicy is rebound later, when the task next attempts
to allocate memory and notices that its task->cpuset_mems_generation is
out-of-date with its cpusets mems_generation.
Because walking the tasklist to find all tasks attached to a changing cpuset
requires holding tasklist_lock, a spinlock, one cannot update the vma's of the
affected tasks while doing the tasklist scan. In general, one cannot acquire
a semaphore (which can sleep) while already holding a spinlock (such as
tasklist_lock). So a list of mm references has to be built up during the
tasklist scan, then the tasklist lock dropped, then for each mm, its mmap_sem
acquired, and the vma's in that mm rebound.
Once the tasklist lock is dropped, affected tasks may fork new tasks, before
their mm's are rebound. A kernel global 'cpuset_being_rebound' is set to
point to the cpuset being rebound (there can only be one; cpuset modifications
are done under a global 'manage_sem' semaphore), and the mpol_copy code that
is used to copy a tasks mempolicies during fork catches such forking tasks,
and ensures their children are also rebound.
When a task is moved to a different cpuset, it is easier, as there is only one
task involved. It's mm->vma's are scanned, using the same
mpol_rebind_policy() as used above.
It may happen that both the mpol_copy hook and the update done via the
tasklist scan update the same mm twice. This is ok, as the mempolicies of
each vma in an mm keep track of what mems_allowed they are relative to, and
safely no-op a second request to rebind to the same nodes.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 12:01:59 +03:00
|
|
|
mmput(mm);
|
|
|
|
}
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* The various types of files and directories in a cpuset file system */
|
|
|
|
|
|
|
|
typedef enum {
|
[PATCH] cpusets: swap migration interface
Add a boolean "memory_migrate" to each cpuset, represented by a file
containing "0" or "1" in each directory below /dev/cpuset.
It defaults to false (file contains "0"). It can be set true by writing
"1" to the file.
If true, then anytime that a task is attached to the cpuset so marked, the
pages of that task will be moved to that cpuset, preserving, to the extent
practical, the cpuset-relative placement of the pages.
Also anytime that a cpuset so marked has its memory placement changed (by
writing to its "mems" file), the tasks in that cpuset will have their pages
moved to the cpusets new nodes, preserving, to the extent practical, the
cpuset-relative placement of the moved pages.
Signed-off-by: Paul Jackson <pj@sgi.com>
Cc: Christoph Lameter <christoph@lameter.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 12:00:56 +03:00
|
|
|
FILE_MEMORY_MIGRATE,
|
2005-04-17 02:20:36 +04:00
|
|
|
FILE_CPULIST,
|
|
|
|
FILE_MEMLIST,
|
|
|
|
FILE_CPU_EXCLUSIVE,
|
|
|
|
FILE_MEM_EXCLUSIVE,
|
2008-04-29 12:00:26 +04:00
|
|
|
FILE_MEM_HARDWALL,
|
2007-10-19 10:40:20 +04:00
|
|
|
FILE_SCHED_LOAD_BALANCE,
|
2008-04-15 09:04:23 +04:00
|
|
|
FILE_SCHED_RELAX_DOMAIN_LEVEL,
|
[PATCH] cpuset: memory pressure meter
Provide a simple per-cpuset metric of memory pressure, tracking the -rate-
that the tasks in a cpuset call try_to_free_pages(), the synchronous
(direct) memory reclaim code.
This enables batch managers monitoring jobs running in dedicated cpusets to
efficiently detect what level of memory pressure that job is causing.
This is useful both on tightly managed systems running a wide mix of
submitted jobs, which may choose to terminate or reprioritize jobs that are
trying to use more memory than allowed on the nodes assigned them, and with
tightly coupled, long running, massively parallel scientific computing jobs
that will dramatically fail to meet required performance goals if they
start to use more memory than allowed to them.
This patch just provides a very economical way for the batch manager to
monitor a cpuset for signs of memory pressure. It's up to the batch
manager or other user code to decide what to do about it and take action.
==> Unless this feature is enabled by writing "1" to the special file
/dev/cpuset/memory_pressure_enabled, the hook in the rebalance
code of __alloc_pages() for this metric reduces to simply noticing
that the cpuset_memory_pressure_enabled flag is zero. So only
systems that enable this feature will compute the metric.
Why a per-cpuset, running average:
Because this meter is per-cpuset, rather than per-task or mm, the
system load imposed by a batch scheduler monitoring this metric is
sharply reduced on large systems, because a scan of the tasklist can be
avoided on each set of queries.
Because this meter is a running average, instead of an accumulating
counter, a batch scheduler can detect memory pressure with a single
read, instead of having to read and accumulate results for a period of
time.
Because this meter is per-cpuset rather than per-task or mm, the
batch scheduler can obtain the key information, memory pressure in a
cpuset, with a single read, rather than having to query and accumulate
results over all the (dynamically changing) set of tasks in the cpuset.
A per-cpuset simple digital filter (requires a spinlock and 3 words of data
per-cpuset) is kept, and updated by any task attached to that cpuset, if it
enters the synchronous (direct) page reclaim code.
A per-cpuset file provides an integer number representing the recent
(half-life of 10 seconds) rate of direct page reclaims caused by the tasks
in the cpuset, in units of reclaims attempted per second, times 1000.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 12:01:49 +03:00
|
|
|
FILE_MEMORY_PRESSURE_ENABLED,
|
|
|
|
FILE_MEMORY_PRESSURE,
|
[PATCH] cpuset memory spread basic implementation
This patch provides the implementation and cpuset interface for an alternative
memory allocation policy that can be applied to certain kinds of memory
allocations, such as the page cache (file system buffers) and some slab caches
(such as inode caches).
The policy is called "memory spreading." If enabled, it spreads out these
kinds of memory allocations over all the nodes allowed to a task, instead of
preferring to place them on the node where the task is executing.
All other kinds of allocations, including anonymous pages for a tasks stack
and data regions, are not affected by this policy choice, and continue to be
allocated preferring the node local to execution, as modified by the NUMA
mempolicy.
There are two boolean flag files per cpuset that control where the kernel
allocates pages for the file system buffers and related in kernel data
structures. They are called 'memory_spread_page' and 'memory_spread_slab'.
If the per-cpuset boolean flag file 'memory_spread_page' is set, then the
kernel will spread the file system buffers (page cache) evenly over all the
nodes that the faulting task is allowed to use, instead of preferring to put
those pages on the node where the task is running.
If the per-cpuset boolean flag file 'memory_spread_slab' is set, then the
kernel will spread some file system related slab caches, such as for inodes
and dentries evenly over all the nodes that the faulting task is allowed to
use, instead of preferring to put those pages on the node where the task is
running.
The implementation is simple. Setting the cpuset flags 'memory_spread_page'
or 'memory_spread_cache' turns on the per-process flags PF_SPREAD_PAGE or
PF_SPREAD_SLAB, respectively, for each task that is in the cpuset or
subsequently joins that cpuset. In subsequent patches, the page allocation
calls for the affected page cache and slab caches are modified to perform an
inline check for these flags, and if set, a call to a new routine
cpuset_mem_spread_node() returns the node to prefer for the allocation.
The cpuset_mem_spread_node() routine is also simple. It uses the value of a
per-task rotor cpuset_mem_spread_rotor to select the next node in the current
tasks mems_allowed to prefer for the allocation.
This policy can provide substantial improvements for jobs that need to place
thread local data on the corresponding node, but that need to access large
file system data sets that need to be spread across the several nodes in the
jobs cpuset in order to fit. Without this patch, especially for jobs that
might have one thread reading in the data set, the memory allocation across
the nodes in the jobs cpuset can become very uneven.
A couple of Copyright year ranges are updated as well. And a couple of email
addresses that can be found in the MAINTAINERS file are removed.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-03-24 14:16:03 +03:00
|
|
|
FILE_SPREAD_PAGE,
|
|
|
|
FILE_SPREAD_SLAB,
|
2005-04-17 02:20:36 +04:00
|
|
|
} cpuset_filetype_t;
|
|
|
|
|
2007-10-19 10:39:39 +04:00
|
|
|
static ssize_t cpuset_common_file_write(struct cgroup *cont,
|
|
|
|
struct cftype *cft,
|
|
|
|
struct file *file,
|
2006-12-07 07:41:37 +03:00
|
|
|
const char __user *userbuf,
|
2005-04-17 02:20:36 +04:00
|
|
|
size_t nbytes, loff_t *unused_ppos)
|
|
|
|
{
|
2007-10-19 10:39:39 +04:00
|
|
|
struct cpuset *cs = cgroup_cs(cont);
|
2005-04-17 02:20:36 +04:00
|
|
|
cpuset_filetype_t type = cft->private;
|
|
|
|
char *buffer;
|
|
|
|
int retval = 0;
|
|
|
|
|
|
|
|
/* Crude upper limit on largest legitimate cpulist user might write. */
|
2007-10-19 10:40:20 +04:00
|
|
|
if (nbytes > 100U + 6 * max(NR_CPUS, MAX_NUMNODES))
|
2005-04-17 02:20:36 +04:00
|
|
|
return -E2BIG;
|
|
|
|
|
|
|
|
/* +1 for nul-terminator */
|
2008-04-29 01:13:19 +04:00
|
|
|
buffer = kmalloc(nbytes + 1, GFP_KERNEL);
|
|
|
|
if (!buffer)
|
2005-04-17 02:20:36 +04:00
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
if (copy_from_user(buffer, userbuf, nbytes)) {
|
|
|
|
retval = -EFAULT;
|
|
|
|
goto out1;
|
|
|
|
}
|
|
|
|
buffer[nbytes] = 0; /* nul-terminate */
|
|
|
|
|
2007-10-19 10:39:39 +04:00
|
|
|
cgroup_lock();
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2007-10-19 10:39:39 +04:00
|
|
|
if (cgroup_is_removed(cont)) {
|
2005-04-17 02:20:36 +04:00
|
|
|
retval = -ENODEV;
|
|
|
|
goto out2;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case FILE_CPULIST:
|
|
|
|
retval = update_cpumask(cs, buffer);
|
|
|
|
break;
|
|
|
|
case FILE_MEMLIST:
|
|
|
|
retval = update_nodemask(cs, buffer);
|
|
|
|
break;
|
2008-04-29 12:00:00 +04:00
|
|
|
default:
|
|
|
|
retval = -EINVAL;
|
|
|
|
goto out2;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (retval == 0)
|
|
|
|
retval = nbytes;
|
|
|
|
out2:
|
|
|
|
cgroup_unlock();
|
|
|
|
out1:
|
|
|
|
kfree(buffer);
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cpuset_write_u64(struct cgroup *cgrp, struct cftype *cft, u64 val)
|
|
|
|
{
|
|
|
|
int retval = 0;
|
|
|
|
struct cpuset *cs = cgroup_cs(cgrp);
|
|
|
|
cpuset_filetype_t type = cft->private;
|
|
|
|
|
|
|
|
cgroup_lock();
|
|
|
|
|
|
|
|
if (cgroup_is_removed(cgrp)) {
|
|
|
|
cgroup_unlock();
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (type) {
|
2005-04-17 02:20:36 +04:00
|
|
|
case FILE_CPU_EXCLUSIVE:
|
2008-04-29 12:00:00 +04:00
|
|
|
retval = update_flag(CS_CPU_EXCLUSIVE, cs, val);
|
2005-04-17 02:20:36 +04:00
|
|
|
break;
|
|
|
|
case FILE_MEM_EXCLUSIVE:
|
2008-04-29 12:00:00 +04:00
|
|
|
retval = update_flag(CS_MEM_EXCLUSIVE, cs, val);
|
2005-04-17 02:20:36 +04:00
|
|
|
break;
|
2008-04-29 12:00:26 +04:00
|
|
|
case FILE_MEM_HARDWALL:
|
|
|
|
retval = update_flag(CS_MEM_HARDWALL, cs, val);
|
|
|
|
break;
|
2007-10-19 10:40:20 +04:00
|
|
|
case FILE_SCHED_LOAD_BALANCE:
|
2008-04-29 12:00:00 +04:00
|
|
|
retval = update_flag(CS_SCHED_LOAD_BALANCE, cs, val);
|
2008-04-15 09:04:23 +04:00
|
|
|
break;
|
[PATCH] cpusets: swap migration interface
Add a boolean "memory_migrate" to each cpuset, represented by a file
containing "0" or "1" in each directory below /dev/cpuset.
It defaults to false (file contains "0"). It can be set true by writing
"1" to the file.
If true, then anytime that a task is attached to the cpuset so marked, the
pages of that task will be moved to that cpuset, preserving, to the extent
practical, the cpuset-relative placement of the pages.
Also anytime that a cpuset so marked has its memory placement changed (by
writing to its "mems" file), the tasks in that cpuset will have their pages
moved to the cpusets new nodes, preserving, to the extent practical, the
cpuset-relative placement of the moved pages.
Signed-off-by: Paul Jackson <pj@sgi.com>
Cc: Christoph Lameter <christoph@lameter.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 12:00:56 +03:00
|
|
|
case FILE_MEMORY_MIGRATE:
|
2008-04-29 12:00:00 +04:00
|
|
|
retval = update_flag(CS_MEMORY_MIGRATE, cs, val);
|
[PATCH] cpusets: swap migration interface
Add a boolean "memory_migrate" to each cpuset, represented by a file
containing "0" or "1" in each directory below /dev/cpuset.
It defaults to false (file contains "0"). It can be set true by writing
"1" to the file.
If true, then anytime that a task is attached to the cpuset so marked, the
pages of that task will be moved to that cpuset, preserving, to the extent
practical, the cpuset-relative placement of the pages.
Also anytime that a cpuset so marked has its memory placement changed (by
writing to its "mems" file), the tasks in that cpuset will have their pages
moved to the cpusets new nodes, preserving, to the extent practical, the
cpuset-relative placement of the moved pages.
Signed-off-by: Paul Jackson <pj@sgi.com>
Cc: Christoph Lameter <christoph@lameter.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 12:00:56 +03:00
|
|
|
break;
|
[PATCH] cpuset: memory pressure meter
Provide a simple per-cpuset metric of memory pressure, tracking the -rate-
that the tasks in a cpuset call try_to_free_pages(), the synchronous
(direct) memory reclaim code.
This enables batch managers monitoring jobs running in dedicated cpusets to
efficiently detect what level of memory pressure that job is causing.
This is useful both on tightly managed systems running a wide mix of
submitted jobs, which may choose to terminate or reprioritize jobs that are
trying to use more memory than allowed on the nodes assigned them, and with
tightly coupled, long running, massively parallel scientific computing jobs
that will dramatically fail to meet required performance goals if they
start to use more memory than allowed to them.
This patch just provides a very economical way for the batch manager to
monitor a cpuset for signs of memory pressure. It's up to the batch
manager or other user code to decide what to do about it and take action.
==> Unless this feature is enabled by writing "1" to the special file
/dev/cpuset/memory_pressure_enabled, the hook in the rebalance
code of __alloc_pages() for this metric reduces to simply noticing
that the cpuset_memory_pressure_enabled flag is zero. So only
systems that enable this feature will compute the metric.
Why a per-cpuset, running average:
Because this meter is per-cpuset, rather than per-task or mm, the
system load imposed by a batch scheduler monitoring this metric is
sharply reduced on large systems, because a scan of the tasklist can be
avoided on each set of queries.
Because this meter is a running average, instead of an accumulating
counter, a batch scheduler can detect memory pressure with a single
read, instead of having to read and accumulate results for a period of
time.
Because this meter is per-cpuset rather than per-task or mm, the
batch scheduler can obtain the key information, memory pressure in a
cpuset, with a single read, rather than having to query and accumulate
results over all the (dynamically changing) set of tasks in the cpuset.
A per-cpuset simple digital filter (requires a spinlock and 3 words of data
per-cpuset) is kept, and updated by any task attached to that cpuset, if it
enters the synchronous (direct) page reclaim code.
A per-cpuset file provides an integer number representing the recent
(half-life of 10 seconds) rate of direct page reclaims caused by the tasks
in the cpuset, in units of reclaims attempted per second, times 1000.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 12:01:49 +03:00
|
|
|
case FILE_MEMORY_PRESSURE_ENABLED:
|
2008-04-29 12:00:00 +04:00
|
|
|
cpuset_memory_pressure_enabled = !!val;
|
[PATCH] cpuset: memory pressure meter
Provide a simple per-cpuset metric of memory pressure, tracking the -rate-
that the tasks in a cpuset call try_to_free_pages(), the synchronous
(direct) memory reclaim code.
This enables batch managers monitoring jobs running in dedicated cpusets to
efficiently detect what level of memory pressure that job is causing.
This is useful both on tightly managed systems running a wide mix of
submitted jobs, which may choose to terminate or reprioritize jobs that are
trying to use more memory than allowed on the nodes assigned them, and with
tightly coupled, long running, massively parallel scientific computing jobs
that will dramatically fail to meet required performance goals if they
start to use more memory than allowed to them.
This patch just provides a very economical way for the batch manager to
monitor a cpuset for signs of memory pressure. It's up to the batch
manager or other user code to decide what to do about it and take action.
==> Unless this feature is enabled by writing "1" to the special file
/dev/cpuset/memory_pressure_enabled, the hook in the rebalance
code of __alloc_pages() for this metric reduces to simply noticing
that the cpuset_memory_pressure_enabled flag is zero. So only
systems that enable this feature will compute the metric.
Why a per-cpuset, running average:
Because this meter is per-cpuset, rather than per-task or mm, the
system load imposed by a batch scheduler monitoring this metric is
sharply reduced on large systems, because a scan of the tasklist can be
avoided on each set of queries.
Because this meter is a running average, instead of an accumulating
counter, a batch scheduler can detect memory pressure with a single
read, instead of having to read and accumulate results for a period of
time.
Because this meter is per-cpuset rather than per-task or mm, the
batch scheduler can obtain the key information, memory pressure in a
cpuset, with a single read, rather than having to query and accumulate
results over all the (dynamically changing) set of tasks in the cpuset.
A per-cpuset simple digital filter (requires a spinlock and 3 words of data
per-cpuset) is kept, and updated by any task attached to that cpuset, if it
enters the synchronous (direct) page reclaim code.
A per-cpuset file provides an integer number representing the recent
(half-life of 10 seconds) rate of direct page reclaims caused by the tasks
in the cpuset, in units of reclaims attempted per second, times 1000.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 12:01:49 +03:00
|
|
|
break;
|
|
|
|
case FILE_MEMORY_PRESSURE:
|
|
|
|
retval = -EACCES;
|
|
|
|
break;
|
[PATCH] cpuset memory spread basic implementation
This patch provides the implementation and cpuset interface for an alternative
memory allocation policy that can be applied to certain kinds of memory
allocations, such as the page cache (file system buffers) and some slab caches
(such as inode caches).
The policy is called "memory spreading." If enabled, it spreads out these
kinds of memory allocations over all the nodes allowed to a task, instead of
preferring to place them on the node where the task is executing.
All other kinds of allocations, including anonymous pages for a tasks stack
and data regions, are not affected by this policy choice, and continue to be
allocated preferring the node local to execution, as modified by the NUMA
mempolicy.
There are two boolean flag files per cpuset that control where the kernel
allocates pages for the file system buffers and related in kernel data
structures. They are called 'memory_spread_page' and 'memory_spread_slab'.
If the per-cpuset boolean flag file 'memory_spread_page' is set, then the
kernel will spread the file system buffers (page cache) evenly over all the
nodes that the faulting task is allowed to use, instead of preferring to put
those pages on the node where the task is running.
If the per-cpuset boolean flag file 'memory_spread_slab' is set, then the
kernel will spread some file system related slab caches, such as for inodes
and dentries evenly over all the nodes that the faulting task is allowed to
use, instead of preferring to put those pages on the node where the task is
running.
The implementation is simple. Setting the cpuset flags 'memory_spread_page'
or 'memory_spread_cache' turns on the per-process flags PF_SPREAD_PAGE or
PF_SPREAD_SLAB, respectively, for each task that is in the cpuset or
subsequently joins that cpuset. In subsequent patches, the page allocation
calls for the affected page cache and slab caches are modified to perform an
inline check for these flags, and if set, a call to a new routine
cpuset_mem_spread_node() returns the node to prefer for the allocation.
The cpuset_mem_spread_node() routine is also simple. It uses the value of a
per-task rotor cpuset_mem_spread_rotor to select the next node in the current
tasks mems_allowed to prefer for the allocation.
This policy can provide substantial improvements for jobs that need to place
thread local data on the corresponding node, but that need to access large
file system data sets that need to be spread across the several nodes in the
jobs cpuset in order to fit. Without this patch, especially for jobs that
might have one thread reading in the data set, the memory allocation across
the nodes in the jobs cpuset can become very uneven.
A couple of Copyright year ranges are updated as well. And a couple of email
addresses that can be found in the MAINTAINERS file are removed.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-03-24 14:16:03 +03:00
|
|
|
case FILE_SPREAD_PAGE:
|
2008-04-29 12:00:00 +04:00
|
|
|
retval = update_flag(CS_SPREAD_PAGE, cs, val);
|
2006-03-24 14:16:11 +03:00
|
|
|
cs->mems_generation = cpuset_mems_generation++;
|
[PATCH] cpuset memory spread basic implementation
This patch provides the implementation and cpuset interface for an alternative
memory allocation policy that can be applied to certain kinds of memory
allocations, such as the page cache (file system buffers) and some slab caches
(such as inode caches).
The policy is called "memory spreading." If enabled, it spreads out these
kinds of memory allocations over all the nodes allowed to a task, instead of
preferring to place them on the node where the task is executing.
All other kinds of allocations, including anonymous pages for a tasks stack
and data regions, are not affected by this policy choice, and continue to be
allocated preferring the node local to execution, as modified by the NUMA
mempolicy.
There are two boolean flag files per cpuset that control where the kernel
allocates pages for the file system buffers and related in kernel data
structures. They are called 'memory_spread_page' and 'memory_spread_slab'.
If the per-cpuset boolean flag file 'memory_spread_page' is set, then the
kernel will spread the file system buffers (page cache) evenly over all the
nodes that the faulting task is allowed to use, instead of preferring to put
those pages on the node where the task is running.
If the per-cpuset boolean flag file 'memory_spread_slab' is set, then the
kernel will spread some file system related slab caches, such as for inodes
and dentries evenly over all the nodes that the faulting task is allowed to
use, instead of preferring to put those pages on the node where the task is
running.
The implementation is simple. Setting the cpuset flags 'memory_spread_page'
or 'memory_spread_cache' turns on the per-process flags PF_SPREAD_PAGE or
PF_SPREAD_SLAB, respectively, for each task that is in the cpuset or
subsequently joins that cpuset. In subsequent patches, the page allocation
calls for the affected page cache and slab caches are modified to perform an
inline check for these flags, and if set, a call to a new routine
cpuset_mem_spread_node() returns the node to prefer for the allocation.
The cpuset_mem_spread_node() routine is also simple. It uses the value of a
per-task rotor cpuset_mem_spread_rotor to select the next node in the current
tasks mems_allowed to prefer for the allocation.
This policy can provide substantial improvements for jobs that need to place
thread local data on the corresponding node, but that need to access large
file system data sets that need to be spread across the several nodes in the
jobs cpuset in order to fit. Without this patch, especially for jobs that
might have one thread reading in the data set, the memory allocation across
the nodes in the jobs cpuset can become very uneven.
A couple of Copyright year ranges are updated as well. And a couple of email
addresses that can be found in the MAINTAINERS file are removed.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-03-24 14:16:03 +03:00
|
|
|
break;
|
|
|
|
case FILE_SPREAD_SLAB:
|
2008-04-29 12:00:00 +04:00
|
|
|
retval = update_flag(CS_SPREAD_SLAB, cs, val);
|
2006-03-24 14:16:11 +03:00
|
|
|
cs->mems_generation = cpuset_mems_generation++;
|
[PATCH] cpuset memory spread basic implementation
This patch provides the implementation and cpuset interface for an alternative
memory allocation policy that can be applied to certain kinds of memory
allocations, such as the page cache (file system buffers) and some slab caches
(such as inode caches).
The policy is called "memory spreading." If enabled, it spreads out these
kinds of memory allocations over all the nodes allowed to a task, instead of
preferring to place them on the node where the task is executing.
All other kinds of allocations, including anonymous pages for a tasks stack
and data regions, are not affected by this policy choice, and continue to be
allocated preferring the node local to execution, as modified by the NUMA
mempolicy.
There are two boolean flag files per cpuset that control where the kernel
allocates pages for the file system buffers and related in kernel data
structures. They are called 'memory_spread_page' and 'memory_spread_slab'.
If the per-cpuset boolean flag file 'memory_spread_page' is set, then the
kernel will spread the file system buffers (page cache) evenly over all the
nodes that the faulting task is allowed to use, instead of preferring to put
those pages on the node where the task is running.
If the per-cpuset boolean flag file 'memory_spread_slab' is set, then the
kernel will spread some file system related slab caches, such as for inodes
and dentries evenly over all the nodes that the faulting task is allowed to
use, instead of preferring to put those pages on the node where the task is
running.
The implementation is simple. Setting the cpuset flags 'memory_spread_page'
or 'memory_spread_cache' turns on the per-process flags PF_SPREAD_PAGE or
PF_SPREAD_SLAB, respectively, for each task that is in the cpuset or
subsequently joins that cpuset. In subsequent patches, the page allocation
calls for the affected page cache and slab caches are modified to perform an
inline check for these flags, and if set, a call to a new routine
cpuset_mem_spread_node() returns the node to prefer for the allocation.
The cpuset_mem_spread_node() routine is also simple. It uses the value of a
per-task rotor cpuset_mem_spread_rotor to select the next node in the current
tasks mems_allowed to prefer for the allocation.
This policy can provide substantial improvements for jobs that need to place
thread local data on the corresponding node, but that need to access large
file system data sets that need to be spread across the several nodes in the
jobs cpuset in order to fit. Without this patch, especially for jobs that
might have one thread reading in the data set, the memory allocation across
the nodes in the jobs cpuset can become very uneven.
A couple of Copyright year ranges are updated as well. And a couple of email
addresses that can be found in the MAINTAINERS file are removed.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-03-24 14:16:03 +03:00
|
|
|
break;
|
2005-04-17 02:20:36 +04:00
|
|
|
default:
|
|
|
|
retval = -EINVAL;
|
2008-04-29 12:00:00 +04:00
|
|
|
break;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
2007-10-19 10:39:39 +04:00
|
|
|
cgroup_unlock();
|
2005-04-17 02:20:36 +04:00
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2008-05-07 07:42:41 +04:00
|
|
|
static int cpuset_write_s64(struct cgroup *cgrp, struct cftype *cft, s64 val)
|
|
|
|
{
|
|
|
|
int retval = 0;
|
|
|
|
struct cpuset *cs = cgroup_cs(cgrp);
|
|
|
|
cpuset_filetype_t type = cft->private;
|
|
|
|
|
|
|
|
cgroup_lock();
|
|
|
|
|
|
|
|
if (cgroup_is_removed(cgrp)) {
|
|
|
|
cgroup_unlock();
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
switch (type) {
|
|
|
|
case FILE_SCHED_RELAX_DOMAIN_LEVEL:
|
|
|
|
retval = update_relax_domain_level(cs, val);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
retval = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
cgroup_unlock();
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
/*
|
|
|
|
* These ascii lists should be read in a single call, by using a user
|
|
|
|
* buffer large enough to hold the entire map. If read in smaller
|
|
|
|
* chunks, there is no guarantee of atomicity. Since the display format
|
|
|
|
* used, list of ranges of sequential numbers, is variable length,
|
|
|
|
* and since these maps can change value dynamically, one could read
|
|
|
|
* gibberish by doing partial reads while a list was changing.
|
|
|
|
* A single large read to a buffer that crosses a page boundary is
|
|
|
|
* ok, because the result being copied to user land is not recomputed
|
|
|
|
* across a page fault.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int cpuset_sprintf_cpulist(char *page, struct cpuset *cs)
|
|
|
|
{
|
|
|
|
cpumask_t mask;
|
|
|
|
|
2006-03-23 14:00:18 +03:00
|
|
|
mutex_lock(&callback_mutex);
|
2005-04-17 02:20:36 +04:00
|
|
|
mask = cs->cpus_allowed;
|
2006-03-23 14:00:18 +03:00
|
|
|
mutex_unlock(&callback_mutex);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
return cpulist_scnprintf(page, PAGE_SIZE, mask);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cpuset_sprintf_memlist(char *page, struct cpuset *cs)
|
|
|
|
{
|
|
|
|
nodemask_t mask;
|
|
|
|
|
2006-03-23 14:00:18 +03:00
|
|
|
mutex_lock(&callback_mutex);
|
2005-04-17 02:20:36 +04:00
|
|
|
mask = cs->mems_allowed;
|
2006-03-23 14:00:18 +03:00
|
|
|
mutex_unlock(&callback_mutex);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
return nodelist_scnprintf(page, PAGE_SIZE, mask);
|
|
|
|
}
|
|
|
|
|
2007-10-19 10:39:39 +04:00
|
|
|
static ssize_t cpuset_common_file_read(struct cgroup *cont,
|
|
|
|
struct cftype *cft,
|
|
|
|
struct file *file,
|
|
|
|
char __user *buf,
|
|
|
|
size_t nbytes, loff_t *ppos)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2007-10-19 10:39:39 +04:00
|
|
|
struct cpuset *cs = cgroup_cs(cont);
|
2005-04-17 02:20:36 +04:00
|
|
|
cpuset_filetype_t type = cft->private;
|
|
|
|
char *page;
|
|
|
|
ssize_t retval = 0;
|
|
|
|
char *s;
|
|
|
|
|
2007-10-16 12:25:52 +04:00
|
|
|
if (!(page = (char *)__get_free_page(GFP_TEMPORARY)))
|
2005-04-17 02:20:36 +04:00
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
s = page;
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case FILE_CPULIST:
|
|
|
|
s += cpuset_sprintf_cpulist(s, cs);
|
|
|
|
break;
|
|
|
|
case FILE_MEMLIST:
|
|
|
|
s += cpuset_sprintf_memlist(s, cs);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
retval = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
*s++ = '\n';
|
|
|
|
|
2005-09-30 06:26:43 +04:00
|
|
|
retval = simple_read_from_buffer(buf, nbytes, ppos, page, s - page);
|
2005-04-17 02:20:36 +04:00
|
|
|
out:
|
|
|
|
free_page((unsigned long)page);
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2008-04-29 12:00:00 +04:00
|
|
|
static u64 cpuset_read_u64(struct cgroup *cont, struct cftype *cft)
|
|
|
|
{
|
|
|
|
struct cpuset *cs = cgroup_cs(cont);
|
|
|
|
cpuset_filetype_t type = cft->private;
|
|
|
|
switch (type) {
|
|
|
|
case FILE_CPU_EXCLUSIVE:
|
|
|
|
return is_cpu_exclusive(cs);
|
|
|
|
case FILE_MEM_EXCLUSIVE:
|
|
|
|
return is_mem_exclusive(cs);
|
2008-04-29 12:00:26 +04:00
|
|
|
case FILE_MEM_HARDWALL:
|
|
|
|
return is_mem_hardwall(cs);
|
2008-04-29 12:00:00 +04:00
|
|
|
case FILE_SCHED_LOAD_BALANCE:
|
|
|
|
return is_sched_load_balance(cs);
|
|
|
|
case FILE_MEMORY_MIGRATE:
|
|
|
|
return is_memory_migrate(cs);
|
|
|
|
case FILE_MEMORY_PRESSURE_ENABLED:
|
|
|
|
return cpuset_memory_pressure_enabled;
|
|
|
|
case FILE_MEMORY_PRESSURE:
|
|
|
|
return fmeter_getrate(&cs->fmeter);
|
|
|
|
case FILE_SPREAD_PAGE:
|
|
|
|
return is_spread_page(cs);
|
|
|
|
case FILE_SPREAD_SLAB:
|
|
|
|
return is_spread_slab(cs);
|
|
|
|
default:
|
|
|
|
BUG();
|
|
|
|
}
|
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2008-05-07 07:42:41 +04:00
|
|
|
static s64 cpuset_read_s64(struct cgroup *cont, struct cftype *cft)
|
|
|
|
{
|
|
|
|
struct cpuset *cs = cgroup_cs(cont);
|
|
|
|
cpuset_filetype_t type = cft->private;
|
|
|
|
switch (type) {
|
|
|
|
case FILE_SCHED_RELAX_DOMAIN_LEVEL:
|
|
|
|
return cs->relax_domain_level;
|
|
|
|
default:
|
|
|
|
BUG();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* for the common functions, 'private' gives the type of file
|
|
|
|
*/
|
|
|
|
|
2008-04-29 12:00:26 +04:00
|
|
|
static struct cftype files[] = {
|
|
|
|
{
|
|
|
|
.name = "cpus",
|
|
|
|
.read = cpuset_common_file_read,
|
|
|
|
.write = cpuset_common_file_write,
|
|
|
|
.private = FILE_CPULIST,
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
.name = "mems",
|
|
|
|
.read = cpuset_common_file_read,
|
|
|
|
.write = cpuset_common_file_write,
|
|
|
|
.private = FILE_MEMLIST,
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
.name = "cpu_exclusive",
|
|
|
|
.read_u64 = cpuset_read_u64,
|
|
|
|
.write_u64 = cpuset_write_u64,
|
|
|
|
.private = FILE_CPU_EXCLUSIVE,
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
.name = "mem_exclusive",
|
|
|
|
.read_u64 = cpuset_read_u64,
|
|
|
|
.write_u64 = cpuset_write_u64,
|
|
|
|
.private = FILE_MEM_EXCLUSIVE,
|
|
|
|
},
|
|
|
|
|
2008-04-29 12:00:26 +04:00
|
|
|
{
|
|
|
|
.name = "mem_hardwall",
|
|
|
|
.read_u64 = cpuset_read_u64,
|
|
|
|
.write_u64 = cpuset_write_u64,
|
|
|
|
.private = FILE_MEM_HARDWALL,
|
|
|
|
},
|
|
|
|
|
2008-04-29 12:00:26 +04:00
|
|
|
{
|
|
|
|
.name = "sched_load_balance",
|
|
|
|
.read_u64 = cpuset_read_u64,
|
|
|
|
.write_u64 = cpuset_write_u64,
|
|
|
|
.private = FILE_SCHED_LOAD_BALANCE,
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
.name = "sched_relax_domain_level",
|
2008-05-07 07:42:41 +04:00
|
|
|
.read_s64 = cpuset_read_s64,
|
|
|
|
.write_s64 = cpuset_write_s64,
|
2008-04-29 12:00:26 +04:00
|
|
|
.private = FILE_SCHED_RELAX_DOMAIN_LEVEL,
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
.name = "memory_migrate",
|
|
|
|
.read_u64 = cpuset_read_u64,
|
|
|
|
.write_u64 = cpuset_write_u64,
|
|
|
|
.private = FILE_MEMORY_MIGRATE,
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
.name = "memory_pressure",
|
|
|
|
.read_u64 = cpuset_read_u64,
|
|
|
|
.write_u64 = cpuset_write_u64,
|
|
|
|
.private = FILE_MEMORY_PRESSURE,
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
.name = "memory_spread_page",
|
|
|
|
.read_u64 = cpuset_read_u64,
|
|
|
|
.write_u64 = cpuset_write_u64,
|
|
|
|
.private = FILE_SPREAD_PAGE,
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
.name = "memory_spread_slab",
|
|
|
|
.read_u64 = cpuset_read_u64,
|
|
|
|
.write_u64 = cpuset_write_u64,
|
|
|
|
.private = FILE_SPREAD_SLAB,
|
|
|
|
},
|
[PATCH] cpusets: swap migration interface
Add a boolean "memory_migrate" to each cpuset, represented by a file
containing "0" or "1" in each directory below /dev/cpuset.
It defaults to false (file contains "0"). It can be set true by writing
"1" to the file.
If true, then anytime that a task is attached to the cpuset so marked, the
pages of that task will be moved to that cpuset, preserving, to the extent
practical, the cpuset-relative placement of the pages.
Also anytime that a cpuset so marked has its memory placement changed (by
writing to its "mems" file), the tasks in that cpuset will have their pages
moved to the cpusets new nodes, preserving, to the extent practical, the
cpuset-relative placement of the moved pages.
Signed-off-by: Paul Jackson <pj@sgi.com>
Cc: Christoph Lameter <christoph@lameter.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 12:00:56 +03:00
|
|
|
};
|
|
|
|
|
[PATCH] cpuset: memory pressure meter
Provide a simple per-cpuset metric of memory pressure, tracking the -rate-
that the tasks in a cpuset call try_to_free_pages(), the synchronous
(direct) memory reclaim code.
This enables batch managers monitoring jobs running in dedicated cpusets to
efficiently detect what level of memory pressure that job is causing.
This is useful both on tightly managed systems running a wide mix of
submitted jobs, which may choose to terminate or reprioritize jobs that are
trying to use more memory than allowed on the nodes assigned them, and with
tightly coupled, long running, massively parallel scientific computing jobs
that will dramatically fail to meet required performance goals if they
start to use more memory than allowed to them.
This patch just provides a very economical way for the batch manager to
monitor a cpuset for signs of memory pressure. It's up to the batch
manager or other user code to decide what to do about it and take action.
==> Unless this feature is enabled by writing "1" to the special file
/dev/cpuset/memory_pressure_enabled, the hook in the rebalance
code of __alloc_pages() for this metric reduces to simply noticing
that the cpuset_memory_pressure_enabled flag is zero. So only
systems that enable this feature will compute the metric.
Why a per-cpuset, running average:
Because this meter is per-cpuset, rather than per-task or mm, the
system load imposed by a batch scheduler monitoring this metric is
sharply reduced on large systems, because a scan of the tasklist can be
avoided on each set of queries.
Because this meter is a running average, instead of an accumulating
counter, a batch scheduler can detect memory pressure with a single
read, instead of having to read and accumulate results for a period of
time.
Because this meter is per-cpuset rather than per-task or mm, the
batch scheduler can obtain the key information, memory pressure in a
cpuset, with a single read, rather than having to query and accumulate
results over all the (dynamically changing) set of tasks in the cpuset.
A per-cpuset simple digital filter (requires a spinlock and 3 words of data
per-cpuset) is kept, and updated by any task attached to that cpuset, if it
enters the synchronous (direct) page reclaim code.
A per-cpuset file provides an integer number representing the recent
(half-life of 10 seconds) rate of direct page reclaims caused by the tasks
in the cpuset, in units of reclaims attempted per second, times 1000.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 12:01:49 +03:00
|
|
|
static struct cftype cft_memory_pressure_enabled = {
|
|
|
|
.name = "memory_pressure_enabled",
|
2008-04-29 12:00:00 +04:00
|
|
|
.read_u64 = cpuset_read_u64,
|
|
|
|
.write_u64 = cpuset_write_u64,
|
[PATCH] cpuset: memory pressure meter
Provide a simple per-cpuset metric of memory pressure, tracking the -rate-
that the tasks in a cpuset call try_to_free_pages(), the synchronous
(direct) memory reclaim code.
This enables batch managers monitoring jobs running in dedicated cpusets to
efficiently detect what level of memory pressure that job is causing.
This is useful both on tightly managed systems running a wide mix of
submitted jobs, which may choose to terminate or reprioritize jobs that are
trying to use more memory than allowed on the nodes assigned them, and with
tightly coupled, long running, massively parallel scientific computing jobs
that will dramatically fail to meet required performance goals if they
start to use more memory than allowed to them.
This patch just provides a very economical way for the batch manager to
monitor a cpuset for signs of memory pressure. It's up to the batch
manager or other user code to decide what to do about it and take action.
==> Unless this feature is enabled by writing "1" to the special file
/dev/cpuset/memory_pressure_enabled, the hook in the rebalance
code of __alloc_pages() for this metric reduces to simply noticing
that the cpuset_memory_pressure_enabled flag is zero. So only
systems that enable this feature will compute the metric.
Why a per-cpuset, running average:
Because this meter is per-cpuset, rather than per-task or mm, the
system load imposed by a batch scheduler monitoring this metric is
sharply reduced on large systems, because a scan of the tasklist can be
avoided on each set of queries.
Because this meter is a running average, instead of an accumulating
counter, a batch scheduler can detect memory pressure with a single
read, instead of having to read and accumulate results for a period of
time.
Because this meter is per-cpuset rather than per-task or mm, the
batch scheduler can obtain the key information, memory pressure in a
cpuset, with a single read, rather than having to query and accumulate
results over all the (dynamically changing) set of tasks in the cpuset.
A per-cpuset simple digital filter (requires a spinlock and 3 words of data
per-cpuset) is kept, and updated by any task attached to that cpuset, if it
enters the synchronous (direct) page reclaim code.
A per-cpuset file provides an integer number representing the recent
(half-life of 10 seconds) rate of direct page reclaims caused by the tasks
in the cpuset, in units of reclaims attempted per second, times 1000.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 12:01:49 +03:00
|
|
|
.private = FILE_MEMORY_PRESSURE_ENABLED,
|
|
|
|
};
|
|
|
|
|
2007-10-19 10:39:39 +04:00
|
|
|
static int cpuset_populate(struct cgroup_subsys *ss, struct cgroup *cont)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
2008-04-29 12:00:26 +04:00
|
|
|
err = cgroup_add_files(cont, ss, files, ARRAY_SIZE(files));
|
|
|
|
if (err)
|
2005-04-17 02:20:36 +04:00
|
|
|
return err;
|
2007-10-19 10:39:39 +04:00
|
|
|
/* memory_pressure_enabled is in root cpuset only */
|
2008-04-29 12:00:26 +04:00
|
|
|
if (!cont->parent)
|
2007-10-19 10:39:39 +04:00
|
|
|
err = cgroup_add_file(cont, ss,
|
2008-04-29 12:00:26 +04:00
|
|
|
&cft_memory_pressure_enabled);
|
|
|
|
return err;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
2007-10-19 10:39:39 +04:00
|
|
|
/*
|
|
|
|
* post_clone() is called at the end of cgroup_clone().
|
|
|
|
* 'cgroup' was just created automatically as a result of
|
|
|
|
* a cgroup_clone(), and the current task is about to
|
|
|
|
* be moved into 'cgroup'.
|
|
|
|
*
|
|
|
|
* Currently we refuse to set up the cgroup - thereby
|
|
|
|
* refusing the task to be entered, and as a result refusing
|
|
|
|
* the sys_unshare() or clone() which initiated it - if any
|
|
|
|
* sibling cpusets have exclusive cpus or mem.
|
|
|
|
*
|
|
|
|
* If this becomes a problem for some users who wish to
|
|
|
|
* allow that scenario, then cpuset_post_clone() could be
|
|
|
|
* changed to grant parent->cpus_allowed-sibling_cpus_exclusive
|
2008-02-07 11:14:45 +03:00
|
|
|
* (and likewise for mems) to the new cgroup. Called with cgroup_mutex
|
|
|
|
* held.
|
2007-10-19 10:39:39 +04:00
|
|
|
*/
|
|
|
|
static void cpuset_post_clone(struct cgroup_subsys *ss,
|
|
|
|
struct cgroup *cgroup)
|
|
|
|
{
|
|
|
|
struct cgroup *parent, *child;
|
|
|
|
struct cpuset *cs, *parent_cs;
|
|
|
|
|
|
|
|
parent = cgroup->parent;
|
|
|
|
list_for_each_entry(child, &parent->children, sibling) {
|
|
|
|
cs = cgroup_cs(child);
|
|
|
|
if (is_mem_exclusive(cs) || is_cpu_exclusive(cs))
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
cs = cgroup_cs(cgroup);
|
|
|
|
parent_cs = cgroup_cs(parent);
|
|
|
|
|
|
|
|
cs->mems_allowed = parent_cs->mems_allowed;
|
|
|
|
cs->cpus_allowed = parent_cs->cpus_allowed;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
/*
|
|
|
|
* cpuset_create - create a cpuset
|
2008-02-07 11:14:45 +03:00
|
|
|
* ss: cpuset cgroup subsystem
|
|
|
|
* cont: control group that the new cpuset will be part of
|
2005-04-17 02:20:36 +04:00
|
|
|
*/
|
|
|
|
|
2007-10-19 10:39:39 +04:00
|
|
|
static struct cgroup_subsys_state *cpuset_create(
|
|
|
|
struct cgroup_subsys *ss,
|
|
|
|
struct cgroup *cont)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
struct cpuset *cs;
|
2007-10-19 10:39:39 +04:00
|
|
|
struct cpuset *parent;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2007-10-19 10:39:39 +04:00
|
|
|
if (!cont->parent) {
|
|
|
|
/* This is early initialization for the top cgroup */
|
|
|
|
top_cpuset.mems_generation = cpuset_mems_generation++;
|
|
|
|
return &top_cpuset.css;
|
|
|
|
}
|
|
|
|
parent = cgroup_cs(cont->parent);
|
2005-04-17 02:20:36 +04:00
|
|
|
cs = kmalloc(sizeof(*cs), GFP_KERNEL);
|
|
|
|
if (!cs)
|
2007-10-19 10:39:39 +04:00
|
|
|
return ERR_PTR(-ENOMEM);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2006-01-08 12:01:54 +03:00
|
|
|
cpuset_update_task_memory_state();
|
2005-04-17 02:20:36 +04:00
|
|
|
cs->flags = 0;
|
[PATCH] cpuset memory spread basic implementation
This patch provides the implementation and cpuset interface for an alternative
memory allocation policy that can be applied to certain kinds of memory
allocations, such as the page cache (file system buffers) and some slab caches
(such as inode caches).
The policy is called "memory spreading." If enabled, it spreads out these
kinds of memory allocations over all the nodes allowed to a task, instead of
preferring to place them on the node where the task is executing.
All other kinds of allocations, including anonymous pages for a tasks stack
and data regions, are not affected by this policy choice, and continue to be
allocated preferring the node local to execution, as modified by the NUMA
mempolicy.
There are two boolean flag files per cpuset that control where the kernel
allocates pages for the file system buffers and related in kernel data
structures. They are called 'memory_spread_page' and 'memory_spread_slab'.
If the per-cpuset boolean flag file 'memory_spread_page' is set, then the
kernel will spread the file system buffers (page cache) evenly over all the
nodes that the faulting task is allowed to use, instead of preferring to put
those pages on the node where the task is running.
If the per-cpuset boolean flag file 'memory_spread_slab' is set, then the
kernel will spread some file system related slab caches, such as for inodes
and dentries evenly over all the nodes that the faulting task is allowed to
use, instead of preferring to put those pages on the node where the task is
running.
The implementation is simple. Setting the cpuset flags 'memory_spread_page'
or 'memory_spread_cache' turns on the per-process flags PF_SPREAD_PAGE or
PF_SPREAD_SLAB, respectively, for each task that is in the cpuset or
subsequently joins that cpuset. In subsequent patches, the page allocation
calls for the affected page cache and slab caches are modified to perform an
inline check for these flags, and if set, a call to a new routine
cpuset_mem_spread_node() returns the node to prefer for the allocation.
The cpuset_mem_spread_node() routine is also simple. It uses the value of a
per-task rotor cpuset_mem_spread_rotor to select the next node in the current
tasks mems_allowed to prefer for the allocation.
This policy can provide substantial improvements for jobs that need to place
thread local data on the corresponding node, but that need to access large
file system data sets that need to be spread across the several nodes in the
jobs cpuset in order to fit. Without this patch, especially for jobs that
might have one thread reading in the data set, the memory allocation across
the nodes in the jobs cpuset can become very uneven.
A couple of Copyright year ranges are updated as well. And a couple of email
addresses that can be found in the MAINTAINERS file are removed.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-03-24 14:16:03 +03:00
|
|
|
if (is_spread_page(parent))
|
|
|
|
set_bit(CS_SPREAD_PAGE, &cs->flags);
|
|
|
|
if (is_spread_slab(parent))
|
|
|
|
set_bit(CS_SPREAD_SLAB, &cs->flags);
|
2007-10-19 10:40:20 +04:00
|
|
|
set_bit(CS_SCHED_LOAD_BALANCE, &cs->flags);
|
2008-04-05 05:11:07 +04:00
|
|
|
cpus_clear(cs->cpus_allowed);
|
|
|
|
nodes_clear(cs->mems_allowed);
|
2006-03-24 14:16:11 +03:00
|
|
|
cs->mems_generation = cpuset_mems_generation++;
|
[PATCH] cpuset: memory pressure meter
Provide a simple per-cpuset metric of memory pressure, tracking the -rate-
that the tasks in a cpuset call try_to_free_pages(), the synchronous
(direct) memory reclaim code.
This enables batch managers monitoring jobs running in dedicated cpusets to
efficiently detect what level of memory pressure that job is causing.
This is useful both on tightly managed systems running a wide mix of
submitted jobs, which may choose to terminate or reprioritize jobs that are
trying to use more memory than allowed on the nodes assigned them, and with
tightly coupled, long running, massively parallel scientific computing jobs
that will dramatically fail to meet required performance goals if they
start to use more memory than allowed to them.
This patch just provides a very economical way for the batch manager to
monitor a cpuset for signs of memory pressure. It's up to the batch
manager or other user code to decide what to do about it and take action.
==> Unless this feature is enabled by writing "1" to the special file
/dev/cpuset/memory_pressure_enabled, the hook in the rebalance
code of __alloc_pages() for this metric reduces to simply noticing
that the cpuset_memory_pressure_enabled flag is zero. So only
systems that enable this feature will compute the metric.
Why a per-cpuset, running average:
Because this meter is per-cpuset, rather than per-task or mm, the
system load imposed by a batch scheduler monitoring this metric is
sharply reduced on large systems, because a scan of the tasklist can be
avoided on each set of queries.
Because this meter is a running average, instead of an accumulating
counter, a batch scheduler can detect memory pressure with a single
read, instead of having to read and accumulate results for a period of
time.
Because this meter is per-cpuset rather than per-task or mm, the
batch scheduler can obtain the key information, memory pressure in a
cpuset, with a single read, rather than having to query and accumulate
results over all the (dynamically changing) set of tasks in the cpuset.
A per-cpuset simple digital filter (requires a spinlock and 3 words of data
per-cpuset) is kept, and updated by any task attached to that cpuset, if it
enters the synchronous (direct) page reclaim code.
A per-cpuset file provides an integer number representing the recent
(half-life of 10 seconds) rate of direct page reclaims caused by the tasks
in the cpuset, in units of reclaims attempted per second, times 1000.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 12:01:49 +03:00
|
|
|
fmeter_init(&cs->fmeter);
|
2008-04-15 09:04:23 +04:00
|
|
|
cs->relax_domain_level = -1;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
cs->parent = parent;
|
2006-01-08 12:01:57 +03:00
|
|
|
number_of_cpusets++;
|
2007-10-19 10:39:39 +04:00
|
|
|
return &cs->css ;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
2007-10-19 10:40:20 +04:00
|
|
|
/*
|
|
|
|
* Locking note on the strange update_flag() call below:
|
|
|
|
*
|
|
|
|
* If the cpuset being removed has its flag 'sched_load_balance'
|
|
|
|
* enabled, then simulate turning sched_load_balance off, which
|
2008-01-25 23:08:02 +03:00
|
|
|
* will call rebuild_sched_domains(). The get_online_cpus()
|
2007-10-19 10:40:20 +04:00
|
|
|
* call in rebuild_sched_domains() must not be made while holding
|
|
|
|
* callback_mutex. Elsewhere the kernel nests callback_mutex inside
|
2008-01-25 23:08:02 +03:00
|
|
|
* get_online_cpus() calls. So the reverse nesting would risk an
|
2007-10-19 10:40:20 +04:00
|
|
|
* ABBA deadlock.
|
|
|
|
*/
|
|
|
|
|
2007-10-19 10:39:39 +04:00
|
|
|
static void cpuset_destroy(struct cgroup_subsys *ss, struct cgroup *cont)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2007-10-19 10:39:39 +04:00
|
|
|
struct cpuset *cs = cgroup_cs(cont);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2006-01-08 12:01:54 +03:00
|
|
|
cpuset_update_task_memory_state();
|
2007-10-19 10:40:20 +04:00
|
|
|
|
|
|
|
if (is_sched_load_balance(cs))
|
2008-04-29 12:00:00 +04:00
|
|
|
update_flag(CS_SCHED_LOAD_BALANCE, cs, 0);
|
2007-10-19 10:40:20 +04:00
|
|
|
|
2006-01-08 12:01:57 +03:00
|
|
|
number_of_cpusets--;
|
2007-10-19 10:39:39 +04:00
|
|
|
kfree(cs);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
2007-10-19 10:39:39 +04:00
|
|
|
struct cgroup_subsys cpuset_subsys = {
|
|
|
|
.name = "cpuset",
|
|
|
|
.create = cpuset_create,
|
|
|
|
.destroy = cpuset_destroy,
|
|
|
|
.can_attach = cpuset_can_attach,
|
|
|
|
.attach = cpuset_attach,
|
|
|
|
.populate = cpuset_populate,
|
|
|
|
.post_clone = cpuset_post_clone,
|
|
|
|
.subsys_id = cpuset_subsys_id,
|
|
|
|
.early_init = 1,
|
|
|
|
};
|
|
|
|
|
2006-01-08 12:02:01 +03:00
|
|
|
/*
|
|
|
|
* cpuset_init_early - just enough so that the calls to
|
|
|
|
* cpuset_update_task_memory_state() in early init code
|
|
|
|
* are harmless.
|
|
|
|
*/
|
|
|
|
|
|
|
|
int __init cpuset_init_early(void)
|
|
|
|
{
|
2007-10-19 10:39:39 +04:00
|
|
|
top_cpuset.mems_generation = cpuset_mems_generation++;
|
2006-01-08 12:02:01 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-10-19 10:39:39 +04:00
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
/**
|
|
|
|
* cpuset_init - initialize cpusets at system boot
|
|
|
|
*
|
|
|
|
* Description: Initialize top_cpuset and the cpuset internal file system,
|
|
|
|
**/
|
|
|
|
|
|
|
|
int __init cpuset_init(void)
|
|
|
|
{
|
2007-10-19 10:39:39 +04:00
|
|
|
int err = 0;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2008-04-05 05:11:07 +04:00
|
|
|
cpus_setall(top_cpuset.cpus_allowed);
|
|
|
|
nodes_setall(top_cpuset.mems_allowed);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
[PATCH] cpuset: memory pressure meter
Provide a simple per-cpuset metric of memory pressure, tracking the -rate-
that the tasks in a cpuset call try_to_free_pages(), the synchronous
(direct) memory reclaim code.
This enables batch managers monitoring jobs running in dedicated cpusets to
efficiently detect what level of memory pressure that job is causing.
This is useful both on tightly managed systems running a wide mix of
submitted jobs, which may choose to terminate or reprioritize jobs that are
trying to use more memory than allowed on the nodes assigned them, and with
tightly coupled, long running, massively parallel scientific computing jobs
that will dramatically fail to meet required performance goals if they
start to use more memory than allowed to them.
This patch just provides a very economical way for the batch manager to
monitor a cpuset for signs of memory pressure. It's up to the batch
manager or other user code to decide what to do about it and take action.
==> Unless this feature is enabled by writing "1" to the special file
/dev/cpuset/memory_pressure_enabled, the hook in the rebalance
code of __alloc_pages() for this metric reduces to simply noticing
that the cpuset_memory_pressure_enabled flag is zero. So only
systems that enable this feature will compute the metric.
Why a per-cpuset, running average:
Because this meter is per-cpuset, rather than per-task or mm, the
system load imposed by a batch scheduler monitoring this metric is
sharply reduced on large systems, because a scan of the tasklist can be
avoided on each set of queries.
Because this meter is a running average, instead of an accumulating
counter, a batch scheduler can detect memory pressure with a single
read, instead of having to read and accumulate results for a period of
time.
Because this meter is per-cpuset rather than per-task or mm, the
batch scheduler can obtain the key information, memory pressure in a
cpuset, with a single read, rather than having to query and accumulate
results over all the (dynamically changing) set of tasks in the cpuset.
A per-cpuset simple digital filter (requires a spinlock and 3 words of data
per-cpuset) is kept, and updated by any task attached to that cpuset, if it
enters the synchronous (direct) page reclaim code.
A per-cpuset file provides an integer number representing the recent
(half-life of 10 seconds) rate of direct page reclaims caused by the tasks
in the cpuset, in units of reclaims attempted per second, times 1000.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 12:01:49 +03:00
|
|
|
fmeter_init(&top_cpuset.fmeter);
|
2006-03-24 14:16:11 +03:00
|
|
|
top_cpuset.mems_generation = cpuset_mems_generation++;
|
2007-10-19 10:40:20 +04:00
|
|
|
set_bit(CS_SCHED_LOAD_BALANCE, &top_cpuset.flags);
|
2008-04-15 09:04:23 +04:00
|
|
|
top_cpuset.relax_domain_level = -1;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
err = register_filesystem(&cpuset_fs_type);
|
|
|
|
if (err < 0)
|
2007-10-19 10:39:39 +04:00
|
|
|
return err;
|
|
|
|
|
2006-01-08 12:01:57 +03:00
|
|
|
number_of_cpusets = 1;
|
2007-10-19 10:39:39 +04:00
|
|
|
return 0;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
2008-02-07 11:14:43 +03:00
|
|
|
/**
|
|
|
|
* cpuset_do_move_task - move a given task to another cpuset
|
|
|
|
* @tsk: pointer to task_struct the task to move
|
|
|
|
* @scan: struct cgroup_scanner contained in its struct cpuset_hotplug_scanner
|
|
|
|
*
|
|
|
|
* Called by cgroup_scan_tasks() for each task in a cgroup.
|
|
|
|
* Return nonzero to stop the walk through the tasks.
|
|
|
|
*/
|
2008-04-29 12:00:25 +04:00
|
|
|
static void cpuset_do_move_task(struct task_struct *tsk,
|
|
|
|
struct cgroup_scanner *scan)
|
2008-02-07 11:14:43 +03:00
|
|
|
{
|
|
|
|
struct cpuset_hotplug_scanner *chsp;
|
|
|
|
|
|
|
|
chsp = container_of(scan, struct cpuset_hotplug_scanner, scan);
|
|
|
|
cgroup_attach_task(chsp->to, tsk);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* move_member_tasks_to_cpuset - move tasks from one cpuset to another
|
|
|
|
* @from: cpuset in which the tasks currently reside
|
|
|
|
* @to: cpuset to which the tasks will be moved
|
|
|
|
*
|
hotplug cpu: move tasks in empty cpusets to parent various other fixes
Various minor formatting and comment tweaks to Cliff Wickman's
[PATCH_3_of_3]_cpusets__update_cpumask_revision.patch
I had had "iff", meaning "if and only if" in a comment. However, except for
ancient mathematicians, the abbreviation "iff" was a tad too cryptic. Cliff
changed it to "if", presumably figuring that the "iff" was a typo. However,
it was the "only if" half of the conjunction that was most interesting.
Reword to emphasis the "only if" aspect.
The locking comment for remove_tasks_in_empty_cpuset() was wrong; it said
callback_mutex had to be held on entry. The opposite is true.
Several mentions of attach_task() in comments needed to be
changed to cgroup_attach_task().
A comment about notify_on_release was no longer relevant,
as the line of code it had commented, namely:
set_bit(CS_RELEASED_RESOURCE, &parent->flags);
is no longer present in that place in the cpuset.c code.
Similarly a comment about notify_on_release before the
scan_for_empty_cpusets() routine was no longer relevant.
Removed extra parentheses and unnecessary return statement.
Renamed attach_task() to cpuset_attach() in various comments.
Removed comment about not needing memory migration, as it seems the migration
is done anyway, via the cpuset_attach() callback from cgroup_attach_task().
Signed-off-by: Paul Jackson <pj@sgi.com>
Acked-by: Cliff Wickman <cpw@sgi.com>
Cc: David Rientjes <rientjes@google.com>
Cc: Paul Menage <menage@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2008-02-07 11:14:46 +03:00
|
|
|
* Called with cgroup_mutex held
|
|
|
|
* callback_mutex must not be held, as cpuset_attach() will take it.
|
2008-02-07 11:14:43 +03:00
|
|
|
*
|
|
|
|
* The cgroup_scan_tasks() function will scan all the tasks in a cgroup,
|
|
|
|
* calling callback functions for each.
|
|
|
|
*/
|
|
|
|
static void move_member_tasks_to_cpuset(struct cpuset *from, struct cpuset *to)
|
|
|
|
{
|
|
|
|
struct cpuset_hotplug_scanner scan;
|
|
|
|
|
|
|
|
scan.scan.cg = from->css.cgroup;
|
|
|
|
scan.scan.test_task = NULL; /* select all tasks in cgroup */
|
|
|
|
scan.scan.process_task = cpuset_do_move_task;
|
|
|
|
scan.scan.heap = NULL;
|
|
|
|
scan.to = to->css.cgroup;
|
|
|
|
|
|
|
|
if (cgroup_scan_tasks((struct cgroup_scanner *)&scan))
|
|
|
|
printk(KERN_ERR "move_member_tasks_to_cpuset: "
|
|
|
|
"cgroup_scan_tasks failed\n");
|
|
|
|
}
|
|
|
|
|
2006-09-29 13:01:17 +04:00
|
|
|
/*
|
|
|
|
* If common_cpu_mem_hotplug_unplug(), below, unplugs any CPUs
|
|
|
|
* or memory nodes, we need to walk over the cpuset hierarchy,
|
|
|
|
* removing that CPU or node from all cpusets. If this removes the
|
2008-02-07 11:14:43 +03:00
|
|
|
* last CPU or node from a cpuset, then move the tasks in the empty
|
|
|
|
* cpuset to its next-highest non-empty parent.
|
2006-09-29 13:01:17 +04:00
|
|
|
*
|
hotplug cpu: move tasks in empty cpusets to parent various other fixes
Various minor formatting and comment tweaks to Cliff Wickman's
[PATCH_3_of_3]_cpusets__update_cpumask_revision.patch
I had had "iff", meaning "if and only if" in a comment. However, except for
ancient mathematicians, the abbreviation "iff" was a tad too cryptic. Cliff
changed it to "if", presumably figuring that the "iff" was a typo. However,
it was the "only if" half of the conjunction that was most interesting.
Reword to emphasis the "only if" aspect.
The locking comment for remove_tasks_in_empty_cpuset() was wrong; it said
callback_mutex had to be held on entry. The opposite is true.
Several mentions of attach_task() in comments needed to be
changed to cgroup_attach_task().
A comment about notify_on_release was no longer relevant,
as the line of code it had commented, namely:
set_bit(CS_RELEASED_RESOURCE, &parent->flags);
is no longer present in that place in the cpuset.c code.
Similarly a comment about notify_on_release before the
scan_for_empty_cpusets() routine was no longer relevant.
Removed extra parentheses and unnecessary return statement.
Renamed attach_task() to cpuset_attach() in various comments.
Removed comment about not needing memory migration, as it seems the migration
is done anyway, via the cpuset_attach() callback from cgroup_attach_task().
Signed-off-by: Paul Jackson <pj@sgi.com>
Acked-by: Cliff Wickman <cpw@sgi.com>
Cc: David Rientjes <rientjes@google.com>
Cc: Paul Menage <menage@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2008-02-07 11:14:46 +03:00
|
|
|
* Called with cgroup_mutex held
|
|
|
|
* callback_mutex must not be held, as cpuset_attach() will take it.
|
2006-09-29 13:01:17 +04:00
|
|
|
*/
|
2008-02-07 11:14:43 +03:00
|
|
|
static void remove_tasks_in_empty_cpuset(struct cpuset *cs)
|
|
|
|
{
|
|
|
|
struct cpuset *parent;
|
|
|
|
|
hotplug cpu: move tasks in empty cpusets to parent various other fixes
Various minor formatting and comment tweaks to Cliff Wickman's
[PATCH_3_of_3]_cpusets__update_cpumask_revision.patch
I had had "iff", meaning "if and only if" in a comment. However, except for
ancient mathematicians, the abbreviation "iff" was a tad too cryptic. Cliff
changed it to "if", presumably figuring that the "iff" was a typo. However,
it was the "only if" half of the conjunction that was most interesting.
Reword to emphasis the "only if" aspect.
The locking comment for remove_tasks_in_empty_cpuset() was wrong; it said
callback_mutex had to be held on entry. The opposite is true.
Several mentions of attach_task() in comments needed to be
changed to cgroup_attach_task().
A comment about notify_on_release was no longer relevant,
as the line of code it had commented, namely:
set_bit(CS_RELEASED_RESOURCE, &parent->flags);
is no longer present in that place in the cpuset.c code.
Similarly a comment about notify_on_release before the
scan_for_empty_cpusets() routine was no longer relevant.
Removed extra parentheses and unnecessary return statement.
Renamed attach_task() to cpuset_attach() in various comments.
Removed comment about not needing memory migration, as it seems the migration
is done anyway, via the cpuset_attach() callback from cgroup_attach_task().
Signed-off-by: Paul Jackson <pj@sgi.com>
Acked-by: Cliff Wickman <cpw@sgi.com>
Cc: David Rientjes <rientjes@google.com>
Cc: Paul Menage <menage@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2008-02-07 11:14:46 +03:00
|
|
|
/*
|
|
|
|
* The cgroup's css_sets list is in use if there are tasks
|
|
|
|
* in the cpuset; the list is empty if there are none;
|
|
|
|
* the cs->css.refcnt seems always 0.
|
|
|
|
*/
|
2008-02-07 11:14:43 +03:00
|
|
|
if (list_empty(&cs->css.cgroup->css_sets))
|
|
|
|
return;
|
2006-09-29 13:01:17 +04:00
|
|
|
|
2008-02-07 11:14:43 +03:00
|
|
|
/*
|
|
|
|
* Find its next-highest non-empty parent, (top cpuset
|
|
|
|
* has online cpus, so can't be empty).
|
|
|
|
*/
|
|
|
|
parent = cs->parent;
|
2008-02-07 11:14:47 +03:00
|
|
|
while (cpus_empty(parent->cpus_allowed) ||
|
|
|
|
nodes_empty(parent->mems_allowed))
|
2008-02-07 11:14:43 +03:00
|
|
|
parent = parent->parent;
|
|
|
|
|
|
|
|
move_member_tasks_to_cpuset(cs, parent);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Walk the specified cpuset subtree and look for empty cpusets.
|
|
|
|
* The tasks of such cpuset must be moved to a parent cpuset.
|
|
|
|
*
|
2008-02-07 11:14:45 +03:00
|
|
|
* Called with cgroup_mutex held. We take callback_mutex to modify
|
2008-02-07 11:14:43 +03:00
|
|
|
* cpus_allowed and mems_allowed.
|
|
|
|
*
|
|
|
|
* This walk processes the tree from top to bottom, completing one layer
|
|
|
|
* before dropping down to the next. It always processes a node before
|
|
|
|
* any of its children.
|
|
|
|
*
|
|
|
|
* For now, since we lack memory hot unplug, we'll never see a cpuset
|
|
|
|
* that has tasks along with an empty 'mems'. But if we did see such
|
|
|
|
* a cpuset, we'd handle it just like we do if its 'cpus' was empty.
|
|
|
|
*/
|
|
|
|
static void scan_for_empty_cpusets(const struct cpuset *root)
|
2006-09-29 13:01:17 +04:00
|
|
|
{
|
2008-02-07 11:14:43 +03:00
|
|
|
struct cpuset *cp; /* scans cpusets being updated */
|
|
|
|
struct cpuset *child; /* scans child cpusets of cp */
|
|
|
|
struct list_head queue;
|
2007-10-19 10:39:39 +04:00
|
|
|
struct cgroup *cont;
|
2006-09-29 13:01:17 +04:00
|
|
|
|
2008-02-07 11:14:43 +03:00
|
|
|
INIT_LIST_HEAD(&queue);
|
|
|
|
|
|
|
|
list_add_tail((struct list_head *)&root->stack_list, &queue);
|
|
|
|
|
|
|
|
while (!list_empty(&queue)) {
|
|
|
|
cp = container_of(queue.next, struct cpuset, stack_list);
|
|
|
|
list_del(queue.next);
|
|
|
|
list_for_each_entry(cont, &cp->css.cgroup->children, sibling) {
|
|
|
|
child = cgroup_cs(cont);
|
|
|
|
list_add_tail(&child->stack_list, &queue);
|
|
|
|
}
|
|
|
|
cont = cp->css.cgroup;
|
2008-02-07 11:14:47 +03:00
|
|
|
|
|
|
|
/* Continue past cpusets with all cpus, mems online */
|
|
|
|
if (cpus_subset(cp->cpus_allowed, cpu_online_map) &&
|
|
|
|
nodes_subset(cp->mems_allowed, node_states[N_HIGH_MEMORY]))
|
|
|
|
continue;
|
|
|
|
|
2008-02-07 11:14:43 +03:00
|
|
|
/* Remove offline cpus and mems from this cpuset. */
|
2008-02-07 11:14:47 +03:00
|
|
|
mutex_lock(&callback_mutex);
|
2008-02-07 11:14:43 +03:00
|
|
|
cpus_and(cp->cpus_allowed, cp->cpus_allowed, cpu_online_map);
|
|
|
|
nodes_and(cp->mems_allowed, cp->mems_allowed,
|
|
|
|
node_states[N_HIGH_MEMORY]);
|
2008-02-07 11:14:47 +03:00
|
|
|
mutex_unlock(&callback_mutex);
|
|
|
|
|
|
|
|
/* Move tasks from the empty cpuset to a parent */
|
hotplug cpu: move tasks in empty cpusets to parent various other fixes
Various minor formatting and comment tweaks to Cliff Wickman's
[PATCH_3_of_3]_cpusets__update_cpumask_revision.patch
I had had "iff", meaning "if and only if" in a comment. However, except for
ancient mathematicians, the abbreviation "iff" was a tad too cryptic. Cliff
changed it to "if", presumably figuring that the "iff" was a typo. However,
it was the "only if" half of the conjunction that was most interesting.
Reword to emphasis the "only if" aspect.
The locking comment for remove_tasks_in_empty_cpuset() was wrong; it said
callback_mutex had to be held on entry. The opposite is true.
Several mentions of attach_task() in comments needed to be
changed to cgroup_attach_task().
A comment about notify_on_release was no longer relevant,
as the line of code it had commented, namely:
set_bit(CS_RELEASED_RESOURCE, &parent->flags);
is no longer present in that place in the cpuset.c code.
Similarly a comment about notify_on_release before the
scan_for_empty_cpusets() routine was no longer relevant.
Removed extra parentheses and unnecessary return statement.
Renamed attach_task() to cpuset_attach() in various comments.
Removed comment about not needing memory migration, as it seems the migration
is done anyway, via the cpuset_attach() callback from cgroup_attach_task().
Signed-off-by: Paul Jackson <pj@sgi.com>
Acked-by: Cliff Wickman <cpw@sgi.com>
Cc: David Rientjes <rientjes@google.com>
Cc: Paul Menage <menage@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2008-02-07 11:14:46 +03:00
|
|
|
if (cpus_empty(cp->cpus_allowed) ||
|
2008-02-07 11:14:47 +03:00
|
|
|
nodes_empty(cp->mems_allowed))
|
2008-02-07 11:14:43 +03:00
|
|
|
remove_tasks_in_empty_cpuset(cp);
|
2006-09-29 13:01:17 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The cpus_allowed and mems_allowed nodemasks in the top_cpuset track
|
2007-10-16 12:25:38 +04:00
|
|
|
* cpu_online_map and node_states[N_HIGH_MEMORY]. Force the top cpuset to
|
2008-02-07 11:14:43 +03:00
|
|
|
* track what's online after any CPU or memory node hotplug or unplug event.
|
2006-09-29 13:01:17 +04:00
|
|
|
*
|
|
|
|
* Since there are two callers of this routine, one for CPU hotplug
|
|
|
|
* events and one for memory node hotplug events, we could have coded
|
|
|
|
* two separate routines here. We code it as a single common routine
|
|
|
|
* in order to minimize text size.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void common_cpu_mem_hotplug_unplug(void)
|
|
|
|
{
|
2007-10-19 10:39:39 +04:00
|
|
|
cgroup_lock();
|
2006-09-29 13:01:17 +04:00
|
|
|
|
|
|
|
top_cpuset.cpus_allowed = cpu_online_map;
|
2007-10-16 12:25:38 +04:00
|
|
|
top_cpuset.mems_allowed = node_states[N_HIGH_MEMORY];
|
2008-02-07 11:14:43 +03:00
|
|
|
scan_for_empty_cpusets(&top_cpuset);
|
2006-09-29 13:01:17 +04:00
|
|
|
|
2008-05-29 22:17:01 +04:00
|
|
|
/*
|
|
|
|
* Scheduler destroys domains on hotplug events.
|
|
|
|
* Rebuild them based on the current settings.
|
|
|
|
*/
|
|
|
|
rebuild_sched_domains();
|
|
|
|
|
2007-10-19 10:39:39 +04:00
|
|
|
cgroup_unlock();
|
2006-09-29 13:01:17 +04:00
|
|
|
}
|
|
|
|
|
[PATCH] cpuset: top_cpuset tracks hotplug changes to cpu_online_map
Change the list of cpus allowed to tasks in the top (root) cpuset to
dynamically track what cpus are online, using a CPU hotplug notifier. Make
this top cpus file read-only.
On systems that have cpusets configured in their kernel, but that aren't
actively using cpusets (for some distros, this covers the majority of
systems) all tasks end up in the top cpuset.
If that system does support CPU hotplug, then these tasks cannot make use
of CPUs that are added after system boot, because the CPUs are not allowed
in the top cpuset. This is a surprising regression over earlier kernels
that didn't have cpusets enabled.
In order to keep the behaviour of cpusets consistent between systems
actively making use of them and systems not using them, this patch changes
the behaviour of the 'cpus' file in the top (root) cpuset, making it read
only, and making it automatically track the value of cpu_online_map. Thus
tasks in the top cpuset will have automatic use of hot plugged CPUs allowed
by their cpuset.
Thanks to Anton Blanchard and Nathan Lynch for reporting this problem,
driving the fix, and earlier versions of this patch.
Signed-off-by: Paul Jackson <pj@sgi.com>
Cc: Nathan Lynch <ntl@pobox.com>
Cc: Anton Blanchard <anton@samba.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-08-27 12:23:51 +04:00
|
|
|
/*
|
|
|
|
* The top_cpuset tracks what CPUs and Memory Nodes are online,
|
|
|
|
* period. This is necessary in order to make cpusets transparent
|
|
|
|
* (of no affect) on systems that are actively using CPU hotplug
|
|
|
|
* but making no active use of cpusets.
|
|
|
|
*
|
[PATCH] cpuset: top_cpuset tracks hotplug changes to node_online_map
Change the list of memory nodes allowed to tasks in the top (root) nodeset
to dynamically track what cpus are online, using a call to a cpuset hook
from the memory hotplug code. Make this top cpus file read-only.
On systems that have cpusets configured in their kernel, but that aren't
actively using cpusets (for some distros, this covers the majority of
systems) all tasks end up in the top cpuset.
If that system does support memory hotplug, then these tasks cannot make
use of memory nodes that are added after system boot, because the memory
nodes are not allowed in the top cpuset. This is a surprising regression
over earlier kernels that didn't have cpusets enabled.
One key motivation for this change is to remain consistent with the
behaviour for the top_cpuset's 'cpus', which is also read-only, and which
automatically tracks the cpu_online_map.
This change also has the minor benefit that it fixes a long standing,
little noticed, minor bug in cpusets. The cpuset performance tweak to
short circuit the cpuset_zone_allowed() check on systems with just a single
cpuset (see 'number_of_cpusets', in linux/cpuset.h) meant that simply
changing the 'mems' of the top_cpuset had no affect, even though the change
(the write system call) appeared to succeed. With the following change,
that write to the 'mems' file fails -EACCES, and the 'mems' file stubbornly
refuses to be changed via user space writes. Thus no one should be mislead
into thinking they've changed the top_cpusets's 'mems' when in affect they
haven't.
In order to keep the behaviour of cpusets consistent between systems
actively making use of them and systems not using them, this patch changes
the behaviour of the 'mems' file in the top (root) cpuset, making it read
only, and making it automatically track the value of node_online_map. Thus
tasks in the top cpuset will have automatic use of hot plugged memory nodes
allowed by their cpuset.
[akpm@osdl.org: build fix]
[bunk@stusta.de: build fix]
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Adrian Bunk <bunk@stusta.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-09-29 13:01:16 +04:00
|
|
|
* This routine ensures that top_cpuset.cpus_allowed tracks
|
|
|
|
* cpu_online_map on each CPU hotplug (cpuhp) event.
|
[PATCH] cpuset: top_cpuset tracks hotplug changes to cpu_online_map
Change the list of cpus allowed to tasks in the top (root) cpuset to
dynamically track what cpus are online, using a CPU hotplug notifier. Make
this top cpus file read-only.
On systems that have cpusets configured in their kernel, but that aren't
actively using cpusets (for some distros, this covers the majority of
systems) all tasks end up in the top cpuset.
If that system does support CPU hotplug, then these tasks cannot make use
of CPUs that are added after system boot, because the CPUs are not allowed
in the top cpuset. This is a surprising regression over earlier kernels
that didn't have cpusets enabled.
In order to keep the behaviour of cpusets consistent between systems
actively making use of them and systems not using them, this patch changes
the behaviour of the 'cpus' file in the top (root) cpuset, making it read
only, and making it automatically track the value of cpu_online_map. Thus
tasks in the top cpuset will have automatic use of hot plugged CPUs allowed
by their cpuset.
Thanks to Anton Blanchard and Nathan Lynch for reporting this problem,
driving the fix, and earlier versions of this patch.
Signed-off-by: Paul Jackson <pj@sgi.com>
Cc: Nathan Lynch <ntl@pobox.com>
Cc: Anton Blanchard <anton@samba.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-08-27 12:23:51 +04:00
|
|
|
*/
|
|
|
|
|
2007-10-19 10:40:20 +04:00
|
|
|
static int cpuset_handle_cpuhp(struct notifier_block *unused_nb,
|
|
|
|
unsigned long phase, void *unused_cpu)
|
[PATCH] cpuset: top_cpuset tracks hotplug changes to cpu_online_map
Change the list of cpus allowed to tasks in the top (root) cpuset to
dynamically track what cpus are online, using a CPU hotplug notifier. Make
this top cpus file read-only.
On systems that have cpusets configured in their kernel, but that aren't
actively using cpusets (for some distros, this covers the majority of
systems) all tasks end up in the top cpuset.
If that system does support CPU hotplug, then these tasks cannot make use
of CPUs that are added after system boot, because the CPUs are not allowed
in the top cpuset. This is a surprising regression over earlier kernels
that didn't have cpusets enabled.
In order to keep the behaviour of cpusets consistent between systems
actively making use of them and systems not using them, this patch changes
the behaviour of the 'cpus' file in the top (root) cpuset, making it read
only, and making it automatically track the value of cpu_online_map. Thus
tasks in the top cpuset will have automatic use of hot plugged CPUs allowed
by their cpuset.
Thanks to Anton Blanchard and Nathan Lynch for reporting this problem,
driving the fix, and earlier versions of this patch.
Signed-off-by: Paul Jackson <pj@sgi.com>
Cc: Nathan Lynch <ntl@pobox.com>
Cc: Anton Blanchard <anton@samba.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-08-27 12:23:51 +04:00
|
|
|
{
|
2007-05-24 13:33:15 +04:00
|
|
|
if (phase == CPU_DYING || phase == CPU_DYING_FROZEN)
|
|
|
|
return NOTIFY_DONE;
|
|
|
|
|
2006-09-29 13:01:17 +04:00
|
|
|
common_cpu_mem_hotplug_unplug();
|
[PATCH] cpuset: top_cpuset tracks hotplug changes to cpu_online_map
Change the list of cpus allowed to tasks in the top (root) cpuset to
dynamically track what cpus are online, using a CPU hotplug notifier. Make
this top cpus file read-only.
On systems that have cpusets configured in their kernel, but that aren't
actively using cpusets (for some distros, this covers the majority of
systems) all tasks end up in the top cpuset.
If that system does support CPU hotplug, then these tasks cannot make use
of CPUs that are added after system boot, because the CPUs are not allowed
in the top cpuset. This is a surprising regression over earlier kernels
that didn't have cpusets enabled.
In order to keep the behaviour of cpusets consistent between systems
actively making use of them and systems not using them, this patch changes
the behaviour of the 'cpus' file in the top (root) cpuset, making it read
only, and making it automatically track the value of cpu_online_map. Thus
tasks in the top cpuset will have automatic use of hot plugged CPUs allowed
by their cpuset.
Thanks to Anton Blanchard and Nathan Lynch for reporting this problem,
driving the fix, and earlier versions of this patch.
Signed-off-by: Paul Jackson <pj@sgi.com>
Cc: Nathan Lynch <ntl@pobox.com>
Cc: Anton Blanchard <anton@samba.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-08-27 12:23:51 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-09-29 13:01:17 +04:00
|
|
|
#ifdef CONFIG_MEMORY_HOTPLUG
|
[PATCH] cpuset: top_cpuset tracks hotplug changes to node_online_map
Change the list of memory nodes allowed to tasks in the top (root) nodeset
to dynamically track what cpus are online, using a call to a cpuset hook
from the memory hotplug code. Make this top cpus file read-only.
On systems that have cpusets configured in their kernel, but that aren't
actively using cpusets (for some distros, this covers the majority of
systems) all tasks end up in the top cpuset.
If that system does support memory hotplug, then these tasks cannot make
use of memory nodes that are added after system boot, because the memory
nodes are not allowed in the top cpuset. This is a surprising regression
over earlier kernels that didn't have cpusets enabled.
One key motivation for this change is to remain consistent with the
behaviour for the top_cpuset's 'cpus', which is also read-only, and which
automatically tracks the cpu_online_map.
This change also has the minor benefit that it fixes a long standing,
little noticed, minor bug in cpusets. The cpuset performance tweak to
short circuit the cpuset_zone_allowed() check on systems with just a single
cpuset (see 'number_of_cpusets', in linux/cpuset.h) meant that simply
changing the 'mems' of the top_cpuset had no affect, even though the change
(the write system call) appeared to succeed. With the following change,
that write to the 'mems' file fails -EACCES, and the 'mems' file stubbornly
refuses to be changed via user space writes. Thus no one should be mislead
into thinking they've changed the top_cpusets's 'mems' when in affect they
haven't.
In order to keep the behaviour of cpusets consistent between systems
actively making use of them and systems not using them, this patch changes
the behaviour of the 'mems' file in the top (root) cpuset, making it read
only, and making it automatically track the value of node_online_map. Thus
tasks in the top cpuset will have automatic use of hot plugged memory nodes
allowed by their cpuset.
[akpm@osdl.org: build fix]
[bunk@stusta.de: build fix]
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Adrian Bunk <bunk@stusta.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-09-29 13:01:16 +04:00
|
|
|
/*
|
2007-10-16 12:25:38 +04:00
|
|
|
* Keep top_cpuset.mems_allowed tracking node_states[N_HIGH_MEMORY].
|
|
|
|
* Call this routine anytime after you change
|
|
|
|
* node_states[N_HIGH_MEMORY].
|
[PATCH] cpuset: top_cpuset tracks hotplug changes to node_online_map
Change the list of memory nodes allowed to tasks in the top (root) nodeset
to dynamically track what cpus are online, using a call to a cpuset hook
from the memory hotplug code. Make this top cpus file read-only.
On systems that have cpusets configured in their kernel, but that aren't
actively using cpusets (for some distros, this covers the majority of
systems) all tasks end up in the top cpuset.
If that system does support memory hotplug, then these tasks cannot make
use of memory nodes that are added after system boot, because the memory
nodes are not allowed in the top cpuset. This is a surprising regression
over earlier kernels that didn't have cpusets enabled.
One key motivation for this change is to remain consistent with the
behaviour for the top_cpuset's 'cpus', which is also read-only, and which
automatically tracks the cpu_online_map.
This change also has the minor benefit that it fixes a long standing,
little noticed, minor bug in cpusets. The cpuset performance tweak to
short circuit the cpuset_zone_allowed() check on systems with just a single
cpuset (see 'number_of_cpusets', in linux/cpuset.h) meant that simply
changing the 'mems' of the top_cpuset had no affect, even though the change
(the write system call) appeared to succeed. With the following change,
that write to the 'mems' file fails -EACCES, and the 'mems' file stubbornly
refuses to be changed via user space writes. Thus no one should be mislead
into thinking they've changed the top_cpusets's 'mems' when in affect they
haven't.
In order to keep the behaviour of cpusets consistent between systems
actively making use of them and systems not using them, this patch changes
the behaviour of the 'mems' file in the top (root) cpuset, making it read
only, and making it automatically track the value of node_online_map. Thus
tasks in the top cpuset will have automatic use of hot plugged memory nodes
allowed by their cpuset.
[akpm@osdl.org: build fix]
[bunk@stusta.de: build fix]
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Adrian Bunk <bunk@stusta.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-09-29 13:01:16 +04:00
|
|
|
* See also the previous routine cpuset_handle_cpuhp().
|
|
|
|
*/
|
|
|
|
|
2006-10-11 01:48:57 +04:00
|
|
|
void cpuset_track_online_nodes(void)
|
[PATCH] cpuset: top_cpuset tracks hotplug changes to node_online_map
Change the list of memory nodes allowed to tasks in the top (root) nodeset
to dynamically track what cpus are online, using a call to a cpuset hook
from the memory hotplug code. Make this top cpus file read-only.
On systems that have cpusets configured in their kernel, but that aren't
actively using cpusets (for some distros, this covers the majority of
systems) all tasks end up in the top cpuset.
If that system does support memory hotplug, then these tasks cannot make
use of memory nodes that are added after system boot, because the memory
nodes are not allowed in the top cpuset. This is a surprising regression
over earlier kernels that didn't have cpusets enabled.
One key motivation for this change is to remain consistent with the
behaviour for the top_cpuset's 'cpus', which is also read-only, and which
automatically tracks the cpu_online_map.
This change also has the minor benefit that it fixes a long standing,
little noticed, minor bug in cpusets. The cpuset performance tweak to
short circuit the cpuset_zone_allowed() check on systems with just a single
cpuset (see 'number_of_cpusets', in linux/cpuset.h) meant that simply
changing the 'mems' of the top_cpuset had no affect, even though the change
(the write system call) appeared to succeed. With the following change,
that write to the 'mems' file fails -EACCES, and the 'mems' file stubbornly
refuses to be changed via user space writes. Thus no one should be mislead
into thinking they've changed the top_cpusets's 'mems' when in affect they
haven't.
In order to keep the behaviour of cpusets consistent between systems
actively making use of them and systems not using them, this patch changes
the behaviour of the 'mems' file in the top (root) cpuset, making it read
only, and making it automatically track the value of node_online_map. Thus
tasks in the top cpuset will have automatic use of hot plugged memory nodes
allowed by their cpuset.
[akpm@osdl.org: build fix]
[bunk@stusta.de: build fix]
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Adrian Bunk <bunk@stusta.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-09-29 13:01:16 +04:00
|
|
|
{
|
2006-09-29 13:01:17 +04:00
|
|
|
common_cpu_mem_hotplug_unplug();
|
[PATCH] cpuset: top_cpuset tracks hotplug changes to node_online_map
Change the list of memory nodes allowed to tasks in the top (root) nodeset
to dynamically track what cpus are online, using a call to a cpuset hook
from the memory hotplug code. Make this top cpus file read-only.
On systems that have cpusets configured in their kernel, but that aren't
actively using cpusets (for some distros, this covers the majority of
systems) all tasks end up in the top cpuset.
If that system does support memory hotplug, then these tasks cannot make
use of memory nodes that are added after system boot, because the memory
nodes are not allowed in the top cpuset. This is a surprising regression
over earlier kernels that didn't have cpusets enabled.
One key motivation for this change is to remain consistent with the
behaviour for the top_cpuset's 'cpus', which is also read-only, and which
automatically tracks the cpu_online_map.
This change also has the minor benefit that it fixes a long standing,
little noticed, minor bug in cpusets. The cpuset performance tweak to
short circuit the cpuset_zone_allowed() check on systems with just a single
cpuset (see 'number_of_cpusets', in linux/cpuset.h) meant that simply
changing the 'mems' of the top_cpuset had no affect, even though the change
(the write system call) appeared to succeed. With the following change,
that write to the 'mems' file fails -EACCES, and the 'mems' file stubbornly
refuses to be changed via user space writes. Thus no one should be mislead
into thinking they've changed the top_cpusets's 'mems' when in affect they
haven't.
In order to keep the behaviour of cpusets consistent between systems
actively making use of them and systems not using them, this patch changes
the behaviour of the 'mems' file in the top (root) cpuset, making it read
only, and making it automatically track the value of node_online_map. Thus
tasks in the top cpuset will have automatic use of hot plugged memory nodes
allowed by their cpuset.
[akpm@osdl.org: build fix]
[bunk@stusta.de: build fix]
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Adrian Bunk <bunk@stusta.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-09-29 13:01:16 +04:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
/**
|
|
|
|
* cpuset_init_smp - initialize cpus_allowed
|
|
|
|
*
|
|
|
|
* Description: Finish top cpuset after cpu, node maps are initialized
|
|
|
|
**/
|
|
|
|
|
|
|
|
void __init cpuset_init_smp(void)
|
|
|
|
{
|
|
|
|
top_cpuset.cpus_allowed = cpu_online_map;
|
2007-10-16 12:25:38 +04:00
|
|
|
top_cpuset.mems_allowed = node_states[N_HIGH_MEMORY];
|
[PATCH] cpuset: top_cpuset tracks hotplug changes to cpu_online_map
Change the list of cpus allowed to tasks in the top (root) cpuset to
dynamically track what cpus are online, using a CPU hotplug notifier. Make
this top cpus file read-only.
On systems that have cpusets configured in their kernel, but that aren't
actively using cpusets (for some distros, this covers the majority of
systems) all tasks end up in the top cpuset.
If that system does support CPU hotplug, then these tasks cannot make use
of CPUs that are added after system boot, because the CPUs are not allowed
in the top cpuset. This is a surprising regression over earlier kernels
that didn't have cpusets enabled.
In order to keep the behaviour of cpusets consistent between systems
actively making use of them and systems not using them, this patch changes
the behaviour of the 'cpus' file in the top (root) cpuset, making it read
only, and making it automatically track the value of cpu_online_map. Thus
tasks in the top cpuset will have automatic use of hot plugged CPUs allowed
by their cpuset.
Thanks to Anton Blanchard and Nathan Lynch for reporting this problem,
driving the fix, and earlier versions of this patch.
Signed-off-by: Paul Jackson <pj@sgi.com>
Cc: Nathan Lynch <ntl@pobox.com>
Cc: Anton Blanchard <anton@samba.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-08-27 12:23:51 +04:00
|
|
|
|
|
|
|
hotcpu_notifier(cpuset_handle_cpuhp, 0);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
[PATCH] cpuset release ABBA deadlock fix
Fix possible cpuset_sem ABBA deadlock if 'notify_on_release' set.
For a particular usage pattern, creating and destroying cpusets fairly
frequently using notify_on_release, on a very large system, this deadlock
can be seen every few days. If you are not using the cpuset
notify_on_release feature, you will never see this deadlock.
The existing code, on task exit (or cpuset deletion) did:
get cpuset_sem
if cpuset marked notify_on_release and is ready to release:
compute cpuset path relative to /dev/cpuset mount point
call_usermodehelper() forks /sbin/cpuset_release_agent with path
drop cpuset_sem
Unfortunately, the fork in call_usermodehelper can allocate memory, and
allocating memory can require cpuset_sem, if the mems_generation values
changed in the interim. This results in an ABBA deadlock, trying to obtain
cpuset_sem when it is already held by the current task.
To fix this, I put the cpuset path (which must be computed while holding
cpuset_sem) in a temporary buffer, to be used in the call_usermodehelper
call of /sbin/cpuset_release_agent only _after_ dropping cpuset_sem.
So the new logic is:
get cpuset_sem
if cpuset marked notify_on_release and is ready to release:
compute cpuset path relative to /dev/cpuset mount point
stash path in kmalloc'd buffer
drop cpuset_sem
call_usermodehelper() forks /sbin/cpuset_release_agent with path
free path
The sharp eyed reader might notice that this patch does not contain any
calls to kmalloc. The existing code in the check_for_release() routine was
already kmalloc'ing a buffer to hold the cpuset path. In the old code, it
just held the buffer for a few lines, over the cpuset_release_agent() call
that in turn invoked call_usermodehelper(). In the new code, with the
application of this patch, it returns that buffer via the new char
**ppathbuf parameter, for later use and freeing in cpuset_release_agent(),
which is called after cpuset_sem is dropped. Whereas the old code has just
one call to cpuset_release_agent(), right in the check_for_release()
routine, the new code has three calls to cpuset_release_agent(), from the
various places that a cpuset can be released.
This patch has been build and booted on SN2, and passed a stress test that
previously hit the deadlock within a few seconds.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-08-09 21:07:59 +04:00
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
* cpuset_cpus_allowed - return cpus_allowed mask from a tasks cpuset.
|
|
|
|
* @tsk: pointer to task_struct from which to obtain cpuset->cpus_allowed.
|
2008-04-05 05:11:07 +04:00
|
|
|
* @pmask: pointer to cpumask_t variable to receive cpus_allowed set.
|
2005-04-17 02:20:36 +04:00
|
|
|
*
|
|
|
|
* Description: Returns the cpumask_t cpus_allowed of the cpuset
|
|
|
|
* attached to the specified @tsk. Guaranteed to return some non-empty
|
|
|
|
* subset of cpu_online_map, even if this means going outside the
|
|
|
|
* tasks cpuset.
|
|
|
|
**/
|
|
|
|
|
2008-04-05 05:11:07 +04:00
|
|
|
void cpuset_cpus_allowed(struct task_struct *tsk, cpumask_t *pmask)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2006-03-23 14:00:18 +03:00
|
|
|
mutex_lock(&callback_mutex);
|
2008-04-05 05:11:07 +04:00
|
|
|
cpuset_cpus_allowed_locked(tsk, pmask);
|
2007-10-19 10:40:46 +04:00
|
|
|
mutex_unlock(&callback_mutex);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* cpuset_cpus_allowed_locked - return cpus_allowed mask from a tasks cpuset.
|
2008-02-07 11:14:45 +03:00
|
|
|
* Must be called with callback_mutex held.
|
2007-10-19 10:40:46 +04:00
|
|
|
**/
|
2008-04-05 05:11:07 +04:00
|
|
|
void cpuset_cpus_allowed_locked(struct task_struct *tsk, cpumask_t *pmask)
|
2007-10-19 10:40:46 +04:00
|
|
|
{
|
2006-01-08 12:01:55 +03:00
|
|
|
task_lock(tsk);
|
2008-04-05 05:11:07 +04:00
|
|
|
guarantee_online_cpus(task_cs(tsk), pmask);
|
2006-01-08 12:01:55 +03:00
|
|
|
task_unlock(tsk);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void cpuset_init_current_mems_allowed(void)
|
|
|
|
{
|
2008-04-05 05:11:07 +04:00
|
|
|
nodes_setall(current->mems_allowed);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
2006-01-08 12:01:55 +03:00
|
|
|
/**
|
|
|
|
* cpuset_mems_allowed - return mems_allowed mask from a tasks cpuset.
|
|
|
|
* @tsk: pointer to task_struct from which to obtain cpuset->mems_allowed.
|
|
|
|
*
|
|
|
|
* Description: Returns the nodemask_t mems_allowed of the cpuset
|
|
|
|
* attached to the specified @tsk. Guaranteed to return some non-empty
|
2007-10-16 12:25:38 +04:00
|
|
|
* subset of node_states[N_HIGH_MEMORY], even if this means going outside the
|
2006-01-08 12:01:55 +03:00
|
|
|
* tasks cpuset.
|
|
|
|
**/
|
|
|
|
|
|
|
|
nodemask_t cpuset_mems_allowed(struct task_struct *tsk)
|
|
|
|
{
|
|
|
|
nodemask_t mask;
|
|
|
|
|
2006-03-23 14:00:18 +03:00
|
|
|
mutex_lock(&callback_mutex);
|
2006-01-08 12:01:55 +03:00
|
|
|
task_lock(tsk);
|
2007-10-19 10:39:39 +04:00
|
|
|
guarantee_online_mems(task_cs(tsk), &mask);
|
2006-01-08 12:01:55 +03:00
|
|
|
task_unlock(tsk);
|
2006-03-23 14:00:18 +03:00
|
|
|
mutex_unlock(&callback_mutex);
|
2006-01-08 12:01:55 +03:00
|
|
|
|
|
|
|
return mask;
|
|
|
|
}
|
|
|
|
|
2005-07-27 22:45:11 +04:00
|
|
|
/**
|
2008-04-28 13:12:18 +04:00
|
|
|
* cpuset_nodemask_valid_mems_allowed - check nodemask vs. curremt mems_allowed
|
|
|
|
* @nodemask: the nodemask to be checked
|
2005-07-27 22:45:11 +04:00
|
|
|
*
|
2008-04-28 13:12:18 +04:00
|
|
|
* Are any of the nodes in the nodemask allowed in current->mems_allowed?
|
2005-04-17 02:20:36 +04:00
|
|
|
*/
|
2008-04-28 13:12:18 +04:00
|
|
|
int cpuset_nodemask_valid_mems_allowed(nodemask_t *nodemask)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2008-04-28 13:12:18 +04:00
|
|
|
return nodes_intersects(*nodemask, current->mems_allowed);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
[PATCH] cpusets: formalize intermediate GFP_KERNEL containment
This patch makes use of the previously underutilized cpuset flag
'mem_exclusive' to provide what amounts to another layer of memory placement
resolution. With this patch, there are now the following four layers of
memory placement available:
1) The whole system (interrupt and GFP_ATOMIC allocations can use this),
2) The nearest enclosing mem_exclusive cpuset (GFP_KERNEL allocations can use),
3) The current tasks cpuset (GFP_USER allocations constrained to here), and
4) Specific node placement, using mbind and set_mempolicy.
These nest - each layer is a subset (same or within) of the previous.
Layer (2) above is new, with this patch. The call used to check whether a
zone (its node, actually) is in a cpuset (in its mems_allowed, actually) is
extended to take a gfp_mask argument, and its logic is extended, in the case
that __GFP_HARDWALL is not set in the flag bits, to look up the cpuset
hierarchy for the nearest enclosing mem_exclusive cpuset, to determine if
placement is allowed. The definition of GFP_USER, which used to be identical
to GFP_KERNEL, is changed to also set the __GFP_HARDWALL bit, in the previous
cpuset_gfp_hardwall_flag patch.
GFP_ATOMIC and GFP_KERNEL allocations will stay within the current tasks
cpuset, so long as any node therein is not too tight on memory, but will
escape to the larger layer, if need be.
The intended use is to allow something like a batch manager to handle several
jobs, each job in its own cpuset, but using common kernel memory for caches
and such. Swapper and oom_kill activity is also constrained to Layer (2). A
task in or below one mem_exclusive cpuset should not cause swapping on nodes
in another non-overlapping mem_exclusive cpuset, nor provoke oom_killing of a
task in another such cpuset. Heavy use of kernel memory for i/o caching and
such by one job should not impact the memory available to jobs in other
non-overlapping mem_exclusive cpusets.
This patch enables providing hardwall, inescapable cpusets for memory
allocations of each job, while sharing kernel memory allocations between
several jobs, in an enclosing mem_exclusive cpuset.
Like Dinakar's patch earlier to enable administering sched domains using the
cpu_exclusive flag, this patch also provides a useful meaning to a cpuset flag
that had previously done nothing much useful other than restrict what cpuset
configurations were allowed.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-09-07 02:18:12 +04:00
|
|
|
/*
|
2008-04-29 12:00:26 +04:00
|
|
|
* nearest_hardwall_ancestor() - Returns the nearest mem_exclusive or
|
|
|
|
* mem_hardwall ancestor to the specified cpuset. Call holding
|
|
|
|
* callback_mutex. If no ancestor is mem_exclusive or mem_hardwall
|
|
|
|
* (an unusual configuration), then returns the root cpuset.
|
[PATCH] cpusets: formalize intermediate GFP_KERNEL containment
This patch makes use of the previously underutilized cpuset flag
'mem_exclusive' to provide what amounts to another layer of memory placement
resolution. With this patch, there are now the following four layers of
memory placement available:
1) The whole system (interrupt and GFP_ATOMIC allocations can use this),
2) The nearest enclosing mem_exclusive cpuset (GFP_KERNEL allocations can use),
3) The current tasks cpuset (GFP_USER allocations constrained to here), and
4) Specific node placement, using mbind and set_mempolicy.
These nest - each layer is a subset (same or within) of the previous.
Layer (2) above is new, with this patch. The call used to check whether a
zone (its node, actually) is in a cpuset (in its mems_allowed, actually) is
extended to take a gfp_mask argument, and its logic is extended, in the case
that __GFP_HARDWALL is not set in the flag bits, to look up the cpuset
hierarchy for the nearest enclosing mem_exclusive cpuset, to determine if
placement is allowed. The definition of GFP_USER, which used to be identical
to GFP_KERNEL, is changed to also set the __GFP_HARDWALL bit, in the previous
cpuset_gfp_hardwall_flag patch.
GFP_ATOMIC and GFP_KERNEL allocations will stay within the current tasks
cpuset, so long as any node therein is not too tight on memory, but will
escape to the larger layer, if need be.
The intended use is to allow something like a batch manager to handle several
jobs, each job in its own cpuset, but using common kernel memory for caches
and such. Swapper and oom_kill activity is also constrained to Layer (2). A
task in or below one mem_exclusive cpuset should not cause swapping on nodes
in another non-overlapping mem_exclusive cpuset, nor provoke oom_killing of a
task in another such cpuset. Heavy use of kernel memory for i/o caching and
such by one job should not impact the memory available to jobs in other
non-overlapping mem_exclusive cpusets.
This patch enables providing hardwall, inescapable cpusets for memory
allocations of each job, while sharing kernel memory allocations between
several jobs, in an enclosing mem_exclusive cpuset.
Like Dinakar's patch earlier to enable administering sched domains using the
cpu_exclusive flag, this patch also provides a useful meaning to a cpuset flag
that had previously done nothing much useful other than restrict what cpuset
configurations were allowed.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-09-07 02:18:12 +04:00
|
|
|
*/
|
2008-04-29 12:00:26 +04:00
|
|
|
static const struct cpuset *nearest_hardwall_ancestor(const struct cpuset *cs)
|
[PATCH] cpusets: formalize intermediate GFP_KERNEL containment
This patch makes use of the previously underutilized cpuset flag
'mem_exclusive' to provide what amounts to another layer of memory placement
resolution. With this patch, there are now the following four layers of
memory placement available:
1) The whole system (interrupt and GFP_ATOMIC allocations can use this),
2) The nearest enclosing mem_exclusive cpuset (GFP_KERNEL allocations can use),
3) The current tasks cpuset (GFP_USER allocations constrained to here), and
4) Specific node placement, using mbind and set_mempolicy.
These nest - each layer is a subset (same or within) of the previous.
Layer (2) above is new, with this patch. The call used to check whether a
zone (its node, actually) is in a cpuset (in its mems_allowed, actually) is
extended to take a gfp_mask argument, and its logic is extended, in the case
that __GFP_HARDWALL is not set in the flag bits, to look up the cpuset
hierarchy for the nearest enclosing mem_exclusive cpuset, to determine if
placement is allowed. The definition of GFP_USER, which used to be identical
to GFP_KERNEL, is changed to also set the __GFP_HARDWALL bit, in the previous
cpuset_gfp_hardwall_flag patch.
GFP_ATOMIC and GFP_KERNEL allocations will stay within the current tasks
cpuset, so long as any node therein is not too tight on memory, but will
escape to the larger layer, if need be.
The intended use is to allow something like a batch manager to handle several
jobs, each job in its own cpuset, but using common kernel memory for caches
and such. Swapper and oom_kill activity is also constrained to Layer (2). A
task in or below one mem_exclusive cpuset should not cause swapping on nodes
in another non-overlapping mem_exclusive cpuset, nor provoke oom_killing of a
task in another such cpuset. Heavy use of kernel memory for i/o caching and
such by one job should not impact the memory available to jobs in other
non-overlapping mem_exclusive cpusets.
This patch enables providing hardwall, inescapable cpusets for memory
allocations of each job, while sharing kernel memory allocations between
several jobs, in an enclosing mem_exclusive cpuset.
Like Dinakar's patch earlier to enable administering sched domains using the
cpu_exclusive flag, this patch also provides a useful meaning to a cpuset flag
that had previously done nothing much useful other than restrict what cpuset
configurations were allowed.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-09-07 02:18:12 +04:00
|
|
|
{
|
2008-04-29 12:00:26 +04:00
|
|
|
while (!(is_mem_exclusive(cs) || is_mem_hardwall(cs)) && cs->parent)
|
[PATCH] cpusets: formalize intermediate GFP_KERNEL containment
This patch makes use of the previously underutilized cpuset flag
'mem_exclusive' to provide what amounts to another layer of memory placement
resolution. With this patch, there are now the following four layers of
memory placement available:
1) The whole system (interrupt and GFP_ATOMIC allocations can use this),
2) The nearest enclosing mem_exclusive cpuset (GFP_KERNEL allocations can use),
3) The current tasks cpuset (GFP_USER allocations constrained to here), and
4) Specific node placement, using mbind and set_mempolicy.
These nest - each layer is a subset (same or within) of the previous.
Layer (2) above is new, with this patch. The call used to check whether a
zone (its node, actually) is in a cpuset (in its mems_allowed, actually) is
extended to take a gfp_mask argument, and its logic is extended, in the case
that __GFP_HARDWALL is not set in the flag bits, to look up the cpuset
hierarchy for the nearest enclosing mem_exclusive cpuset, to determine if
placement is allowed. The definition of GFP_USER, which used to be identical
to GFP_KERNEL, is changed to also set the __GFP_HARDWALL bit, in the previous
cpuset_gfp_hardwall_flag patch.
GFP_ATOMIC and GFP_KERNEL allocations will stay within the current tasks
cpuset, so long as any node therein is not too tight on memory, but will
escape to the larger layer, if need be.
The intended use is to allow something like a batch manager to handle several
jobs, each job in its own cpuset, but using common kernel memory for caches
and such. Swapper and oom_kill activity is also constrained to Layer (2). A
task in or below one mem_exclusive cpuset should not cause swapping on nodes
in another non-overlapping mem_exclusive cpuset, nor provoke oom_killing of a
task in another such cpuset. Heavy use of kernel memory for i/o caching and
such by one job should not impact the memory available to jobs in other
non-overlapping mem_exclusive cpusets.
This patch enables providing hardwall, inescapable cpusets for memory
allocations of each job, while sharing kernel memory allocations between
several jobs, in an enclosing mem_exclusive cpuset.
Like Dinakar's patch earlier to enable administering sched domains using the
cpu_exclusive flag, this patch also provides a useful meaning to a cpuset flag
that had previously done nothing much useful other than restrict what cpuset
configurations were allowed.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-09-07 02:18:12 +04:00
|
|
|
cs = cs->parent;
|
|
|
|
return cs;
|
|
|
|
}
|
|
|
|
|
2005-07-27 22:45:11 +04:00
|
|
|
/**
|
[PATCH] cpuset: rework cpuset_zone_allowed api
Elaborate the API for calling cpuset_zone_allowed(), so that users have to
explicitly choose between the two variants:
cpuset_zone_allowed_hardwall()
cpuset_zone_allowed_softwall()
Until now, whether or not you got the hardwall flavor depended solely on
whether or not you or'd in the __GFP_HARDWALL gfp flag to the gfp_mask
argument.
If you didn't specify __GFP_HARDWALL, you implicitly got the softwall
version.
Unfortunately, this meant that users would end up with the softwall version
without thinking about it. Since only the softwall version might sleep,
this led to bugs with possible sleeping in interrupt context on more than
one occassion.
The hardwall version requires that the current tasks mems_allowed allows
the node of the specified zone (or that you're in interrupt or that
__GFP_THISNODE is set or that you're on a one cpuset system.)
The softwall version, depending on the gfp_mask, might allow a node if it
was allowed in the nearest enclusing cpuset marked mem_exclusive (which
requires taking the cpuset lock 'callback_mutex' to evaluate.)
This patch removes the cpuset_zone_allowed() call, and forces the caller to
explicitly choose between the hardwall and the softwall case.
If the caller wants the gfp_mask to determine this choice, they should (1)
be sure they can sleep or that __GFP_HARDWALL is set, and (2) invoke the
cpuset_zone_allowed_softwall() routine.
This adds another 100 or 200 bytes to the kernel text space, due to the few
lines of nearly duplicate code at the top of both cpuset_zone_allowed_*
routines. It should save a few instructions executed for the calls that
turned into calls of cpuset_zone_allowed_hardwall, thanks to not having to
set (before the call) then check (within the call) the __GFP_HARDWALL flag.
For the most critical call, from get_page_from_freelist(), the same
instructions are executed as before -- the old cpuset_zone_allowed()
routine it used to call is the same code as the
cpuset_zone_allowed_softwall() routine that it calls now.
Not a perfect win, but seems worth it, to reduce this chance of hitting a
sleeping with irq off complaint again.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-12-13 11:34:25 +03:00
|
|
|
* cpuset_zone_allowed_softwall - Can we allocate on zone z's memory node?
|
[PATCH] cpusets: formalize intermediate GFP_KERNEL containment
This patch makes use of the previously underutilized cpuset flag
'mem_exclusive' to provide what amounts to another layer of memory placement
resolution. With this patch, there are now the following four layers of
memory placement available:
1) The whole system (interrupt and GFP_ATOMIC allocations can use this),
2) The nearest enclosing mem_exclusive cpuset (GFP_KERNEL allocations can use),
3) The current tasks cpuset (GFP_USER allocations constrained to here), and
4) Specific node placement, using mbind and set_mempolicy.
These nest - each layer is a subset (same or within) of the previous.
Layer (2) above is new, with this patch. The call used to check whether a
zone (its node, actually) is in a cpuset (in its mems_allowed, actually) is
extended to take a gfp_mask argument, and its logic is extended, in the case
that __GFP_HARDWALL is not set in the flag bits, to look up the cpuset
hierarchy for the nearest enclosing mem_exclusive cpuset, to determine if
placement is allowed. The definition of GFP_USER, which used to be identical
to GFP_KERNEL, is changed to also set the __GFP_HARDWALL bit, in the previous
cpuset_gfp_hardwall_flag patch.
GFP_ATOMIC and GFP_KERNEL allocations will stay within the current tasks
cpuset, so long as any node therein is not too tight on memory, but will
escape to the larger layer, if need be.
The intended use is to allow something like a batch manager to handle several
jobs, each job in its own cpuset, but using common kernel memory for caches
and such. Swapper and oom_kill activity is also constrained to Layer (2). A
task in or below one mem_exclusive cpuset should not cause swapping on nodes
in another non-overlapping mem_exclusive cpuset, nor provoke oom_killing of a
task in another such cpuset. Heavy use of kernel memory for i/o caching and
such by one job should not impact the memory available to jobs in other
non-overlapping mem_exclusive cpusets.
This patch enables providing hardwall, inescapable cpusets for memory
allocations of each job, while sharing kernel memory allocations between
several jobs, in an enclosing mem_exclusive cpuset.
Like Dinakar's patch earlier to enable administering sched domains using the
cpu_exclusive flag, this patch also provides a useful meaning to a cpuset flag
that had previously done nothing much useful other than restrict what cpuset
configurations were allowed.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-09-07 02:18:12 +04:00
|
|
|
* @z: is this zone on an allowed node?
|
[PATCH] cpuset: rework cpuset_zone_allowed api
Elaborate the API for calling cpuset_zone_allowed(), so that users have to
explicitly choose between the two variants:
cpuset_zone_allowed_hardwall()
cpuset_zone_allowed_softwall()
Until now, whether or not you got the hardwall flavor depended solely on
whether or not you or'd in the __GFP_HARDWALL gfp flag to the gfp_mask
argument.
If you didn't specify __GFP_HARDWALL, you implicitly got the softwall
version.
Unfortunately, this meant that users would end up with the softwall version
without thinking about it. Since only the softwall version might sleep,
this led to bugs with possible sleeping in interrupt context on more than
one occassion.
The hardwall version requires that the current tasks mems_allowed allows
the node of the specified zone (or that you're in interrupt or that
__GFP_THISNODE is set or that you're on a one cpuset system.)
The softwall version, depending on the gfp_mask, might allow a node if it
was allowed in the nearest enclusing cpuset marked mem_exclusive (which
requires taking the cpuset lock 'callback_mutex' to evaluate.)
This patch removes the cpuset_zone_allowed() call, and forces the caller to
explicitly choose between the hardwall and the softwall case.
If the caller wants the gfp_mask to determine this choice, they should (1)
be sure they can sleep or that __GFP_HARDWALL is set, and (2) invoke the
cpuset_zone_allowed_softwall() routine.
This adds another 100 or 200 bytes to the kernel text space, due to the few
lines of nearly duplicate code at the top of both cpuset_zone_allowed_*
routines. It should save a few instructions executed for the calls that
turned into calls of cpuset_zone_allowed_hardwall, thanks to not having to
set (before the call) then check (within the call) the __GFP_HARDWALL flag.
For the most critical call, from get_page_from_freelist(), the same
instructions are executed as before -- the old cpuset_zone_allowed()
routine it used to call is the same code as the
cpuset_zone_allowed_softwall() routine that it calls now.
Not a perfect win, but seems worth it, to reduce this chance of hitting a
sleeping with irq off complaint again.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-12-13 11:34:25 +03:00
|
|
|
* @gfp_mask: memory allocation flags
|
2005-07-27 22:45:11 +04:00
|
|
|
*
|
[PATCH] cpuset: rework cpuset_zone_allowed api
Elaborate the API for calling cpuset_zone_allowed(), so that users have to
explicitly choose between the two variants:
cpuset_zone_allowed_hardwall()
cpuset_zone_allowed_softwall()
Until now, whether or not you got the hardwall flavor depended solely on
whether or not you or'd in the __GFP_HARDWALL gfp flag to the gfp_mask
argument.
If you didn't specify __GFP_HARDWALL, you implicitly got the softwall
version.
Unfortunately, this meant that users would end up with the softwall version
without thinking about it. Since only the softwall version might sleep,
this led to bugs with possible sleeping in interrupt context on more than
one occassion.
The hardwall version requires that the current tasks mems_allowed allows
the node of the specified zone (or that you're in interrupt or that
__GFP_THISNODE is set or that you're on a one cpuset system.)
The softwall version, depending on the gfp_mask, might allow a node if it
was allowed in the nearest enclusing cpuset marked mem_exclusive (which
requires taking the cpuset lock 'callback_mutex' to evaluate.)
This patch removes the cpuset_zone_allowed() call, and forces the caller to
explicitly choose between the hardwall and the softwall case.
If the caller wants the gfp_mask to determine this choice, they should (1)
be sure they can sleep or that __GFP_HARDWALL is set, and (2) invoke the
cpuset_zone_allowed_softwall() routine.
This adds another 100 or 200 bytes to the kernel text space, due to the few
lines of nearly duplicate code at the top of both cpuset_zone_allowed_*
routines. It should save a few instructions executed for the calls that
turned into calls of cpuset_zone_allowed_hardwall, thanks to not having to
set (before the call) then check (within the call) the __GFP_HARDWALL flag.
For the most critical call, from get_page_from_freelist(), the same
instructions are executed as before -- the old cpuset_zone_allowed()
routine it used to call is the same code as the
cpuset_zone_allowed_softwall() routine that it calls now.
Not a perfect win, but seems worth it, to reduce this chance of hitting a
sleeping with irq off complaint again.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-12-13 11:34:25 +03:00
|
|
|
* If we're in interrupt, yes, we can always allocate. If
|
|
|
|
* __GFP_THISNODE is set, yes, we can always allocate. If zone
|
[PATCH] cpusets: formalize intermediate GFP_KERNEL containment
This patch makes use of the previously underutilized cpuset flag
'mem_exclusive' to provide what amounts to another layer of memory placement
resolution. With this patch, there are now the following four layers of
memory placement available:
1) The whole system (interrupt and GFP_ATOMIC allocations can use this),
2) The nearest enclosing mem_exclusive cpuset (GFP_KERNEL allocations can use),
3) The current tasks cpuset (GFP_USER allocations constrained to here), and
4) Specific node placement, using mbind and set_mempolicy.
These nest - each layer is a subset (same or within) of the previous.
Layer (2) above is new, with this patch. The call used to check whether a
zone (its node, actually) is in a cpuset (in its mems_allowed, actually) is
extended to take a gfp_mask argument, and its logic is extended, in the case
that __GFP_HARDWALL is not set in the flag bits, to look up the cpuset
hierarchy for the nearest enclosing mem_exclusive cpuset, to determine if
placement is allowed. The definition of GFP_USER, which used to be identical
to GFP_KERNEL, is changed to also set the __GFP_HARDWALL bit, in the previous
cpuset_gfp_hardwall_flag patch.
GFP_ATOMIC and GFP_KERNEL allocations will stay within the current tasks
cpuset, so long as any node therein is not too tight on memory, but will
escape to the larger layer, if need be.
The intended use is to allow something like a batch manager to handle several
jobs, each job in its own cpuset, but using common kernel memory for caches
and such. Swapper and oom_kill activity is also constrained to Layer (2). A
task in or below one mem_exclusive cpuset should not cause swapping on nodes
in another non-overlapping mem_exclusive cpuset, nor provoke oom_killing of a
task in another such cpuset. Heavy use of kernel memory for i/o caching and
such by one job should not impact the memory available to jobs in other
non-overlapping mem_exclusive cpusets.
This patch enables providing hardwall, inescapable cpusets for memory
allocations of each job, while sharing kernel memory allocations between
several jobs, in an enclosing mem_exclusive cpuset.
Like Dinakar's patch earlier to enable administering sched domains using the
cpu_exclusive flag, this patch also provides a useful meaning to a cpuset flag
that had previously done nothing much useful other than restrict what cpuset
configurations were allowed.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-09-07 02:18:12 +04:00
|
|
|
* z's node is in our tasks mems_allowed, yes. If it's not a
|
|
|
|
* __GFP_HARDWALL request and this zone's nodes is in the nearest
|
2008-04-29 12:00:26 +04:00
|
|
|
* hardwalled cpuset ancestor to this tasks cpuset, yes.
|
2007-05-07 01:49:32 +04:00
|
|
|
* If the task has been OOM killed and has access to memory reserves
|
|
|
|
* as specified by the TIF_MEMDIE flag, yes.
|
[PATCH] cpusets: formalize intermediate GFP_KERNEL containment
This patch makes use of the previously underutilized cpuset flag
'mem_exclusive' to provide what amounts to another layer of memory placement
resolution. With this patch, there are now the following four layers of
memory placement available:
1) The whole system (interrupt and GFP_ATOMIC allocations can use this),
2) The nearest enclosing mem_exclusive cpuset (GFP_KERNEL allocations can use),
3) The current tasks cpuset (GFP_USER allocations constrained to here), and
4) Specific node placement, using mbind and set_mempolicy.
These nest - each layer is a subset (same or within) of the previous.
Layer (2) above is new, with this patch. The call used to check whether a
zone (its node, actually) is in a cpuset (in its mems_allowed, actually) is
extended to take a gfp_mask argument, and its logic is extended, in the case
that __GFP_HARDWALL is not set in the flag bits, to look up the cpuset
hierarchy for the nearest enclosing mem_exclusive cpuset, to determine if
placement is allowed. The definition of GFP_USER, which used to be identical
to GFP_KERNEL, is changed to also set the __GFP_HARDWALL bit, in the previous
cpuset_gfp_hardwall_flag patch.
GFP_ATOMIC and GFP_KERNEL allocations will stay within the current tasks
cpuset, so long as any node therein is not too tight on memory, but will
escape to the larger layer, if need be.
The intended use is to allow something like a batch manager to handle several
jobs, each job in its own cpuset, but using common kernel memory for caches
and such. Swapper and oom_kill activity is also constrained to Layer (2). A
task in or below one mem_exclusive cpuset should not cause swapping on nodes
in another non-overlapping mem_exclusive cpuset, nor provoke oom_killing of a
task in another such cpuset. Heavy use of kernel memory for i/o caching and
such by one job should not impact the memory available to jobs in other
non-overlapping mem_exclusive cpusets.
This patch enables providing hardwall, inescapable cpusets for memory
allocations of each job, while sharing kernel memory allocations between
several jobs, in an enclosing mem_exclusive cpuset.
Like Dinakar's patch earlier to enable administering sched domains using the
cpu_exclusive flag, this patch also provides a useful meaning to a cpuset flag
that had previously done nothing much useful other than restrict what cpuset
configurations were allowed.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-09-07 02:18:12 +04:00
|
|
|
* Otherwise, no.
|
|
|
|
*
|
[PATCH] cpuset: rework cpuset_zone_allowed api
Elaborate the API for calling cpuset_zone_allowed(), so that users have to
explicitly choose between the two variants:
cpuset_zone_allowed_hardwall()
cpuset_zone_allowed_softwall()
Until now, whether or not you got the hardwall flavor depended solely on
whether or not you or'd in the __GFP_HARDWALL gfp flag to the gfp_mask
argument.
If you didn't specify __GFP_HARDWALL, you implicitly got the softwall
version.
Unfortunately, this meant that users would end up with the softwall version
without thinking about it. Since only the softwall version might sleep,
this led to bugs with possible sleeping in interrupt context on more than
one occassion.
The hardwall version requires that the current tasks mems_allowed allows
the node of the specified zone (or that you're in interrupt or that
__GFP_THISNODE is set or that you're on a one cpuset system.)
The softwall version, depending on the gfp_mask, might allow a node if it
was allowed in the nearest enclusing cpuset marked mem_exclusive (which
requires taking the cpuset lock 'callback_mutex' to evaluate.)
This patch removes the cpuset_zone_allowed() call, and forces the caller to
explicitly choose between the hardwall and the softwall case.
If the caller wants the gfp_mask to determine this choice, they should (1)
be sure they can sleep or that __GFP_HARDWALL is set, and (2) invoke the
cpuset_zone_allowed_softwall() routine.
This adds another 100 or 200 bytes to the kernel text space, due to the few
lines of nearly duplicate code at the top of both cpuset_zone_allowed_*
routines. It should save a few instructions executed for the calls that
turned into calls of cpuset_zone_allowed_hardwall, thanks to not having to
set (before the call) then check (within the call) the __GFP_HARDWALL flag.
For the most critical call, from get_page_from_freelist(), the same
instructions are executed as before -- the old cpuset_zone_allowed()
routine it used to call is the same code as the
cpuset_zone_allowed_softwall() routine that it calls now.
Not a perfect win, but seems worth it, to reduce this chance of hitting a
sleeping with irq off complaint again.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-12-13 11:34:25 +03:00
|
|
|
* If __GFP_HARDWALL is set, cpuset_zone_allowed_softwall()
|
|
|
|
* reduces to cpuset_zone_allowed_hardwall(). Otherwise,
|
|
|
|
* cpuset_zone_allowed_softwall() might sleep, and might allow a zone
|
|
|
|
* from an enclosing cpuset.
|
|
|
|
*
|
|
|
|
* cpuset_zone_allowed_hardwall() only handles the simpler case of
|
|
|
|
* hardwall cpusets, and never sleeps.
|
|
|
|
*
|
|
|
|
* The __GFP_THISNODE placement logic is really handled elsewhere,
|
|
|
|
* by forcibly using a zonelist starting at a specified node, and by
|
|
|
|
* (in get_page_from_freelist()) refusing to consider the zones for
|
|
|
|
* any node on the zonelist except the first. By the time any such
|
|
|
|
* calls get to this routine, we should just shut up and say 'yes'.
|
|
|
|
*
|
[PATCH] cpusets: formalize intermediate GFP_KERNEL containment
This patch makes use of the previously underutilized cpuset flag
'mem_exclusive' to provide what amounts to another layer of memory placement
resolution. With this patch, there are now the following four layers of
memory placement available:
1) The whole system (interrupt and GFP_ATOMIC allocations can use this),
2) The nearest enclosing mem_exclusive cpuset (GFP_KERNEL allocations can use),
3) The current tasks cpuset (GFP_USER allocations constrained to here), and
4) Specific node placement, using mbind and set_mempolicy.
These nest - each layer is a subset (same or within) of the previous.
Layer (2) above is new, with this patch. The call used to check whether a
zone (its node, actually) is in a cpuset (in its mems_allowed, actually) is
extended to take a gfp_mask argument, and its logic is extended, in the case
that __GFP_HARDWALL is not set in the flag bits, to look up the cpuset
hierarchy for the nearest enclosing mem_exclusive cpuset, to determine if
placement is allowed. The definition of GFP_USER, which used to be identical
to GFP_KERNEL, is changed to also set the __GFP_HARDWALL bit, in the previous
cpuset_gfp_hardwall_flag patch.
GFP_ATOMIC and GFP_KERNEL allocations will stay within the current tasks
cpuset, so long as any node therein is not too tight on memory, but will
escape to the larger layer, if need be.
The intended use is to allow something like a batch manager to handle several
jobs, each job in its own cpuset, but using common kernel memory for caches
and such. Swapper and oom_kill activity is also constrained to Layer (2). A
task in or below one mem_exclusive cpuset should not cause swapping on nodes
in another non-overlapping mem_exclusive cpuset, nor provoke oom_killing of a
task in another such cpuset. Heavy use of kernel memory for i/o caching and
such by one job should not impact the memory available to jobs in other
non-overlapping mem_exclusive cpusets.
This patch enables providing hardwall, inescapable cpusets for memory
allocations of each job, while sharing kernel memory allocations between
several jobs, in an enclosing mem_exclusive cpuset.
Like Dinakar's patch earlier to enable administering sched domains using the
cpu_exclusive flag, this patch also provides a useful meaning to a cpuset flag
that had previously done nothing much useful other than restrict what cpuset
configurations were allowed.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-09-07 02:18:12 +04:00
|
|
|
* GFP_USER allocations are marked with the __GFP_HARDWALL bit,
|
2007-05-07 01:49:32 +04:00
|
|
|
* and do not allow allocations outside the current tasks cpuset
|
|
|
|
* unless the task has been OOM killed as is marked TIF_MEMDIE.
|
[PATCH] cpusets: formalize intermediate GFP_KERNEL containment
This patch makes use of the previously underutilized cpuset flag
'mem_exclusive' to provide what amounts to another layer of memory placement
resolution. With this patch, there are now the following four layers of
memory placement available:
1) The whole system (interrupt and GFP_ATOMIC allocations can use this),
2) The nearest enclosing mem_exclusive cpuset (GFP_KERNEL allocations can use),
3) The current tasks cpuset (GFP_USER allocations constrained to here), and
4) Specific node placement, using mbind and set_mempolicy.
These nest - each layer is a subset (same or within) of the previous.
Layer (2) above is new, with this patch. The call used to check whether a
zone (its node, actually) is in a cpuset (in its mems_allowed, actually) is
extended to take a gfp_mask argument, and its logic is extended, in the case
that __GFP_HARDWALL is not set in the flag bits, to look up the cpuset
hierarchy for the nearest enclosing mem_exclusive cpuset, to determine if
placement is allowed. The definition of GFP_USER, which used to be identical
to GFP_KERNEL, is changed to also set the __GFP_HARDWALL bit, in the previous
cpuset_gfp_hardwall_flag patch.
GFP_ATOMIC and GFP_KERNEL allocations will stay within the current tasks
cpuset, so long as any node therein is not too tight on memory, but will
escape to the larger layer, if need be.
The intended use is to allow something like a batch manager to handle several
jobs, each job in its own cpuset, but using common kernel memory for caches
and such. Swapper and oom_kill activity is also constrained to Layer (2). A
task in or below one mem_exclusive cpuset should not cause swapping on nodes
in another non-overlapping mem_exclusive cpuset, nor provoke oom_killing of a
task in another such cpuset. Heavy use of kernel memory for i/o caching and
such by one job should not impact the memory available to jobs in other
non-overlapping mem_exclusive cpusets.
This patch enables providing hardwall, inescapable cpusets for memory
allocations of each job, while sharing kernel memory allocations between
several jobs, in an enclosing mem_exclusive cpuset.
Like Dinakar's patch earlier to enable administering sched domains using the
cpu_exclusive flag, this patch also provides a useful meaning to a cpuset flag
that had previously done nothing much useful other than restrict what cpuset
configurations were allowed.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-09-07 02:18:12 +04:00
|
|
|
* GFP_KERNEL allocations are not so marked, so can escape to the
|
2008-04-29 12:00:26 +04:00
|
|
|
* nearest enclosing hardwalled ancestor cpuset.
|
[PATCH] cpusets: formalize intermediate GFP_KERNEL containment
This patch makes use of the previously underutilized cpuset flag
'mem_exclusive' to provide what amounts to another layer of memory placement
resolution. With this patch, there are now the following four layers of
memory placement available:
1) The whole system (interrupt and GFP_ATOMIC allocations can use this),
2) The nearest enclosing mem_exclusive cpuset (GFP_KERNEL allocations can use),
3) The current tasks cpuset (GFP_USER allocations constrained to here), and
4) Specific node placement, using mbind and set_mempolicy.
These nest - each layer is a subset (same or within) of the previous.
Layer (2) above is new, with this patch. The call used to check whether a
zone (its node, actually) is in a cpuset (in its mems_allowed, actually) is
extended to take a gfp_mask argument, and its logic is extended, in the case
that __GFP_HARDWALL is not set in the flag bits, to look up the cpuset
hierarchy for the nearest enclosing mem_exclusive cpuset, to determine if
placement is allowed. The definition of GFP_USER, which used to be identical
to GFP_KERNEL, is changed to also set the __GFP_HARDWALL bit, in the previous
cpuset_gfp_hardwall_flag patch.
GFP_ATOMIC and GFP_KERNEL allocations will stay within the current tasks
cpuset, so long as any node therein is not too tight on memory, but will
escape to the larger layer, if need be.
The intended use is to allow something like a batch manager to handle several
jobs, each job in its own cpuset, but using common kernel memory for caches
and such. Swapper and oom_kill activity is also constrained to Layer (2). A
task in or below one mem_exclusive cpuset should not cause swapping on nodes
in another non-overlapping mem_exclusive cpuset, nor provoke oom_killing of a
task in another such cpuset. Heavy use of kernel memory for i/o caching and
such by one job should not impact the memory available to jobs in other
non-overlapping mem_exclusive cpusets.
This patch enables providing hardwall, inescapable cpusets for memory
allocations of each job, while sharing kernel memory allocations between
several jobs, in an enclosing mem_exclusive cpuset.
Like Dinakar's patch earlier to enable administering sched domains using the
cpu_exclusive flag, this patch also provides a useful meaning to a cpuset flag
that had previously done nothing much useful other than restrict what cpuset
configurations were allowed.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-09-07 02:18:12 +04:00
|
|
|
*
|
[PATCH] cpuset: rework cpuset_zone_allowed api
Elaborate the API for calling cpuset_zone_allowed(), so that users have to
explicitly choose between the two variants:
cpuset_zone_allowed_hardwall()
cpuset_zone_allowed_softwall()
Until now, whether or not you got the hardwall flavor depended solely on
whether or not you or'd in the __GFP_HARDWALL gfp flag to the gfp_mask
argument.
If you didn't specify __GFP_HARDWALL, you implicitly got the softwall
version.
Unfortunately, this meant that users would end up with the softwall version
without thinking about it. Since only the softwall version might sleep,
this led to bugs with possible sleeping in interrupt context on more than
one occassion.
The hardwall version requires that the current tasks mems_allowed allows
the node of the specified zone (or that you're in interrupt or that
__GFP_THISNODE is set or that you're on a one cpuset system.)
The softwall version, depending on the gfp_mask, might allow a node if it
was allowed in the nearest enclusing cpuset marked mem_exclusive (which
requires taking the cpuset lock 'callback_mutex' to evaluate.)
This patch removes the cpuset_zone_allowed() call, and forces the caller to
explicitly choose between the hardwall and the softwall case.
If the caller wants the gfp_mask to determine this choice, they should (1)
be sure they can sleep or that __GFP_HARDWALL is set, and (2) invoke the
cpuset_zone_allowed_softwall() routine.
This adds another 100 or 200 bytes to the kernel text space, due to the few
lines of nearly duplicate code at the top of both cpuset_zone_allowed_*
routines. It should save a few instructions executed for the calls that
turned into calls of cpuset_zone_allowed_hardwall, thanks to not having to
set (before the call) then check (within the call) the __GFP_HARDWALL flag.
For the most critical call, from get_page_from_freelist(), the same
instructions are executed as before -- the old cpuset_zone_allowed()
routine it used to call is the same code as the
cpuset_zone_allowed_softwall() routine that it calls now.
Not a perfect win, but seems worth it, to reduce this chance of hitting a
sleeping with irq off complaint again.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-12-13 11:34:25 +03:00
|
|
|
* Scanning up parent cpusets requires callback_mutex. The
|
|
|
|
* __alloc_pages() routine only calls here with __GFP_HARDWALL bit
|
|
|
|
* _not_ set if it's a GFP_KERNEL allocation, and all nodes in the
|
|
|
|
* current tasks mems_allowed came up empty on the first pass over
|
|
|
|
* the zonelist. So only GFP_KERNEL allocations, if all nodes in the
|
|
|
|
* cpuset are short of memory, might require taking the callback_mutex
|
|
|
|
* mutex.
|
[PATCH] cpusets: formalize intermediate GFP_KERNEL containment
This patch makes use of the previously underutilized cpuset flag
'mem_exclusive' to provide what amounts to another layer of memory placement
resolution. With this patch, there are now the following four layers of
memory placement available:
1) The whole system (interrupt and GFP_ATOMIC allocations can use this),
2) The nearest enclosing mem_exclusive cpuset (GFP_KERNEL allocations can use),
3) The current tasks cpuset (GFP_USER allocations constrained to here), and
4) Specific node placement, using mbind and set_mempolicy.
These nest - each layer is a subset (same or within) of the previous.
Layer (2) above is new, with this patch. The call used to check whether a
zone (its node, actually) is in a cpuset (in its mems_allowed, actually) is
extended to take a gfp_mask argument, and its logic is extended, in the case
that __GFP_HARDWALL is not set in the flag bits, to look up the cpuset
hierarchy for the nearest enclosing mem_exclusive cpuset, to determine if
placement is allowed. The definition of GFP_USER, which used to be identical
to GFP_KERNEL, is changed to also set the __GFP_HARDWALL bit, in the previous
cpuset_gfp_hardwall_flag patch.
GFP_ATOMIC and GFP_KERNEL allocations will stay within the current tasks
cpuset, so long as any node therein is not too tight on memory, but will
escape to the larger layer, if need be.
The intended use is to allow something like a batch manager to handle several
jobs, each job in its own cpuset, but using common kernel memory for caches
and such. Swapper and oom_kill activity is also constrained to Layer (2). A
task in or below one mem_exclusive cpuset should not cause swapping on nodes
in another non-overlapping mem_exclusive cpuset, nor provoke oom_killing of a
task in another such cpuset. Heavy use of kernel memory for i/o caching and
such by one job should not impact the memory available to jobs in other
non-overlapping mem_exclusive cpusets.
This patch enables providing hardwall, inescapable cpusets for memory
allocations of each job, while sharing kernel memory allocations between
several jobs, in an enclosing mem_exclusive cpuset.
Like Dinakar's patch earlier to enable administering sched domains using the
cpu_exclusive flag, this patch also provides a useful meaning to a cpuset flag
that had previously done nothing much useful other than restrict what cpuset
configurations were allowed.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-09-07 02:18:12 +04:00
|
|
|
*
|
2006-05-21 02:00:10 +04:00
|
|
|
* The first call here from mm/page_alloc:get_page_from_freelist()
|
[PATCH] cpuset: rework cpuset_zone_allowed api
Elaborate the API for calling cpuset_zone_allowed(), so that users have to
explicitly choose between the two variants:
cpuset_zone_allowed_hardwall()
cpuset_zone_allowed_softwall()
Until now, whether or not you got the hardwall flavor depended solely on
whether or not you or'd in the __GFP_HARDWALL gfp flag to the gfp_mask
argument.
If you didn't specify __GFP_HARDWALL, you implicitly got the softwall
version.
Unfortunately, this meant that users would end up with the softwall version
without thinking about it. Since only the softwall version might sleep,
this led to bugs with possible sleeping in interrupt context on more than
one occassion.
The hardwall version requires that the current tasks mems_allowed allows
the node of the specified zone (or that you're in interrupt or that
__GFP_THISNODE is set or that you're on a one cpuset system.)
The softwall version, depending on the gfp_mask, might allow a node if it
was allowed in the nearest enclusing cpuset marked mem_exclusive (which
requires taking the cpuset lock 'callback_mutex' to evaluate.)
This patch removes the cpuset_zone_allowed() call, and forces the caller to
explicitly choose between the hardwall and the softwall case.
If the caller wants the gfp_mask to determine this choice, they should (1)
be sure they can sleep or that __GFP_HARDWALL is set, and (2) invoke the
cpuset_zone_allowed_softwall() routine.
This adds another 100 or 200 bytes to the kernel text space, due to the few
lines of nearly duplicate code at the top of both cpuset_zone_allowed_*
routines. It should save a few instructions executed for the calls that
turned into calls of cpuset_zone_allowed_hardwall, thanks to not having to
set (before the call) then check (within the call) the __GFP_HARDWALL flag.
For the most critical call, from get_page_from_freelist(), the same
instructions are executed as before -- the old cpuset_zone_allowed()
routine it used to call is the same code as the
cpuset_zone_allowed_softwall() routine that it calls now.
Not a perfect win, but seems worth it, to reduce this chance of hitting a
sleeping with irq off complaint again.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-12-13 11:34:25 +03:00
|
|
|
* has __GFP_HARDWALL set in gfp_mask, enforcing hardwall cpusets,
|
|
|
|
* so no allocation on a node outside the cpuset is allowed (unless
|
|
|
|
* in interrupt, of course).
|
2006-05-21 02:00:10 +04:00
|
|
|
*
|
|
|
|
* The second pass through get_page_from_freelist() doesn't even call
|
|
|
|
* here for GFP_ATOMIC calls. For those calls, the __alloc_pages()
|
|
|
|
* variable 'wait' is not set, and the bit ALLOC_CPUSET is not set
|
|
|
|
* in alloc_flags. That logic and the checks below have the combined
|
|
|
|
* affect that:
|
[PATCH] cpusets: formalize intermediate GFP_KERNEL containment
This patch makes use of the previously underutilized cpuset flag
'mem_exclusive' to provide what amounts to another layer of memory placement
resolution. With this patch, there are now the following four layers of
memory placement available:
1) The whole system (interrupt and GFP_ATOMIC allocations can use this),
2) The nearest enclosing mem_exclusive cpuset (GFP_KERNEL allocations can use),
3) The current tasks cpuset (GFP_USER allocations constrained to here), and
4) Specific node placement, using mbind and set_mempolicy.
These nest - each layer is a subset (same or within) of the previous.
Layer (2) above is new, with this patch. The call used to check whether a
zone (its node, actually) is in a cpuset (in its mems_allowed, actually) is
extended to take a gfp_mask argument, and its logic is extended, in the case
that __GFP_HARDWALL is not set in the flag bits, to look up the cpuset
hierarchy for the nearest enclosing mem_exclusive cpuset, to determine if
placement is allowed. The definition of GFP_USER, which used to be identical
to GFP_KERNEL, is changed to also set the __GFP_HARDWALL bit, in the previous
cpuset_gfp_hardwall_flag patch.
GFP_ATOMIC and GFP_KERNEL allocations will stay within the current tasks
cpuset, so long as any node therein is not too tight on memory, but will
escape to the larger layer, if need be.
The intended use is to allow something like a batch manager to handle several
jobs, each job in its own cpuset, but using common kernel memory for caches
and such. Swapper and oom_kill activity is also constrained to Layer (2). A
task in or below one mem_exclusive cpuset should not cause swapping on nodes
in another non-overlapping mem_exclusive cpuset, nor provoke oom_killing of a
task in another such cpuset. Heavy use of kernel memory for i/o caching and
such by one job should not impact the memory available to jobs in other
non-overlapping mem_exclusive cpusets.
This patch enables providing hardwall, inescapable cpusets for memory
allocations of each job, while sharing kernel memory allocations between
several jobs, in an enclosing mem_exclusive cpuset.
Like Dinakar's patch earlier to enable administering sched domains using the
cpu_exclusive flag, this patch also provides a useful meaning to a cpuset flag
that had previously done nothing much useful other than restrict what cpuset
configurations were allowed.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-09-07 02:18:12 +04:00
|
|
|
* in_interrupt - any node ok (current task context irrelevant)
|
|
|
|
* GFP_ATOMIC - any node ok
|
2007-05-07 01:49:32 +04:00
|
|
|
* TIF_MEMDIE - any node ok
|
2008-04-29 12:00:26 +04:00
|
|
|
* GFP_KERNEL - any node in enclosing hardwalled cpuset ok
|
[PATCH] cpusets: formalize intermediate GFP_KERNEL containment
This patch makes use of the previously underutilized cpuset flag
'mem_exclusive' to provide what amounts to another layer of memory placement
resolution. With this patch, there are now the following four layers of
memory placement available:
1) The whole system (interrupt and GFP_ATOMIC allocations can use this),
2) The nearest enclosing mem_exclusive cpuset (GFP_KERNEL allocations can use),
3) The current tasks cpuset (GFP_USER allocations constrained to here), and
4) Specific node placement, using mbind and set_mempolicy.
These nest - each layer is a subset (same or within) of the previous.
Layer (2) above is new, with this patch. The call used to check whether a
zone (its node, actually) is in a cpuset (in its mems_allowed, actually) is
extended to take a gfp_mask argument, and its logic is extended, in the case
that __GFP_HARDWALL is not set in the flag bits, to look up the cpuset
hierarchy for the nearest enclosing mem_exclusive cpuset, to determine if
placement is allowed. The definition of GFP_USER, which used to be identical
to GFP_KERNEL, is changed to also set the __GFP_HARDWALL bit, in the previous
cpuset_gfp_hardwall_flag patch.
GFP_ATOMIC and GFP_KERNEL allocations will stay within the current tasks
cpuset, so long as any node therein is not too tight on memory, but will
escape to the larger layer, if need be.
The intended use is to allow something like a batch manager to handle several
jobs, each job in its own cpuset, but using common kernel memory for caches
and such. Swapper and oom_kill activity is also constrained to Layer (2). A
task in or below one mem_exclusive cpuset should not cause swapping on nodes
in another non-overlapping mem_exclusive cpuset, nor provoke oom_killing of a
task in another such cpuset. Heavy use of kernel memory for i/o caching and
such by one job should not impact the memory available to jobs in other
non-overlapping mem_exclusive cpusets.
This patch enables providing hardwall, inescapable cpusets for memory
allocations of each job, while sharing kernel memory allocations between
several jobs, in an enclosing mem_exclusive cpuset.
Like Dinakar's patch earlier to enable administering sched domains using the
cpu_exclusive flag, this patch also provides a useful meaning to a cpuset flag
that had previously done nothing much useful other than restrict what cpuset
configurations were allowed.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-09-07 02:18:12 +04:00
|
|
|
* GFP_USER - only nodes in current tasks mems allowed ok.
|
2006-05-21 02:00:10 +04:00
|
|
|
*
|
|
|
|
* Rule:
|
[PATCH] cpuset: rework cpuset_zone_allowed api
Elaborate the API for calling cpuset_zone_allowed(), so that users have to
explicitly choose between the two variants:
cpuset_zone_allowed_hardwall()
cpuset_zone_allowed_softwall()
Until now, whether or not you got the hardwall flavor depended solely on
whether or not you or'd in the __GFP_HARDWALL gfp flag to the gfp_mask
argument.
If you didn't specify __GFP_HARDWALL, you implicitly got the softwall
version.
Unfortunately, this meant that users would end up with the softwall version
without thinking about it. Since only the softwall version might sleep,
this led to bugs with possible sleeping in interrupt context on more than
one occassion.
The hardwall version requires that the current tasks mems_allowed allows
the node of the specified zone (or that you're in interrupt or that
__GFP_THISNODE is set or that you're on a one cpuset system.)
The softwall version, depending on the gfp_mask, might allow a node if it
was allowed in the nearest enclusing cpuset marked mem_exclusive (which
requires taking the cpuset lock 'callback_mutex' to evaluate.)
This patch removes the cpuset_zone_allowed() call, and forces the caller to
explicitly choose between the hardwall and the softwall case.
If the caller wants the gfp_mask to determine this choice, they should (1)
be sure they can sleep or that __GFP_HARDWALL is set, and (2) invoke the
cpuset_zone_allowed_softwall() routine.
This adds another 100 or 200 bytes to the kernel text space, due to the few
lines of nearly duplicate code at the top of both cpuset_zone_allowed_*
routines. It should save a few instructions executed for the calls that
turned into calls of cpuset_zone_allowed_hardwall, thanks to not having to
set (before the call) then check (within the call) the __GFP_HARDWALL flag.
For the most critical call, from get_page_from_freelist(), the same
instructions are executed as before -- the old cpuset_zone_allowed()
routine it used to call is the same code as the
cpuset_zone_allowed_softwall() routine that it calls now.
Not a perfect win, but seems worth it, to reduce this chance of hitting a
sleeping with irq off complaint again.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-12-13 11:34:25 +03:00
|
|
|
* Don't call cpuset_zone_allowed_softwall if you can't sleep, unless you
|
2006-05-21 02:00:10 +04:00
|
|
|
* pass in the __GFP_HARDWALL flag set in gfp_flag, which disables
|
|
|
|
* the code that might scan up ancestor cpusets and sleep.
|
[PATCH] cpuset: rework cpuset_zone_allowed api
Elaborate the API for calling cpuset_zone_allowed(), so that users have to
explicitly choose between the two variants:
cpuset_zone_allowed_hardwall()
cpuset_zone_allowed_softwall()
Until now, whether or not you got the hardwall flavor depended solely on
whether or not you or'd in the __GFP_HARDWALL gfp flag to the gfp_mask
argument.
If you didn't specify __GFP_HARDWALL, you implicitly got the softwall
version.
Unfortunately, this meant that users would end up with the softwall version
without thinking about it. Since only the softwall version might sleep,
this led to bugs with possible sleeping in interrupt context on more than
one occassion.
The hardwall version requires that the current tasks mems_allowed allows
the node of the specified zone (or that you're in interrupt or that
__GFP_THISNODE is set or that you're on a one cpuset system.)
The softwall version, depending on the gfp_mask, might allow a node if it
was allowed in the nearest enclusing cpuset marked mem_exclusive (which
requires taking the cpuset lock 'callback_mutex' to evaluate.)
This patch removes the cpuset_zone_allowed() call, and forces the caller to
explicitly choose between the hardwall and the softwall case.
If the caller wants the gfp_mask to determine this choice, they should (1)
be sure they can sleep or that __GFP_HARDWALL is set, and (2) invoke the
cpuset_zone_allowed_softwall() routine.
This adds another 100 or 200 bytes to the kernel text space, due to the few
lines of nearly duplicate code at the top of both cpuset_zone_allowed_*
routines. It should save a few instructions executed for the calls that
turned into calls of cpuset_zone_allowed_hardwall, thanks to not having to
set (before the call) then check (within the call) the __GFP_HARDWALL flag.
For the most critical call, from get_page_from_freelist(), the same
instructions are executed as before -- the old cpuset_zone_allowed()
routine it used to call is the same code as the
cpuset_zone_allowed_softwall() routine that it calls now.
Not a perfect win, but seems worth it, to reduce this chance of hitting a
sleeping with irq off complaint again.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-12-13 11:34:25 +03:00
|
|
|
*/
|
[PATCH] cpusets: formalize intermediate GFP_KERNEL containment
This patch makes use of the previously underutilized cpuset flag
'mem_exclusive' to provide what amounts to another layer of memory placement
resolution. With this patch, there are now the following four layers of
memory placement available:
1) The whole system (interrupt and GFP_ATOMIC allocations can use this),
2) The nearest enclosing mem_exclusive cpuset (GFP_KERNEL allocations can use),
3) The current tasks cpuset (GFP_USER allocations constrained to here), and
4) Specific node placement, using mbind and set_mempolicy.
These nest - each layer is a subset (same or within) of the previous.
Layer (2) above is new, with this patch. The call used to check whether a
zone (its node, actually) is in a cpuset (in its mems_allowed, actually) is
extended to take a gfp_mask argument, and its logic is extended, in the case
that __GFP_HARDWALL is not set in the flag bits, to look up the cpuset
hierarchy for the nearest enclosing mem_exclusive cpuset, to determine if
placement is allowed. The definition of GFP_USER, which used to be identical
to GFP_KERNEL, is changed to also set the __GFP_HARDWALL bit, in the previous
cpuset_gfp_hardwall_flag patch.
GFP_ATOMIC and GFP_KERNEL allocations will stay within the current tasks
cpuset, so long as any node therein is not too tight on memory, but will
escape to the larger layer, if need be.
The intended use is to allow something like a batch manager to handle several
jobs, each job in its own cpuset, but using common kernel memory for caches
and such. Swapper and oom_kill activity is also constrained to Layer (2). A
task in or below one mem_exclusive cpuset should not cause swapping on nodes
in another non-overlapping mem_exclusive cpuset, nor provoke oom_killing of a
task in another such cpuset. Heavy use of kernel memory for i/o caching and
such by one job should not impact the memory available to jobs in other
non-overlapping mem_exclusive cpusets.
This patch enables providing hardwall, inescapable cpusets for memory
allocations of each job, while sharing kernel memory allocations between
several jobs, in an enclosing mem_exclusive cpuset.
Like Dinakar's patch earlier to enable administering sched domains using the
cpu_exclusive flag, this patch also provides a useful meaning to a cpuset flag
that had previously done nothing much useful other than restrict what cpuset
configurations were allowed.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-09-07 02:18:12 +04:00
|
|
|
|
[PATCH] cpuset: rework cpuset_zone_allowed api
Elaborate the API for calling cpuset_zone_allowed(), so that users have to
explicitly choose between the two variants:
cpuset_zone_allowed_hardwall()
cpuset_zone_allowed_softwall()
Until now, whether or not you got the hardwall flavor depended solely on
whether or not you or'd in the __GFP_HARDWALL gfp flag to the gfp_mask
argument.
If you didn't specify __GFP_HARDWALL, you implicitly got the softwall
version.
Unfortunately, this meant that users would end up with the softwall version
without thinking about it. Since only the softwall version might sleep,
this led to bugs with possible sleeping in interrupt context on more than
one occassion.
The hardwall version requires that the current tasks mems_allowed allows
the node of the specified zone (or that you're in interrupt or that
__GFP_THISNODE is set or that you're on a one cpuset system.)
The softwall version, depending on the gfp_mask, might allow a node if it
was allowed in the nearest enclusing cpuset marked mem_exclusive (which
requires taking the cpuset lock 'callback_mutex' to evaluate.)
This patch removes the cpuset_zone_allowed() call, and forces the caller to
explicitly choose between the hardwall and the softwall case.
If the caller wants the gfp_mask to determine this choice, they should (1)
be sure they can sleep or that __GFP_HARDWALL is set, and (2) invoke the
cpuset_zone_allowed_softwall() routine.
This adds another 100 or 200 bytes to the kernel text space, due to the few
lines of nearly duplicate code at the top of both cpuset_zone_allowed_*
routines. It should save a few instructions executed for the calls that
turned into calls of cpuset_zone_allowed_hardwall, thanks to not having to
set (before the call) then check (within the call) the __GFP_HARDWALL flag.
For the most critical call, from get_page_from_freelist(), the same
instructions are executed as before -- the old cpuset_zone_allowed()
routine it used to call is the same code as the
cpuset_zone_allowed_softwall() routine that it calls now.
Not a perfect win, but seems worth it, to reduce this chance of hitting a
sleeping with irq off complaint again.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-12-13 11:34:25 +03:00
|
|
|
int __cpuset_zone_allowed_softwall(struct zone *z, gfp_t gfp_mask)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
[PATCH] cpusets: formalize intermediate GFP_KERNEL containment
This patch makes use of the previously underutilized cpuset flag
'mem_exclusive' to provide what amounts to another layer of memory placement
resolution. With this patch, there are now the following four layers of
memory placement available:
1) The whole system (interrupt and GFP_ATOMIC allocations can use this),
2) The nearest enclosing mem_exclusive cpuset (GFP_KERNEL allocations can use),
3) The current tasks cpuset (GFP_USER allocations constrained to here), and
4) Specific node placement, using mbind and set_mempolicy.
These nest - each layer is a subset (same or within) of the previous.
Layer (2) above is new, with this patch. The call used to check whether a
zone (its node, actually) is in a cpuset (in its mems_allowed, actually) is
extended to take a gfp_mask argument, and its logic is extended, in the case
that __GFP_HARDWALL is not set in the flag bits, to look up the cpuset
hierarchy for the nearest enclosing mem_exclusive cpuset, to determine if
placement is allowed. The definition of GFP_USER, which used to be identical
to GFP_KERNEL, is changed to also set the __GFP_HARDWALL bit, in the previous
cpuset_gfp_hardwall_flag patch.
GFP_ATOMIC and GFP_KERNEL allocations will stay within the current tasks
cpuset, so long as any node therein is not too tight on memory, but will
escape to the larger layer, if need be.
The intended use is to allow something like a batch manager to handle several
jobs, each job in its own cpuset, but using common kernel memory for caches
and such. Swapper and oom_kill activity is also constrained to Layer (2). A
task in or below one mem_exclusive cpuset should not cause swapping on nodes
in another non-overlapping mem_exclusive cpuset, nor provoke oom_killing of a
task in another such cpuset. Heavy use of kernel memory for i/o caching and
such by one job should not impact the memory available to jobs in other
non-overlapping mem_exclusive cpusets.
This patch enables providing hardwall, inescapable cpusets for memory
allocations of each job, while sharing kernel memory allocations between
several jobs, in an enclosing mem_exclusive cpuset.
Like Dinakar's patch earlier to enable administering sched domains using the
cpu_exclusive flag, this patch also provides a useful meaning to a cpuset flag
that had previously done nothing much useful other than restrict what cpuset
configurations were allowed.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-09-07 02:18:12 +04:00
|
|
|
int node; /* node that zone z is on */
|
|
|
|
const struct cpuset *cs; /* current cpuset ancestors */
|
2006-03-24 14:16:12 +03:00
|
|
|
int allowed; /* is allocation in zone z allowed? */
|
[PATCH] cpusets: formalize intermediate GFP_KERNEL containment
This patch makes use of the previously underutilized cpuset flag
'mem_exclusive' to provide what amounts to another layer of memory placement
resolution. With this patch, there are now the following four layers of
memory placement available:
1) The whole system (interrupt and GFP_ATOMIC allocations can use this),
2) The nearest enclosing mem_exclusive cpuset (GFP_KERNEL allocations can use),
3) The current tasks cpuset (GFP_USER allocations constrained to here), and
4) Specific node placement, using mbind and set_mempolicy.
These nest - each layer is a subset (same or within) of the previous.
Layer (2) above is new, with this patch. The call used to check whether a
zone (its node, actually) is in a cpuset (in its mems_allowed, actually) is
extended to take a gfp_mask argument, and its logic is extended, in the case
that __GFP_HARDWALL is not set in the flag bits, to look up the cpuset
hierarchy for the nearest enclosing mem_exclusive cpuset, to determine if
placement is allowed. The definition of GFP_USER, which used to be identical
to GFP_KERNEL, is changed to also set the __GFP_HARDWALL bit, in the previous
cpuset_gfp_hardwall_flag patch.
GFP_ATOMIC and GFP_KERNEL allocations will stay within the current tasks
cpuset, so long as any node therein is not too tight on memory, but will
escape to the larger layer, if need be.
The intended use is to allow something like a batch manager to handle several
jobs, each job in its own cpuset, but using common kernel memory for caches
and such. Swapper and oom_kill activity is also constrained to Layer (2). A
task in or below one mem_exclusive cpuset should not cause swapping on nodes
in another non-overlapping mem_exclusive cpuset, nor provoke oom_killing of a
task in another such cpuset. Heavy use of kernel memory for i/o caching and
such by one job should not impact the memory available to jobs in other
non-overlapping mem_exclusive cpusets.
This patch enables providing hardwall, inescapable cpusets for memory
allocations of each job, while sharing kernel memory allocations between
several jobs, in an enclosing mem_exclusive cpuset.
Like Dinakar's patch earlier to enable administering sched domains using the
cpu_exclusive flag, this patch also provides a useful meaning to a cpuset flag
that had previously done nothing much useful other than restrict what cpuset
configurations were allowed.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-09-07 02:18:12 +04:00
|
|
|
|
2006-09-26 10:31:40 +04:00
|
|
|
if (in_interrupt() || (gfp_mask & __GFP_THISNODE))
|
[PATCH] cpusets: formalize intermediate GFP_KERNEL containment
This patch makes use of the previously underutilized cpuset flag
'mem_exclusive' to provide what amounts to another layer of memory placement
resolution. With this patch, there are now the following four layers of
memory placement available:
1) The whole system (interrupt and GFP_ATOMIC allocations can use this),
2) The nearest enclosing mem_exclusive cpuset (GFP_KERNEL allocations can use),
3) The current tasks cpuset (GFP_USER allocations constrained to here), and
4) Specific node placement, using mbind and set_mempolicy.
These nest - each layer is a subset (same or within) of the previous.
Layer (2) above is new, with this patch. The call used to check whether a
zone (its node, actually) is in a cpuset (in its mems_allowed, actually) is
extended to take a gfp_mask argument, and its logic is extended, in the case
that __GFP_HARDWALL is not set in the flag bits, to look up the cpuset
hierarchy for the nearest enclosing mem_exclusive cpuset, to determine if
placement is allowed. The definition of GFP_USER, which used to be identical
to GFP_KERNEL, is changed to also set the __GFP_HARDWALL bit, in the previous
cpuset_gfp_hardwall_flag patch.
GFP_ATOMIC and GFP_KERNEL allocations will stay within the current tasks
cpuset, so long as any node therein is not too tight on memory, but will
escape to the larger layer, if need be.
The intended use is to allow something like a batch manager to handle several
jobs, each job in its own cpuset, but using common kernel memory for caches
and such. Swapper and oom_kill activity is also constrained to Layer (2). A
task in or below one mem_exclusive cpuset should not cause swapping on nodes
in another non-overlapping mem_exclusive cpuset, nor provoke oom_killing of a
task in another such cpuset. Heavy use of kernel memory for i/o caching and
such by one job should not impact the memory available to jobs in other
non-overlapping mem_exclusive cpusets.
This patch enables providing hardwall, inescapable cpusets for memory
allocations of each job, while sharing kernel memory allocations between
several jobs, in an enclosing mem_exclusive cpuset.
Like Dinakar's patch earlier to enable administering sched domains using the
cpu_exclusive flag, this patch also provides a useful meaning to a cpuset flag
that had previously done nothing much useful other than restrict what cpuset
configurations were allowed.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-09-07 02:18:12 +04:00
|
|
|
return 1;
|
2006-09-26 10:31:55 +04:00
|
|
|
node = zone_to_nid(z);
|
2006-05-21 02:00:11 +04:00
|
|
|
might_sleep_if(!(gfp_mask & __GFP_HARDWALL));
|
[PATCH] cpusets: formalize intermediate GFP_KERNEL containment
This patch makes use of the previously underutilized cpuset flag
'mem_exclusive' to provide what amounts to another layer of memory placement
resolution. With this patch, there are now the following four layers of
memory placement available:
1) The whole system (interrupt and GFP_ATOMIC allocations can use this),
2) The nearest enclosing mem_exclusive cpuset (GFP_KERNEL allocations can use),
3) The current tasks cpuset (GFP_USER allocations constrained to here), and
4) Specific node placement, using mbind and set_mempolicy.
These nest - each layer is a subset (same or within) of the previous.
Layer (2) above is new, with this patch. The call used to check whether a
zone (its node, actually) is in a cpuset (in its mems_allowed, actually) is
extended to take a gfp_mask argument, and its logic is extended, in the case
that __GFP_HARDWALL is not set in the flag bits, to look up the cpuset
hierarchy for the nearest enclosing mem_exclusive cpuset, to determine if
placement is allowed. The definition of GFP_USER, which used to be identical
to GFP_KERNEL, is changed to also set the __GFP_HARDWALL bit, in the previous
cpuset_gfp_hardwall_flag patch.
GFP_ATOMIC and GFP_KERNEL allocations will stay within the current tasks
cpuset, so long as any node therein is not too tight on memory, but will
escape to the larger layer, if need be.
The intended use is to allow something like a batch manager to handle several
jobs, each job in its own cpuset, but using common kernel memory for caches
and such. Swapper and oom_kill activity is also constrained to Layer (2). A
task in or below one mem_exclusive cpuset should not cause swapping on nodes
in another non-overlapping mem_exclusive cpuset, nor provoke oom_killing of a
task in another such cpuset. Heavy use of kernel memory for i/o caching and
such by one job should not impact the memory available to jobs in other
non-overlapping mem_exclusive cpusets.
This patch enables providing hardwall, inescapable cpusets for memory
allocations of each job, while sharing kernel memory allocations between
several jobs, in an enclosing mem_exclusive cpuset.
Like Dinakar's patch earlier to enable administering sched domains using the
cpu_exclusive flag, this patch also provides a useful meaning to a cpuset flag
that had previously done nothing much useful other than restrict what cpuset
configurations were allowed.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-09-07 02:18:12 +04:00
|
|
|
if (node_isset(node, current->mems_allowed))
|
|
|
|
return 1;
|
2007-05-07 01:49:32 +04:00
|
|
|
/*
|
|
|
|
* Allow tasks that have access to memory reserves because they have
|
|
|
|
* been OOM killed to get memory anywhere.
|
|
|
|
*/
|
|
|
|
if (unlikely(test_thread_flag(TIF_MEMDIE)))
|
|
|
|
return 1;
|
[PATCH] cpusets: formalize intermediate GFP_KERNEL containment
This patch makes use of the previously underutilized cpuset flag
'mem_exclusive' to provide what amounts to another layer of memory placement
resolution. With this patch, there are now the following four layers of
memory placement available:
1) The whole system (interrupt and GFP_ATOMIC allocations can use this),
2) The nearest enclosing mem_exclusive cpuset (GFP_KERNEL allocations can use),
3) The current tasks cpuset (GFP_USER allocations constrained to here), and
4) Specific node placement, using mbind and set_mempolicy.
These nest - each layer is a subset (same or within) of the previous.
Layer (2) above is new, with this patch. The call used to check whether a
zone (its node, actually) is in a cpuset (in its mems_allowed, actually) is
extended to take a gfp_mask argument, and its logic is extended, in the case
that __GFP_HARDWALL is not set in the flag bits, to look up the cpuset
hierarchy for the nearest enclosing mem_exclusive cpuset, to determine if
placement is allowed. The definition of GFP_USER, which used to be identical
to GFP_KERNEL, is changed to also set the __GFP_HARDWALL bit, in the previous
cpuset_gfp_hardwall_flag patch.
GFP_ATOMIC and GFP_KERNEL allocations will stay within the current tasks
cpuset, so long as any node therein is not too tight on memory, but will
escape to the larger layer, if need be.
The intended use is to allow something like a batch manager to handle several
jobs, each job in its own cpuset, but using common kernel memory for caches
and such. Swapper and oom_kill activity is also constrained to Layer (2). A
task in or below one mem_exclusive cpuset should not cause swapping on nodes
in another non-overlapping mem_exclusive cpuset, nor provoke oom_killing of a
task in another such cpuset. Heavy use of kernel memory for i/o caching and
such by one job should not impact the memory available to jobs in other
non-overlapping mem_exclusive cpusets.
This patch enables providing hardwall, inescapable cpusets for memory
allocations of each job, while sharing kernel memory allocations between
several jobs, in an enclosing mem_exclusive cpuset.
Like Dinakar's patch earlier to enable administering sched domains using the
cpu_exclusive flag, this patch also provides a useful meaning to a cpuset flag
that had previously done nothing much useful other than restrict what cpuset
configurations were allowed.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-09-07 02:18:12 +04:00
|
|
|
if (gfp_mask & __GFP_HARDWALL) /* If hardwall request, stop here */
|
|
|
|
return 0;
|
|
|
|
|
2005-11-14 03:06:35 +03:00
|
|
|
if (current->flags & PF_EXITING) /* Let dying task have memory */
|
|
|
|
return 1;
|
|
|
|
|
[PATCH] cpusets: formalize intermediate GFP_KERNEL containment
This patch makes use of the previously underutilized cpuset flag
'mem_exclusive' to provide what amounts to another layer of memory placement
resolution. With this patch, there are now the following four layers of
memory placement available:
1) The whole system (interrupt and GFP_ATOMIC allocations can use this),
2) The nearest enclosing mem_exclusive cpuset (GFP_KERNEL allocations can use),
3) The current tasks cpuset (GFP_USER allocations constrained to here), and
4) Specific node placement, using mbind and set_mempolicy.
These nest - each layer is a subset (same or within) of the previous.
Layer (2) above is new, with this patch. The call used to check whether a
zone (its node, actually) is in a cpuset (in its mems_allowed, actually) is
extended to take a gfp_mask argument, and its logic is extended, in the case
that __GFP_HARDWALL is not set in the flag bits, to look up the cpuset
hierarchy for the nearest enclosing mem_exclusive cpuset, to determine if
placement is allowed. The definition of GFP_USER, which used to be identical
to GFP_KERNEL, is changed to also set the __GFP_HARDWALL bit, in the previous
cpuset_gfp_hardwall_flag patch.
GFP_ATOMIC and GFP_KERNEL allocations will stay within the current tasks
cpuset, so long as any node therein is not too tight on memory, but will
escape to the larger layer, if need be.
The intended use is to allow something like a batch manager to handle several
jobs, each job in its own cpuset, but using common kernel memory for caches
and such. Swapper and oom_kill activity is also constrained to Layer (2). A
task in or below one mem_exclusive cpuset should not cause swapping on nodes
in another non-overlapping mem_exclusive cpuset, nor provoke oom_killing of a
task in another such cpuset. Heavy use of kernel memory for i/o caching and
such by one job should not impact the memory available to jobs in other
non-overlapping mem_exclusive cpusets.
This patch enables providing hardwall, inescapable cpusets for memory
allocations of each job, while sharing kernel memory allocations between
several jobs, in an enclosing mem_exclusive cpuset.
Like Dinakar's patch earlier to enable administering sched domains using the
cpu_exclusive flag, this patch also provides a useful meaning to a cpuset flag
that had previously done nothing much useful other than restrict what cpuset
configurations were allowed.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-09-07 02:18:12 +04:00
|
|
|
/* Not hardwall and node outside mems_allowed: scan up cpusets */
|
2006-03-23 14:00:18 +03:00
|
|
|
mutex_lock(&callback_mutex);
|
[PATCH] cpusets: dual semaphore locking overhaul
Overhaul cpuset locking. Replace single semaphore with two semaphores.
The suggestion to use two locks was made by Roman Zippel.
Both locks are global. Code that wants to modify cpusets must first
acquire the exclusive manage_sem, which allows them read-only access to
cpusets, and holds off other would-be modifiers. Before making actual
changes, the second semaphore, callback_sem must be acquired as well. Code
that needs only to query cpusets must acquire callback_sem, which is also a
global exclusive lock.
The earlier problems with double tripping are avoided, because it is
allowed for holders of manage_sem to nest the second callback_sem lock, and
only callback_sem is needed by code called from within __alloc_pages(),
where the double tripping had been possible.
This is not quite the same as a normal read/write semaphore, because
obtaining read-only access with intent to change must hold off other such
attempts, while allowing read-only access w/o such intention. Changing
cpusets involves several related checks and changes, which must be done
while allowing read-only queries (to avoid the double trip), but while
ensuring nothing changes (holding off other would be modifiers.)
This overhaul of cpuset locking also makes careful use of task_lock() to
guard access to the task->cpuset pointer, closing a couple of race
conditions noticed while reading this code (thanks, Roman). I've never
seen these races fail in any use or test.
See further the comments in the code.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-10-31 02:02:30 +03:00
|
|
|
|
|
|
|
task_lock(current);
|
2008-04-29 12:00:26 +04:00
|
|
|
cs = nearest_hardwall_ancestor(task_cs(current));
|
[PATCH] cpusets: dual semaphore locking overhaul
Overhaul cpuset locking. Replace single semaphore with two semaphores.
The suggestion to use two locks was made by Roman Zippel.
Both locks are global. Code that wants to modify cpusets must first
acquire the exclusive manage_sem, which allows them read-only access to
cpusets, and holds off other would-be modifiers. Before making actual
changes, the second semaphore, callback_sem must be acquired as well. Code
that needs only to query cpusets must acquire callback_sem, which is also a
global exclusive lock.
The earlier problems with double tripping are avoided, because it is
allowed for holders of manage_sem to nest the second callback_sem lock, and
only callback_sem is needed by code called from within __alloc_pages(),
where the double tripping had been possible.
This is not quite the same as a normal read/write semaphore, because
obtaining read-only access with intent to change must hold off other such
attempts, while allowing read-only access w/o such intention. Changing
cpusets involves several related checks and changes, which must be done
while allowing read-only queries (to avoid the double trip), but while
ensuring nothing changes (holding off other would be modifiers.)
This overhaul of cpuset locking also makes careful use of task_lock() to
guard access to the task->cpuset pointer, closing a couple of race
conditions noticed while reading this code (thanks, Roman). I've never
seen these races fail in any use or test.
See further the comments in the code.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-10-31 02:02:30 +03:00
|
|
|
task_unlock(current);
|
|
|
|
|
[PATCH] cpusets: formalize intermediate GFP_KERNEL containment
This patch makes use of the previously underutilized cpuset flag
'mem_exclusive' to provide what amounts to another layer of memory placement
resolution. With this patch, there are now the following four layers of
memory placement available:
1) The whole system (interrupt and GFP_ATOMIC allocations can use this),
2) The nearest enclosing mem_exclusive cpuset (GFP_KERNEL allocations can use),
3) The current tasks cpuset (GFP_USER allocations constrained to here), and
4) Specific node placement, using mbind and set_mempolicy.
These nest - each layer is a subset (same or within) of the previous.
Layer (2) above is new, with this patch. The call used to check whether a
zone (its node, actually) is in a cpuset (in its mems_allowed, actually) is
extended to take a gfp_mask argument, and its logic is extended, in the case
that __GFP_HARDWALL is not set in the flag bits, to look up the cpuset
hierarchy for the nearest enclosing mem_exclusive cpuset, to determine if
placement is allowed. The definition of GFP_USER, which used to be identical
to GFP_KERNEL, is changed to also set the __GFP_HARDWALL bit, in the previous
cpuset_gfp_hardwall_flag patch.
GFP_ATOMIC and GFP_KERNEL allocations will stay within the current tasks
cpuset, so long as any node therein is not too tight on memory, but will
escape to the larger layer, if need be.
The intended use is to allow something like a batch manager to handle several
jobs, each job in its own cpuset, but using common kernel memory for caches
and such. Swapper and oom_kill activity is also constrained to Layer (2). A
task in or below one mem_exclusive cpuset should not cause swapping on nodes
in another non-overlapping mem_exclusive cpuset, nor provoke oom_killing of a
task in another such cpuset. Heavy use of kernel memory for i/o caching and
such by one job should not impact the memory available to jobs in other
non-overlapping mem_exclusive cpusets.
This patch enables providing hardwall, inescapable cpusets for memory
allocations of each job, while sharing kernel memory allocations between
several jobs, in an enclosing mem_exclusive cpuset.
Like Dinakar's patch earlier to enable administering sched domains using the
cpu_exclusive flag, this patch also provides a useful meaning to a cpuset flag
that had previously done nothing much useful other than restrict what cpuset
configurations were allowed.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-09-07 02:18:12 +04:00
|
|
|
allowed = node_isset(node, cs->mems_allowed);
|
2006-03-23 14:00:18 +03:00
|
|
|
mutex_unlock(&callback_mutex);
|
[PATCH] cpusets: formalize intermediate GFP_KERNEL containment
This patch makes use of the previously underutilized cpuset flag
'mem_exclusive' to provide what amounts to another layer of memory placement
resolution. With this patch, there are now the following four layers of
memory placement available:
1) The whole system (interrupt and GFP_ATOMIC allocations can use this),
2) The nearest enclosing mem_exclusive cpuset (GFP_KERNEL allocations can use),
3) The current tasks cpuset (GFP_USER allocations constrained to here), and
4) Specific node placement, using mbind and set_mempolicy.
These nest - each layer is a subset (same or within) of the previous.
Layer (2) above is new, with this patch. The call used to check whether a
zone (its node, actually) is in a cpuset (in its mems_allowed, actually) is
extended to take a gfp_mask argument, and its logic is extended, in the case
that __GFP_HARDWALL is not set in the flag bits, to look up the cpuset
hierarchy for the nearest enclosing mem_exclusive cpuset, to determine if
placement is allowed. The definition of GFP_USER, which used to be identical
to GFP_KERNEL, is changed to also set the __GFP_HARDWALL bit, in the previous
cpuset_gfp_hardwall_flag patch.
GFP_ATOMIC and GFP_KERNEL allocations will stay within the current tasks
cpuset, so long as any node therein is not too tight on memory, but will
escape to the larger layer, if need be.
The intended use is to allow something like a batch manager to handle several
jobs, each job in its own cpuset, but using common kernel memory for caches
and such. Swapper and oom_kill activity is also constrained to Layer (2). A
task in or below one mem_exclusive cpuset should not cause swapping on nodes
in another non-overlapping mem_exclusive cpuset, nor provoke oom_killing of a
task in another such cpuset. Heavy use of kernel memory for i/o caching and
such by one job should not impact the memory available to jobs in other
non-overlapping mem_exclusive cpusets.
This patch enables providing hardwall, inescapable cpusets for memory
allocations of each job, while sharing kernel memory allocations between
several jobs, in an enclosing mem_exclusive cpuset.
Like Dinakar's patch earlier to enable administering sched domains using the
cpu_exclusive flag, this patch also provides a useful meaning to a cpuset flag
that had previously done nothing much useful other than restrict what cpuset
configurations were allowed.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-09-07 02:18:12 +04:00
|
|
|
return allowed;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
[PATCH] cpuset: rework cpuset_zone_allowed api
Elaborate the API for calling cpuset_zone_allowed(), so that users have to
explicitly choose between the two variants:
cpuset_zone_allowed_hardwall()
cpuset_zone_allowed_softwall()
Until now, whether or not you got the hardwall flavor depended solely on
whether or not you or'd in the __GFP_HARDWALL gfp flag to the gfp_mask
argument.
If you didn't specify __GFP_HARDWALL, you implicitly got the softwall
version.
Unfortunately, this meant that users would end up with the softwall version
without thinking about it. Since only the softwall version might sleep,
this led to bugs with possible sleeping in interrupt context on more than
one occassion.
The hardwall version requires that the current tasks mems_allowed allows
the node of the specified zone (or that you're in interrupt or that
__GFP_THISNODE is set or that you're on a one cpuset system.)
The softwall version, depending on the gfp_mask, might allow a node if it
was allowed in the nearest enclusing cpuset marked mem_exclusive (which
requires taking the cpuset lock 'callback_mutex' to evaluate.)
This patch removes the cpuset_zone_allowed() call, and forces the caller to
explicitly choose between the hardwall and the softwall case.
If the caller wants the gfp_mask to determine this choice, they should (1)
be sure they can sleep or that __GFP_HARDWALL is set, and (2) invoke the
cpuset_zone_allowed_softwall() routine.
This adds another 100 or 200 bytes to the kernel text space, due to the few
lines of nearly duplicate code at the top of both cpuset_zone_allowed_*
routines. It should save a few instructions executed for the calls that
turned into calls of cpuset_zone_allowed_hardwall, thanks to not having to
set (before the call) then check (within the call) the __GFP_HARDWALL flag.
For the most critical call, from get_page_from_freelist(), the same
instructions are executed as before -- the old cpuset_zone_allowed()
routine it used to call is the same code as the
cpuset_zone_allowed_softwall() routine that it calls now.
Not a perfect win, but seems worth it, to reduce this chance of hitting a
sleeping with irq off complaint again.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-12-13 11:34:25 +03:00
|
|
|
/*
|
|
|
|
* cpuset_zone_allowed_hardwall - Can we allocate on zone z's memory node?
|
|
|
|
* @z: is this zone on an allowed node?
|
|
|
|
* @gfp_mask: memory allocation flags
|
|
|
|
*
|
|
|
|
* If we're in interrupt, yes, we can always allocate.
|
|
|
|
* If __GFP_THISNODE is set, yes, we can always allocate. If zone
|
2007-05-07 01:49:32 +04:00
|
|
|
* z's node is in our tasks mems_allowed, yes. If the task has been
|
|
|
|
* OOM killed and has access to memory reserves as specified by the
|
|
|
|
* TIF_MEMDIE flag, yes. Otherwise, no.
|
[PATCH] cpuset: rework cpuset_zone_allowed api
Elaborate the API for calling cpuset_zone_allowed(), so that users have to
explicitly choose between the two variants:
cpuset_zone_allowed_hardwall()
cpuset_zone_allowed_softwall()
Until now, whether or not you got the hardwall flavor depended solely on
whether or not you or'd in the __GFP_HARDWALL gfp flag to the gfp_mask
argument.
If you didn't specify __GFP_HARDWALL, you implicitly got the softwall
version.
Unfortunately, this meant that users would end up with the softwall version
without thinking about it. Since only the softwall version might sleep,
this led to bugs with possible sleeping in interrupt context on more than
one occassion.
The hardwall version requires that the current tasks mems_allowed allows
the node of the specified zone (or that you're in interrupt or that
__GFP_THISNODE is set or that you're on a one cpuset system.)
The softwall version, depending on the gfp_mask, might allow a node if it
was allowed in the nearest enclusing cpuset marked mem_exclusive (which
requires taking the cpuset lock 'callback_mutex' to evaluate.)
This patch removes the cpuset_zone_allowed() call, and forces the caller to
explicitly choose between the hardwall and the softwall case.
If the caller wants the gfp_mask to determine this choice, they should (1)
be sure they can sleep or that __GFP_HARDWALL is set, and (2) invoke the
cpuset_zone_allowed_softwall() routine.
This adds another 100 or 200 bytes to the kernel text space, due to the few
lines of nearly duplicate code at the top of both cpuset_zone_allowed_*
routines. It should save a few instructions executed for the calls that
turned into calls of cpuset_zone_allowed_hardwall, thanks to not having to
set (before the call) then check (within the call) the __GFP_HARDWALL flag.
For the most critical call, from get_page_from_freelist(), the same
instructions are executed as before -- the old cpuset_zone_allowed()
routine it used to call is the same code as the
cpuset_zone_allowed_softwall() routine that it calls now.
Not a perfect win, but seems worth it, to reduce this chance of hitting a
sleeping with irq off complaint again.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-12-13 11:34:25 +03:00
|
|
|
*
|
|
|
|
* The __GFP_THISNODE placement logic is really handled elsewhere,
|
|
|
|
* by forcibly using a zonelist starting at a specified node, and by
|
|
|
|
* (in get_page_from_freelist()) refusing to consider the zones for
|
|
|
|
* any node on the zonelist except the first. By the time any such
|
|
|
|
* calls get to this routine, we should just shut up and say 'yes'.
|
|
|
|
*
|
|
|
|
* Unlike the cpuset_zone_allowed_softwall() variant, above,
|
|
|
|
* this variant requires that the zone be in the current tasks
|
|
|
|
* mems_allowed or that we're in interrupt. It does not scan up the
|
|
|
|
* cpuset hierarchy for the nearest enclosing mem_exclusive cpuset.
|
|
|
|
* It never sleeps.
|
|
|
|
*/
|
|
|
|
|
|
|
|
int __cpuset_zone_allowed_hardwall(struct zone *z, gfp_t gfp_mask)
|
|
|
|
{
|
|
|
|
int node; /* node that zone z is on */
|
|
|
|
|
|
|
|
if (in_interrupt() || (gfp_mask & __GFP_THISNODE))
|
|
|
|
return 1;
|
|
|
|
node = zone_to_nid(z);
|
|
|
|
if (node_isset(node, current->mems_allowed))
|
|
|
|
return 1;
|
2007-10-18 14:06:04 +04:00
|
|
|
/*
|
|
|
|
* Allow tasks that have access to memory reserves because they have
|
|
|
|
* been OOM killed to get memory anywhere.
|
|
|
|
*/
|
|
|
|
if (unlikely(test_thread_flag(TIF_MEMDIE)))
|
|
|
|
return 1;
|
[PATCH] cpuset: rework cpuset_zone_allowed api
Elaborate the API for calling cpuset_zone_allowed(), so that users have to
explicitly choose between the two variants:
cpuset_zone_allowed_hardwall()
cpuset_zone_allowed_softwall()
Until now, whether or not you got the hardwall flavor depended solely on
whether or not you or'd in the __GFP_HARDWALL gfp flag to the gfp_mask
argument.
If you didn't specify __GFP_HARDWALL, you implicitly got the softwall
version.
Unfortunately, this meant that users would end up with the softwall version
without thinking about it. Since only the softwall version might sleep,
this led to bugs with possible sleeping in interrupt context on more than
one occassion.
The hardwall version requires that the current tasks mems_allowed allows
the node of the specified zone (or that you're in interrupt or that
__GFP_THISNODE is set or that you're on a one cpuset system.)
The softwall version, depending on the gfp_mask, might allow a node if it
was allowed in the nearest enclusing cpuset marked mem_exclusive (which
requires taking the cpuset lock 'callback_mutex' to evaluate.)
This patch removes the cpuset_zone_allowed() call, and forces the caller to
explicitly choose between the hardwall and the softwall case.
If the caller wants the gfp_mask to determine this choice, they should (1)
be sure they can sleep or that __GFP_HARDWALL is set, and (2) invoke the
cpuset_zone_allowed_softwall() routine.
This adds another 100 or 200 bytes to the kernel text space, due to the few
lines of nearly duplicate code at the top of both cpuset_zone_allowed_*
routines. It should save a few instructions executed for the calls that
turned into calls of cpuset_zone_allowed_hardwall, thanks to not having to
set (before the call) then check (within the call) the __GFP_HARDWALL flag.
For the most critical call, from get_page_from_freelist(), the same
instructions are executed as before -- the old cpuset_zone_allowed()
routine it used to call is the same code as the
cpuset_zone_allowed_softwall() routine that it calls now.
Not a perfect win, but seems worth it, to reduce this chance of hitting a
sleeping with irq off complaint again.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-12-13 11:34:25 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-01-15 00:21:06 +03:00
|
|
|
/**
|
|
|
|
* cpuset_lock - lock out any changes to cpuset structures
|
|
|
|
*
|
2006-03-23 14:00:18 +03:00
|
|
|
* The out of memory (oom) code needs to mutex_lock cpusets
|
2006-01-15 00:21:06 +03:00
|
|
|
* from being changed while it scans the tasklist looking for a
|
2006-03-23 14:00:18 +03:00
|
|
|
* task in an overlapping cpuset. Expose callback_mutex via this
|
2006-01-15 00:21:06 +03:00
|
|
|
* cpuset_lock() routine, so the oom code can lock it, before
|
|
|
|
* locking the task list. The tasklist_lock is a spinlock, so
|
2006-03-23 14:00:18 +03:00
|
|
|
* must be taken inside callback_mutex.
|
2006-01-15 00:21:06 +03:00
|
|
|
*/
|
|
|
|
|
|
|
|
void cpuset_lock(void)
|
|
|
|
{
|
2006-03-23 14:00:18 +03:00
|
|
|
mutex_lock(&callback_mutex);
|
2006-01-15 00:21:06 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* cpuset_unlock - release lock on cpuset changes
|
|
|
|
*
|
|
|
|
* Undo the lock taken in a previous cpuset_lock() call.
|
|
|
|
*/
|
|
|
|
|
|
|
|
void cpuset_unlock(void)
|
|
|
|
{
|
2006-03-23 14:00:18 +03:00
|
|
|
mutex_unlock(&callback_mutex);
|
2006-01-15 00:21:06 +03:00
|
|
|
}
|
|
|
|
|
[PATCH] cpuset memory spread basic implementation
This patch provides the implementation and cpuset interface for an alternative
memory allocation policy that can be applied to certain kinds of memory
allocations, such as the page cache (file system buffers) and some slab caches
(such as inode caches).
The policy is called "memory spreading." If enabled, it spreads out these
kinds of memory allocations over all the nodes allowed to a task, instead of
preferring to place them on the node where the task is executing.
All other kinds of allocations, including anonymous pages for a tasks stack
and data regions, are not affected by this policy choice, and continue to be
allocated preferring the node local to execution, as modified by the NUMA
mempolicy.
There are two boolean flag files per cpuset that control where the kernel
allocates pages for the file system buffers and related in kernel data
structures. They are called 'memory_spread_page' and 'memory_spread_slab'.
If the per-cpuset boolean flag file 'memory_spread_page' is set, then the
kernel will spread the file system buffers (page cache) evenly over all the
nodes that the faulting task is allowed to use, instead of preferring to put
those pages on the node where the task is running.
If the per-cpuset boolean flag file 'memory_spread_slab' is set, then the
kernel will spread some file system related slab caches, such as for inodes
and dentries evenly over all the nodes that the faulting task is allowed to
use, instead of preferring to put those pages on the node where the task is
running.
The implementation is simple. Setting the cpuset flags 'memory_spread_page'
or 'memory_spread_cache' turns on the per-process flags PF_SPREAD_PAGE or
PF_SPREAD_SLAB, respectively, for each task that is in the cpuset or
subsequently joins that cpuset. In subsequent patches, the page allocation
calls for the affected page cache and slab caches are modified to perform an
inline check for these flags, and if set, a call to a new routine
cpuset_mem_spread_node() returns the node to prefer for the allocation.
The cpuset_mem_spread_node() routine is also simple. It uses the value of a
per-task rotor cpuset_mem_spread_rotor to select the next node in the current
tasks mems_allowed to prefer for the allocation.
This policy can provide substantial improvements for jobs that need to place
thread local data on the corresponding node, but that need to access large
file system data sets that need to be spread across the several nodes in the
jobs cpuset in order to fit. Without this patch, especially for jobs that
might have one thread reading in the data set, the memory allocation across
the nodes in the jobs cpuset can become very uneven.
A couple of Copyright year ranges are updated as well. And a couple of email
addresses that can be found in the MAINTAINERS file are removed.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-03-24 14:16:03 +03:00
|
|
|
/**
|
|
|
|
* cpuset_mem_spread_node() - On which node to begin search for a page
|
|
|
|
*
|
|
|
|
* If a task is marked PF_SPREAD_PAGE or PF_SPREAD_SLAB (as for
|
|
|
|
* tasks in a cpuset with is_spread_page or is_spread_slab set),
|
|
|
|
* and if the memory allocation used cpuset_mem_spread_node()
|
|
|
|
* to determine on which node to start looking, as it will for
|
|
|
|
* certain page cache or slab cache pages such as used for file
|
|
|
|
* system buffers and inode caches, then instead of starting on the
|
|
|
|
* local node to look for a free page, rather spread the starting
|
|
|
|
* node around the tasks mems_allowed nodes.
|
|
|
|
*
|
|
|
|
* We don't have to worry about the returned node being offline
|
|
|
|
* because "it can't happen", and even if it did, it would be ok.
|
|
|
|
*
|
|
|
|
* The routines calling guarantee_online_mems() are careful to
|
|
|
|
* only set nodes in task->mems_allowed that are online. So it
|
|
|
|
* should not be possible for the following code to return an
|
|
|
|
* offline node. But if it did, that would be ok, as this routine
|
|
|
|
* is not returning the node where the allocation must be, only
|
|
|
|
* the node where the search should start. The zonelist passed to
|
|
|
|
* __alloc_pages() will include all nodes. If the slab allocator
|
|
|
|
* is passed an offline node, it will fall back to the local node.
|
|
|
|
* See kmem_cache_alloc_node().
|
|
|
|
*/
|
|
|
|
|
|
|
|
int cpuset_mem_spread_node(void)
|
|
|
|
{
|
|
|
|
int node;
|
|
|
|
|
|
|
|
node = next_node(current->cpuset_mem_spread_rotor, current->mems_allowed);
|
|
|
|
if (node == MAX_NUMNODES)
|
|
|
|
node = first_node(current->mems_allowed);
|
|
|
|
current->cpuset_mem_spread_rotor = node;
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(cpuset_mem_spread_node);
|
|
|
|
|
2005-09-07 02:18:13 +04:00
|
|
|
/**
|
2007-10-17 10:25:58 +04:00
|
|
|
* cpuset_mems_allowed_intersects - Does @tsk1's mems_allowed intersect @tsk2's?
|
|
|
|
* @tsk1: pointer to task_struct of some task.
|
|
|
|
* @tsk2: pointer to task_struct of some other task.
|
|
|
|
*
|
|
|
|
* Description: Return true if @tsk1's mems_allowed intersects the
|
|
|
|
* mems_allowed of @tsk2. Used by the OOM killer to determine if
|
|
|
|
* one of the task's memory usage might impact the memory available
|
|
|
|
* to the other.
|
2005-09-07 02:18:13 +04:00
|
|
|
**/
|
|
|
|
|
2007-10-17 10:25:58 +04:00
|
|
|
int cpuset_mems_allowed_intersects(const struct task_struct *tsk1,
|
|
|
|
const struct task_struct *tsk2)
|
2005-09-07 02:18:13 +04:00
|
|
|
{
|
2007-10-17 10:25:58 +04:00
|
|
|
return nodes_intersects(tsk1->mems_allowed, tsk2->mems_allowed);
|
2005-09-07 02:18:13 +04:00
|
|
|
}
|
|
|
|
|
[PATCH] cpuset: memory pressure meter
Provide a simple per-cpuset metric of memory pressure, tracking the -rate-
that the tasks in a cpuset call try_to_free_pages(), the synchronous
(direct) memory reclaim code.
This enables batch managers monitoring jobs running in dedicated cpusets to
efficiently detect what level of memory pressure that job is causing.
This is useful both on tightly managed systems running a wide mix of
submitted jobs, which may choose to terminate or reprioritize jobs that are
trying to use more memory than allowed on the nodes assigned them, and with
tightly coupled, long running, massively parallel scientific computing jobs
that will dramatically fail to meet required performance goals if they
start to use more memory than allowed to them.
This patch just provides a very economical way for the batch manager to
monitor a cpuset for signs of memory pressure. It's up to the batch
manager or other user code to decide what to do about it and take action.
==> Unless this feature is enabled by writing "1" to the special file
/dev/cpuset/memory_pressure_enabled, the hook in the rebalance
code of __alloc_pages() for this metric reduces to simply noticing
that the cpuset_memory_pressure_enabled flag is zero. So only
systems that enable this feature will compute the metric.
Why a per-cpuset, running average:
Because this meter is per-cpuset, rather than per-task or mm, the
system load imposed by a batch scheduler monitoring this metric is
sharply reduced on large systems, because a scan of the tasklist can be
avoided on each set of queries.
Because this meter is a running average, instead of an accumulating
counter, a batch scheduler can detect memory pressure with a single
read, instead of having to read and accumulate results for a period of
time.
Because this meter is per-cpuset rather than per-task or mm, the
batch scheduler can obtain the key information, memory pressure in a
cpuset, with a single read, rather than having to query and accumulate
results over all the (dynamically changing) set of tasks in the cpuset.
A per-cpuset simple digital filter (requires a spinlock and 3 words of data
per-cpuset) is kept, and updated by any task attached to that cpuset, if it
enters the synchronous (direct) page reclaim code.
A per-cpuset file provides an integer number representing the recent
(half-life of 10 seconds) rate of direct page reclaims caused by the tasks
in the cpuset, in units of reclaims attempted per second, times 1000.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 12:01:49 +03:00
|
|
|
/*
|
|
|
|
* Collection of memory_pressure is suppressed unless
|
|
|
|
* this flag is enabled by writing "1" to the special
|
|
|
|
* cpuset file 'memory_pressure_enabled' in the root cpuset.
|
|
|
|
*/
|
|
|
|
|
2006-01-08 12:01:51 +03:00
|
|
|
int cpuset_memory_pressure_enabled __read_mostly;
|
[PATCH] cpuset: memory pressure meter
Provide a simple per-cpuset metric of memory pressure, tracking the -rate-
that the tasks in a cpuset call try_to_free_pages(), the synchronous
(direct) memory reclaim code.
This enables batch managers monitoring jobs running in dedicated cpusets to
efficiently detect what level of memory pressure that job is causing.
This is useful both on tightly managed systems running a wide mix of
submitted jobs, which may choose to terminate or reprioritize jobs that are
trying to use more memory than allowed on the nodes assigned them, and with
tightly coupled, long running, massively parallel scientific computing jobs
that will dramatically fail to meet required performance goals if they
start to use more memory than allowed to them.
This patch just provides a very economical way for the batch manager to
monitor a cpuset for signs of memory pressure. It's up to the batch
manager or other user code to decide what to do about it and take action.
==> Unless this feature is enabled by writing "1" to the special file
/dev/cpuset/memory_pressure_enabled, the hook in the rebalance
code of __alloc_pages() for this metric reduces to simply noticing
that the cpuset_memory_pressure_enabled flag is zero. So only
systems that enable this feature will compute the metric.
Why a per-cpuset, running average:
Because this meter is per-cpuset, rather than per-task or mm, the
system load imposed by a batch scheduler monitoring this metric is
sharply reduced on large systems, because a scan of the tasklist can be
avoided on each set of queries.
Because this meter is a running average, instead of an accumulating
counter, a batch scheduler can detect memory pressure with a single
read, instead of having to read and accumulate results for a period of
time.
Because this meter is per-cpuset rather than per-task or mm, the
batch scheduler can obtain the key information, memory pressure in a
cpuset, with a single read, rather than having to query and accumulate
results over all the (dynamically changing) set of tasks in the cpuset.
A per-cpuset simple digital filter (requires a spinlock and 3 words of data
per-cpuset) is kept, and updated by any task attached to that cpuset, if it
enters the synchronous (direct) page reclaim code.
A per-cpuset file provides an integer number representing the recent
(half-life of 10 seconds) rate of direct page reclaims caused by the tasks
in the cpuset, in units of reclaims attempted per second, times 1000.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 12:01:49 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* cpuset_memory_pressure_bump - keep stats of per-cpuset reclaims.
|
|
|
|
*
|
|
|
|
* Keep a running average of the rate of synchronous (direct)
|
|
|
|
* page reclaim efforts initiated by tasks in each cpuset.
|
|
|
|
*
|
|
|
|
* This represents the rate at which some task in the cpuset
|
|
|
|
* ran low on memory on all nodes it was allowed to use, and
|
|
|
|
* had to enter the kernels page reclaim code in an effort to
|
|
|
|
* create more free memory by tossing clean pages or swapping
|
|
|
|
* or writing dirty pages.
|
|
|
|
*
|
|
|
|
* Display to user space in the per-cpuset read-only file
|
|
|
|
* "memory_pressure". Value displayed is an integer
|
|
|
|
* representing the recent rate of entry into the synchronous
|
|
|
|
* (direct) page reclaim by any task attached to the cpuset.
|
|
|
|
**/
|
|
|
|
|
|
|
|
void __cpuset_memory_pressure_bump(void)
|
|
|
|
{
|
|
|
|
task_lock(current);
|
2007-10-19 10:39:39 +04:00
|
|
|
fmeter_markevent(&task_cs(current)->fmeter);
|
[PATCH] cpuset: memory pressure meter
Provide a simple per-cpuset metric of memory pressure, tracking the -rate-
that the tasks in a cpuset call try_to_free_pages(), the synchronous
(direct) memory reclaim code.
This enables batch managers monitoring jobs running in dedicated cpusets to
efficiently detect what level of memory pressure that job is causing.
This is useful both on tightly managed systems running a wide mix of
submitted jobs, which may choose to terminate or reprioritize jobs that are
trying to use more memory than allowed on the nodes assigned them, and with
tightly coupled, long running, massively parallel scientific computing jobs
that will dramatically fail to meet required performance goals if they
start to use more memory than allowed to them.
This patch just provides a very economical way for the batch manager to
monitor a cpuset for signs of memory pressure. It's up to the batch
manager or other user code to decide what to do about it and take action.
==> Unless this feature is enabled by writing "1" to the special file
/dev/cpuset/memory_pressure_enabled, the hook in the rebalance
code of __alloc_pages() for this metric reduces to simply noticing
that the cpuset_memory_pressure_enabled flag is zero. So only
systems that enable this feature will compute the metric.
Why a per-cpuset, running average:
Because this meter is per-cpuset, rather than per-task or mm, the
system load imposed by a batch scheduler monitoring this metric is
sharply reduced on large systems, because a scan of the tasklist can be
avoided on each set of queries.
Because this meter is a running average, instead of an accumulating
counter, a batch scheduler can detect memory pressure with a single
read, instead of having to read and accumulate results for a period of
time.
Because this meter is per-cpuset rather than per-task or mm, the
batch scheduler can obtain the key information, memory pressure in a
cpuset, with a single read, rather than having to query and accumulate
results over all the (dynamically changing) set of tasks in the cpuset.
A per-cpuset simple digital filter (requires a spinlock and 3 words of data
per-cpuset) is kept, and updated by any task attached to that cpuset, if it
enters the synchronous (direct) page reclaim code.
A per-cpuset file provides an integer number representing the recent
(half-life of 10 seconds) rate of direct page reclaims caused by the tasks
in the cpuset, in units of reclaims attempted per second, times 1000.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 12:01:49 +03:00
|
|
|
task_unlock(current);
|
|
|
|
}
|
|
|
|
|
2007-10-19 10:39:39 +04:00
|
|
|
#ifdef CONFIG_PROC_PID_CPUSET
|
2005-04-17 02:20:36 +04:00
|
|
|
/*
|
|
|
|
* proc_cpuset_show()
|
|
|
|
* - Print tasks cpuset path into seq_file.
|
|
|
|
* - Used for /proc/<pid>/cpuset.
|
[PATCH] cpusets: dual semaphore locking overhaul
Overhaul cpuset locking. Replace single semaphore with two semaphores.
The suggestion to use two locks was made by Roman Zippel.
Both locks are global. Code that wants to modify cpusets must first
acquire the exclusive manage_sem, which allows them read-only access to
cpusets, and holds off other would-be modifiers. Before making actual
changes, the second semaphore, callback_sem must be acquired as well. Code
that needs only to query cpusets must acquire callback_sem, which is also a
global exclusive lock.
The earlier problems with double tripping are avoided, because it is
allowed for holders of manage_sem to nest the second callback_sem lock, and
only callback_sem is needed by code called from within __alloc_pages(),
where the double tripping had been possible.
This is not quite the same as a normal read/write semaphore, because
obtaining read-only access with intent to change must hold off other such
attempts, while allowing read-only access w/o such intention. Changing
cpusets involves several related checks and changes, which must be done
while allowing read-only queries (to avoid the double trip), but while
ensuring nothing changes (holding off other would be modifiers.)
This overhaul of cpuset locking also makes careful use of task_lock() to
guard access to the task->cpuset pointer, closing a couple of race
conditions noticed while reading this code (thanks, Roman). I've never
seen these races fail in any use or test.
See further the comments in the code.
Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-10-31 02:02:30 +03:00
|
|
|
* - No need to task_lock(tsk) on this tsk->cpuset reference, as it
|
|
|
|
* doesn't really matter if tsk->cpuset changes after we read it,
|
hotplug cpu: move tasks in empty cpusets to parent various other fixes
Various minor formatting and comment tweaks to Cliff Wickman's
[PATCH_3_of_3]_cpusets__update_cpumask_revision.patch
I had had "iff", meaning "if and only if" in a comment. However, except for
ancient mathematicians, the abbreviation "iff" was a tad too cryptic. Cliff
changed it to "if", presumably figuring that the "iff" was a typo. However,
it was the "only if" half of the conjunction that was most interesting.
Reword to emphasis the "only if" aspect.
The locking comment for remove_tasks_in_empty_cpuset() was wrong; it said
callback_mutex had to be held on entry. The opposite is true.
Several mentions of attach_task() in comments needed to be
changed to cgroup_attach_task().
A comment about notify_on_release was no longer relevant,
as the line of code it had commented, namely:
set_bit(CS_RELEASED_RESOURCE, &parent->flags);
is no longer present in that place in the cpuset.c code.
Similarly a comment about notify_on_release before the
scan_for_empty_cpusets() routine was no longer relevant.
Removed extra parentheses and unnecessary return statement.
Renamed attach_task() to cpuset_attach() in various comments.
Removed comment about not needing memory migration, as it seems the migration
is done anyway, via the cpuset_attach() callback from cgroup_attach_task().
Signed-off-by: Paul Jackson <pj@sgi.com>
Acked-by: Cliff Wickman <cpw@sgi.com>
Cc: David Rientjes <rientjes@google.com>
Cc: Paul Menage <menage@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2008-02-07 11:14:46 +03:00
|
|
|
* and we take cgroup_mutex, keeping cpuset_attach() from changing it
|
2008-02-07 11:14:45 +03:00
|
|
|
* anyway.
|
2005-04-17 02:20:36 +04:00
|
|
|
*/
|
2007-10-19 10:40:20 +04:00
|
|
|
static int proc_cpuset_show(struct seq_file *m, void *unused_v)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2006-06-26 11:25:56 +04:00
|
|
|
struct pid *pid;
|
2005-04-17 02:20:36 +04:00
|
|
|
struct task_struct *tsk;
|
|
|
|
char *buf;
|
2007-10-19 10:39:39 +04:00
|
|
|
struct cgroup_subsys_state *css;
|
2006-06-26 11:25:55 +04:00
|
|
|
int retval;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2006-06-26 11:25:55 +04:00
|
|
|
retval = -ENOMEM;
|
2005-04-17 02:20:36 +04:00
|
|
|
buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
|
|
|
|
if (!buf)
|
2006-06-26 11:25:55 +04:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
retval = -ESRCH;
|
2006-06-26 11:25:56 +04:00
|
|
|
pid = m->private;
|
|
|
|
tsk = get_pid_task(pid, PIDTYPE_PID);
|
2006-06-26 11:25:55 +04:00
|
|
|
if (!tsk)
|
|
|
|
goto out_free;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2006-06-26 11:25:55 +04:00
|
|
|
retval = -EINVAL;
|
2007-10-19 10:39:39 +04:00
|
|
|
cgroup_lock();
|
|
|
|
css = task_subsys_state(tsk, cpuset_subsys_id);
|
|
|
|
retval = cgroup_path(css->cgroup, buf, PAGE_SIZE);
|
2005-04-17 02:20:36 +04:00
|
|
|
if (retval < 0)
|
2006-06-26 11:25:55 +04:00
|
|
|
goto out_unlock;
|
2005-04-17 02:20:36 +04:00
|
|
|
seq_puts(m, buf);
|
|
|
|
seq_putc(m, '\n');
|
2006-06-26 11:25:55 +04:00
|
|
|
out_unlock:
|
2007-10-19 10:39:39 +04:00
|
|
|
cgroup_unlock();
|
2006-06-26 11:25:55 +04:00
|
|
|
put_task_struct(tsk);
|
|
|
|
out_free:
|
2005-04-17 02:20:36 +04:00
|
|
|
kfree(buf);
|
2006-06-26 11:25:55 +04:00
|
|
|
out:
|
2005-04-17 02:20:36 +04:00
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cpuset_open(struct inode *inode, struct file *file)
|
|
|
|
{
|
2006-06-26 11:25:56 +04:00
|
|
|
struct pid *pid = PROC_I(inode)->pid;
|
|
|
|
return single_open(file, proc_cpuset_show, pid);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
2007-02-12 11:55:35 +03:00
|
|
|
const struct file_operations proc_cpuset_operations = {
|
2005-04-17 02:20:36 +04:00
|
|
|
.open = cpuset_open,
|
|
|
|
.read = seq_read,
|
|
|
|
.llseek = seq_lseek,
|
|
|
|
.release = single_release,
|
|
|
|
};
|
2007-10-19 10:39:39 +04:00
|
|
|
#endif /* CONFIG_PROC_PID_CPUSET */
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
/* Display task cpus_allowed, mems_allowed in /proc/<pid>/status file. */
|
2008-02-08 15:18:33 +03:00
|
|
|
void cpuset_task_status_allowed(struct seq_file *m, struct task_struct *task)
|
|
|
|
{
|
|
|
|
seq_printf(m, "Cpus_allowed:\t");
|
|
|
|
m->count += cpumask_scnprintf(m->buf + m->count, m->size - m->count,
|
|
|
|
task->cpus_allowed);
|
|
|
|
seq_printf(m, "\n");
|
2008-04-08 22:43:03 +04:00
|
|
|
seq_printf(m, "Cpus_allowed_list:\t");
|
|
|
|
m->count += cpulist_scnprintf(m->buf + m->count, m->size - m->count,
|
|
|
|
task->cpus_allowed);
|
|
|
|
seq_printf(m, "\n");
|
2008-02-08 15:18:33 +03:00
|
|
|
seq_printf(m, "Mems_allowed:\t");
|
|
|
|
m->count += nodemask_scnprintf(m->buf + m->count, m->size - m->count,
|
|
|
|
task->mems_allowed);
|
|
|
|
seq_printf(m, "\n");
|
2008-04-08 22:43:03 +04:00
|
|
|
seq_printf(m, "Mems_allowed_list:\t");
|
|
|
|
m->count += nodelist_scnprintf(m->buf + m->count, m->size - m->count,
|
|
|
|
task->mems_allowed);
|
|
|
|
seq_printf(m, "\n");
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|