mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-24 13:44:26 -04:00
Merge branch 'linus' into irq/genirq
This commit is contained in:
commit
5a2dd72abd
8824 changed files with 1016100 additions and 238345 deletions
|
|
@ -52,28 +52,3 @@ config PREEMPT
|
|||
|
||||
endchoice
|
||||
|
||||
config PREEMPT_RCU
|
||||
bool "Preemptible RCU"
|
||||
depends on PREEMPT
|
||||
default n
|
||||
help
|
||||
This option reduces the latency of the kernel by making certain
|
||||
RCU sections preemptible. Normally RCU code is non-preemptible, if
|
||||
this option is selected then read-only RCU sections become
|
||||
preemptible. This helps latency, but may expose bugs due to
|
||||
now-naive assumptions about each RCU read-side critical section
|
||||
remaining on a given CPU through its execution.
|
||||
|
||||
Say N if you are unsure.
|
||||
|
||||
config RCU_TRACE
|
||||
bool "Enable tracing for RCU - currently stats in debugfs"
|
||||
depends on PREEMPT_RCU
|
||||
select DEBUG_FS
|
||||
default y
|
||||
help
|
||||
This option provides tracing in RCU which presents stats
|
||||
in debugfs for debugging RCU implementation.
|
||||
|
||||
Say Y here if you want to enable RCU tracing
|
||||
Say N if you are unsure.
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@ obj-y = sched.o fork.o exec_domain.o panic.o printk.o \
|
|||
rcupdate.o extable.o params.o posix-timers.o \
|
||||
kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o mutex.o \
|
||||
hrtimer.o rwsem.o nsproxy.o srcu.o semaphore.o \
|
||||
notifier.o ksysfs.o pm_qos_params.o sched_clock.o
|
||||
notifier.o ksysfs.o pm_qos_params.o sched_clock.o cred.o \
|
||||
async.o
|
||||
|
||||
ifdef CONFIG_FUNCTION_TRACER
|
||||
# Do not trace debug files and internal ftrace files
|
||||
|
|
@ -19,7 +20,6 @@ CFLAGS_REMOVE_mutex-debug.o = -pg
|
|||
CFLAGS_REMOVE_rtmutex-debug.o = -pg
|
||||
CFLAGS_REMOVE_cgroup-debug.o = -pg
|
||||
CFLAGS_REMOVE_sched_clock.o = -pg
|
||||
CFLAGS_REMOVE_sched.o = -pg
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_FREEZER) += freezer.o
|
||||
|
|
@ -41,6 +41,9 @@ obj-$(CONFIG_DEBUG_RT_MUTEXES) += rtmutex-debug.o
|
|||
obj-$(CONFIG_RT_MUTEX_TESTER) += rtmutex-tester.o
|
||||
obj-$(CONFIG_GENERIC_ISA_DMA) += dma.o
|
||||
obj-$(CONFIG_USE_GENERIC_SMP_HELPERS) += smp.o
|
||||
ifneq ($(CONFIG_SMP),y)
|
||||
obj-y += up.o
|
||||
endif
|
||||
obj-$(CONFIG_SMP) += spinlock.o
|
||||
obj-$(CONFIG_DEBUG_SPINLOCK) += spinlock.o
|
||||
obj-$(CONFIG_PROVE_LOCKING) += spinlock.o
|
||||
|
|
@ -74,10 +77,10 @@ obj-$(CONFIG_GENERIC_HARDIRQS) += irq/
|
|||
obj-$(CONFIG_SECCOMP) += seccomp.o
|
||||
obj-$(CONFIG_RCU_TORTURE_TEST) += rcutorture.o
|
||||
obj-$(CONFIG_CLASSIC_RCU) += rcuclassic.o
|
||||
obj-$(CONFIG_TREE_RCU) += rcutree.o
|
||||
obj-$(CONFIG_PREEMPT_RCU) += rcupreempt.o
|
||||
ifeq ($(CONFIG_PREEMPT_RCU),y)
|
||||
obj-$(CONFIG_RCU_TRACE) += rcupreempt_trace.o
|
||||
endif
|
||||
obj-$(CONFIG_TREE_RCU_TRACE) += rcutree_trace.o
|
||||
obj-$(CONFIG_PREEMPT_RCU_TRACE) += rcupreempt_trace.o
|
||||
obj-$(CONFIG_RELAY) += relay.o
|
||||
obj-$(CONFIG_SYSCTL) += utsname_sysctl.o
|
||||
obj-$(CONFIG_TASK_DELAY_ACCT) += delayacct.o
|
||||
|
|
@ -90,7 +93,7 @@ obj-$(CONFIG_FUNCTION_TRACER) += trace/
|
|||
obj-$(CONFIG_TRACING) += trace/
|
||||
obj-$(CONFIG_SMP) += sched_cpupri.o
|
||||
|
||||
ifneq ($(CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER),y)
|
||||
ifneq ($(CONFIG_SCHED_OMIT_FRAME_POINTER),y)
|
||||
# According to Alan Modra <alan@linuxcare.com.au>, the -fno-omit-frame-pointer is
|
||||
# needed for x86 only. Why this used to be enabled for all architectures is beyond
|
||||
# me. I suspect most platforms don't need this, but until we know that for sure
|
||||
|
|
|
|||
|
|
@ -277,7 +277,7 @@ static int acct_on(char *name)
|
|||
* should be written. If the filename is NULL, accounting will be
|
||||
* shutdown.
|
||||
*/
|
||||
asmlinkage long sys_acct(const char __user *name)
|
||||
SYSCALL_DEFINE1(acct, const char __user *, name)
|
||||
{
|
||||
int error;
|
||||
|
||||
|
|
@ -530,15 +530,14 @@ static void do_acct_process(struct bsd_acct_struct *acct,
|
|||
do_div(elapsed, AHZ);
|
||||
ac.ac_btime = get_seconds() - elapsed;
|
||||
/* we really need to bite the bullet and change layout */
|
||||
ac.ac_uid = current->uid;
|
||||
ac.ac_gid = current->gid;
|
||||
current_uid_gid(&ac.ac_uid, &ac.ac_gid);
|
||||
#if ACCT_VERSION==2
|
||||
ac.ac_ahz = AHZ;
|
||||
#endif
|
||||
#if ACCT_VERSION==1 || ACCT_VERSION==2
|
||||
/* backward-compatible 16 bit fields */
|
||||
ac.ac_uid16 = current->uid;
|
||||
ac.ac_gid16 = current->gid;
|
||||
ac.ac_uid16 = ac.ac_uid;
|
||||
ac.ac_gid16 = ac.ac_gid;
|
||||
#endif
|
||||
#if ACCT_VERSION==3
|
||||
ac.ac_pid = task_tgid_nr_ns(current, ns);
|
||||
|
|
|
|||
346
kernel/async.c
Normal file
346
kernel/async.c
Normal file
|
|
@ -0,0 +1,346 @@
|
|||
/*
|
||||
* async.c: Asynchronous function calls for boot performance
|
||||
*
|
||||
* (C) Copyright 2009 Intel Corporation
|
||||
* Author: Arjan van de Ven <arjan@linux.intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; version 2
|
||||
* of the License.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
|
||||
Goals and Theory of Operation
|
||||
|
||||
The primary goal of this feature is to reduce the kernel boot time,
|
||||
by doing various independent hardware delays and discovery operations
|
||||
decoupled and not strictly serialized.
|
||||
|
||||
More specifically, the asynchronous function call concept allows
|
||||
certain operations (primarily during system boot) to happen
|
||||
asynchronously, out of order, while these operations still
|
||||
have their externally visible parts happen sequentially and in-order.
|
||||
(not unlike how out-of-order CPUs retire their instructions in order)
|
||||
|
||||
Key to the asynchronous function call implementation is the concept of
|
||||
a "sequence cookie" (which, although it has an abstracted type, can be
|
||||
thought of as a monotonically incrementing number).
|
||||
|
||||
The async core will assign each scheduled event such a sequence cookie and
|
||||
pass this to the called functions.
|
||||
|
||||
The asynchronously called function should before doing a globally visible
|
||||
operation, such as registering device numbers, call the
|
||||
async_synchronize_cookie() function and pass in its own cookie. The
|
||||
async_synchronize_cookie() function will make sure that all asynchronous
|
||||
operations that were scheduled prior to the operation corresponding with the
|
||||
cookie have completed.
|
||||
|
||||
Subsystem/driver initialization code that scheduled asynchronous probe
|
||||
functions, but which shares global resources with other drivers/subsystems
|
||||
that do not use the asynchronous call feature, need to do a full
|
||||
synchronization with the async_synchronize_full() function, before returning
|
||||
from their init function. This is to maintain strict ordering between the
|
||||
asynchronous and synchronous parts of the kernel.
|
||||
|
||||
*/
|
||||
|
||||
#include <linux/async.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <asm/atomic.h>
|
||||
|
||||
static async_cookie_t next_cookie = 1;
|
||||
|
||||
#define MAX_THREADS 256
|
||||
#define MAX_WORK 32768
|
||||
|
||||
static LIST_HEAD(async_pending);
|
||||
static LIST_HEAD(async_running);
|
||||
static DEFINE_SPINLOCK(async_lock);
|
||||
|
||||
static int async_enabled = 0;
|
||||
|
||||
struct async_entry {
|
||||
struct list_head list;
|
||||
async_cookie_t cookie;
|
||||
async_func_ptr *func;
|
||||
void *data;
|
||||
struct list_head *running;
|
||||
};
|
||||
|
||||
static DECLARE_WAIT_QUEUE_HEAD(async_done);
|
||||
static DECLARE_WAIT_QUEUE_HEAD(async_new);
|
||||
|
||||
static atomic_t entry_count;
|
||||
static atomic_t thread_count;
|
||||
|
||||
extern int initcall_debug;
|
||||
|
||||
|
||||
/*
|
||||
* MUST be called with the lock held!
|
||||
*/
|
||||
static async_cookie_t __lowest_in_progress(struct list_head *running)
|
||||
{
|
||||
struct async_entry *entry;
|
||||
if (!list_empty(running)) {
|
||||
entry = list_first_entry(running,
|
||||
struct async_entry, list);
|
||||
return entry->cookie;
|
||||
} else if (!list_empty(&async_pending)) {
|
||||
entry = list_first_entry(&async_pending,
|
||||
struct async_entry, list);
|
||||
return entry->cookie;
|
||||
} else {
|
||||
/* nothing in progress... next_cookie is "infinity" */
|
||||
return next_cookie;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static async_cookie_t lowest_in_progress(struct list_head *running)
|
||||
{
|
||||
unsigned long flags;
|
||||
async_cookie_t ret;
|
||||
|
||||
spin_lock_irqsave(&async_lock, flags);
|
||||
ret = __lowest_in_progress(running);
|
||||
spin_unlock_irqrestore(&async_lock, flags);
|
||||
return ret;
|
||||
}
|
||||
/*
|
||||
* pick the first pending entry and run it
|
||||
*/
|
||||
static void run_one_entry(void)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct async_entry *entry;
|
||||
ktime_t calltime, delta, rettime;
|
||||
|
||||
/* 1) pick one task from the pending queue */
|
||||
|
||||
spin_lock_irqsave(&async_lock, flags);
|
||||
if (list_empty(&async_pending))
|
||||
goto out;
|
||||
entry = list_first_entry(&async_pending, struct async_entry, list);
|
||||
|
||||
/* 2) move it to the running queue */
|
||||
list_del(&entry->list);
|
||||
list_add_tail(&entry->list, &async_running);
|
||||
spin_unlock_irqrestore(&async_lock, flags);
|
||||
|
||||
/* 3) run it (and print duration)*/
|
||||
if (initcall_debug && system_state == SYSTEM_BOOTING) {
|
||||
printk("calling %lli_%pF @ %i\n", entry->cookie, entry->func, task_pid_nr(current));
|
||||
calltime = ktime_get();
|
||||
}
|
||||
entry->func(entry->data, entry->cookie);
|
||||
if (initcall_debug && system_state == SYSTEM_BOOTING) {
|
||||
rettime = ktime_get();
|
||||
delta = ktime_sub(rettime, calltime);
|
||||
printk("initcall %lli_%pF returned 0 after %lld usecs\n", entry->cookie,
|
||||
entry->func, ktime_to_ns(delta) >> 10);
|
||||
}
|
||||
|
||||
/* 4) remove it from the running queue */
|
||||
spin_lock_irqsave(&async_lock, flags);
|
||||
list_del(&entry->list);
|
||||
|
||||
/* 5) free the entry */
|
||||
kfree(entry);
|
||||
atomic_dec(&entry_count);
|
||||
|
||||
spin_unlock_irqrestore(&async_lock, flags);
|
||||
|
||||
/* 6) wake up any waiters. */
|
||||
wake_up(&async_done);
|
||||
return;
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&async_lock, flags);
|
||||
}
|
||||
|
||||
|
||||
static async_cookie_t __async_schedule(async_func_ptr *ptr, void *data, struct list_head *running)
|
||||
{
|
||||
struct async_entry *entry;
|
||||
unsigned long flags;
|
||||
async_cookie_t newcookie;
|
||||
|
||||
|
||||
/* allow irq-off callers */
|
||||
entry = kzalloc(sizeof(struct async_entry), GFP_ATOMIC);
|
||||
|
||||
/*
|
||||
* If we're out of memory or if there's too much work
|
||||
* pending already, we execute synchronously.
|
||||
*/
|
||||
if (!async_enabled || !entry || atomic_read(&entry_count) > MAX_WORK) {
|
||||
kfree(entry);
|
||||
spin_lock_irqsave(&async_lock, flags);
|
||||
newcookie = next_cookie++;
|
||||
spin_unlock_irqrestore(&async_lock, flags);
|
||||
|
||||
/* low on memory.. run synchronously */
|
||||
ptr(data, newcookie);
|
||||
return newcookie;
|
||||
}
|
||||
entry->func = ptr;
|
||||
entry->data = data;
|
||||
entry->running = running;
|
||||
|
||||
spin_lock_irqsave(&async_lock, flags);
|
||||
newcookie = entry->cookie = next_cookie++;
|
||||
list_add_tail(&entry->list, &async_pending);
|
||||
atomic_inc(&entry_count);
|
||||
spin_unlock_irqrestore(&async_lock, flags);
|
||||
wake_up(&async_new);
|
||||
return newcookie;
|
||||
}
|
||||
|
||||
async_cookie_t async_schedule(async_func_ptr *ptr, void *data)
|
||||
{
|
||||
return __async_schedule(ptr, data, &async_pending);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(async_schedule);
|
||||
|
||||
async_cookie_t async_schedule_special(async_func_ptr *ptr, void *data, struct list_head *running)
|
||||
{
|
||||
return __async_schedule(ptr, data, running);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(async_schedule_special);
|
||||
|
||||
void async_synchronize_full(void)
|
||||
{
|
||||
do {
|
||||
async_synchronize_cookie(next_cookie);
|
||||
} while (!list_empty(&async_running) || !list_empty(&async_pending));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(async_synchronize_full);
|
||||
|
||||
void async_synchronize_full_special(struct list_head *list)
|
||||
{
|
||||
async_synchronize_cookie_special(next_cookie, list);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(async_synchronize_full_special);
|
||||
|
||||
void async_synchronize_cookie_special(async_cookie_t cookie, struct list_head *running)
|
||||
{
|
||||
ktime_t starttime, delta, endtime;
|
||||
|
||||
if (initcall_debug && system_state == SYSTEM_BOOTING) {
|
||||
printk("async_waiting @ %i\n", task_pid_nr(current));
|
||||
starttime = ktime_get();
|
||||
}
|
||||
|
||||
wait_event(async_done, lowest_in_progress(running) >= cookie);
|
||||
|
||||
if (initcall_debug && system_state == SYSTEM_BOOTING) {
|
||||
endtime = ktime_get();
|
||||
delta = ktime_sub(endtime, starttime);
|
||||
|
||||
printk("async_continuing @ %i after %lli usec\n",
|
||||
task_pid_nr(current), ktime_to_ns(delta) >> 10);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(async_synchronize_cookie_special);
|
||||
|
||||
void async_synchronize_cookie(async_cookie_t cookie)
|
||||
{
|
||||
async_synchronize_cookie_special(cookie, &async_running);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(async_synchronize_cookie);
|
||||
|
||||
|
||||
static int async_thread(void *unused)
|
||||
{
|
||||
DECLARE_WAITQUEUE(wq, current);
|
||||
add_wait_queue(&async_new, &wq);
|
||||
|
||||
while (!kthread_should_stop()) {
|
||||
int ret = HZ;
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
/*
|
||||
* check the list head without lock.. false positives
|
||||
* are dealt with inside run_one_entry() while holding
|
||||
* the lock.
|
||||
*/
|
||||
rmb();
|
||||
if (!list_empty(&async_pending))
|
||||
run_one_entry();
|
||||
else
|
||||
ret = schedule_timeout(HZ);
|
||||
|
||||
if (ret == 0) {
|
||||
/*
|
||||
* we timed out, this means we as thread are redundant.
|
||||
* we sign off and die, but we to avoid any races there
|
||||
* is a last-straw check to see if work snuck in.
|
||||
*/
|
||||
atomic_dec(&thread_count);
|
||||
wmb(); /* manager must see our departure first */
|
||||
if (list_empty(&async_pending))
|
||||
break;
|
||||
/*
|
||||
* woops work came in between us timing out and us
|
||||
* signing off; we need to stay alive and keep working.
|
||||
*/
|
||||
atomic_inc(&thread_count);
|
||||
}
|
||||
}
|
||||
remove_wait_queue(&async_new, &wq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int async_manager_thread(void *unused)
|
||||
{
|
||||
DECLARE_WAITQUEUE(wq, current);
|
||||
add_wait_queue(&async_new, &wq);
|
||||
|
||||
while (!kthread_should_stop()) {
|
||||
int tc, ec;
|
||||
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
|
||||
tc = atomic_read(&thread_count);
|
||||
rmb();
|
||||
ec = atomic_read(&entry_count);
|
||||
|
||||
while (tc < ec && tc < MAX_THREADS) {
|
||||
kthread_run(async_thread, NULL, "async/%i", tc);
|
||||
atomic_inc(&thread_count);
|
||||
tc++;
|
||||
}
|
||||
|
||||
schedule();
|
||||
}
|
||||
remove_wait_queue(&async_new, &wq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init async_init(void)
|
||||
{
|
||||
if (async_enabled)
|
||||
kthread_run(async_manager_thread, NULL, "async/mgr");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init setup_async(char *str)
|
||||
{
|
||||
async_enabled = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
__setup("fastboot", setup_async);
|
||||
|
||||
|
||||
core_initcall(async_init);
|
||||
|
|
@ -159,11 +159,8 @@ static inline int audit_signal_info(int sig, struct task_struct *t)
|
|||
return __audit_signal_info(sig, t);
|
||||
return 0;
|
||||
}
|
||||
extern enum audit_state audit_filter_inodes(struct task_struct *,
|
||||
struct audit_context *);
|
||||
extern void audit_set_auditable(struct audit_context *);
|
||||
extern void audit_filter_inodes(struct task_struct *, struct audit_context *);
|
||||
#else
|
||||
#define audit_signal_info(s,t) AUDIT_DISABLED
|
||||
#define audit_filter_inodes(t,c) AUDIT_DISABLED
|
||||
#define audit_set_auditable(c)
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -450,6 +450,7 @@ static void kill_rules(struct audit_tree *tree)
|
|||
audit_log_end(ab);
|
||||
rule->tree = NULL;
|
||||
list_del_rcu(&entry->list);
|
||||
list_del(&entry->rule.list);
|
||||
call_rcu(&entry->rcu, audit_free_rule_rcu);
|
||||
}
|
||||
}
|
||||
|
|
@ -617,7 +618,7 @@ int audit_make_tree(struct audit_krule *rule, char *pathname, u32 op)
|
|||
|
||||
if (pathname[0] != '/' ||
|
||||
rule->listnr != AUDIT_FILTER_EXIT ||
|
||||
op & ~AUDIT_EQUAL ||
|
||||
op != Audit_equal ||
|
||||
rule->inode_f || rule->watch || rule->tree)
|
||||
return -EINVAL;
|
||||
rule->tree = alloc_tree(pathname);
|
||||
|
|
|
|||
|
|
@ -86,6 +86,14 @@ struct list_head audit_filter_list[AUDIT_NR_FILTERS] = {
|
|||
#error Fix audit_filter_list initialiser
|
||||
#endif
|
||||
};
|
||||
static struct list_head audit_rules_list[AUDIT_NR_FILTERS] = {
|
||||
LIST_HEAD_INIT(audit_rules_list[0]),
|
||||
LIST_HEAD_INIT(audit_rules_list[1]),
|
||||
LIST_HEAD_INIT(audit_rules_list[2]),
|
||||
LIST_HEAD_INIT(audit_rules_list[3]),
|
||||
LIST_HEAD_INIT(audit_rules_list[4]),
|
||||
LIST_HEAD_INIT(audit_rules_list[5]),
|
||||
};
|
||||
|
||||
DEFINE_MUTEX(audit_filter_mutex);
|
||||
|
||||
|
|
@ -244,7 +252,8 @@ static inline int audit_to_inode(struct audit_krule *krule,
|
|||
struct audit_field *f)
|
||||
{
|
||||
if (krule->listnr != AUDIT_FILTER_EXIT ||
|
||||
krule->watch || krule->inode_f || krule->tree)
|
||||
krule->watch || krule->inode_f || krule->tree ||
|
||||
(f->op != Audit_equal && f->op != Audit_not_equal))
|
||||
return -EINVAL;
|
||||
|
||||
krule->inode_f = f;
|
||||
|
|
@ -262,7 +271,7 @@ static int audit_to_watch(struct audit_krule *krule, char *path, int len,
|
|||
|
||||
if (path[0] != '/' || path[len-1] == '/' ||
|
||||
krule->listnr != AUDIT_FILTER_EXIT ||
|
||||
op & ~AUDIT_EQUAL ||
|
||||
op != Audit_equal ||
|
||||
krule->inode_f || krule->watch || krule->tree)
|
||||
return -EINVAL;
|
||||
|
||||
|
|
@ -412,12 +421,32 @@ exit_err:
|
|||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static u32 audit_ops[] =
|
||||
{
|
||||
[Audit_equal] = AUDIT_EQUAL,
|
||||
[Audit_not_equal] = AUDIT_NOT_EQUAL,
|
||||
[Audit_bitmask] = AUDIT_BIT_MASK,
|
||||
[Audit_bittest] = AUDIT_BIT_TEST,
|
||||
[Audit_lt] = AUDIT_LESS_THAN,
|
||||
[Audit_gt] = AUDIT_GREATER_THAN,
|
||||
[Audit_le] = AUDIT_LESS_THAN_OR_EQUAL,
|
||||
[Audit_ge] = AUDIT_GREATER_THAN_OR_EQUAL,
|
||||
};
|
||||
|
||||
static u32 audit_to_op(u32 op)
|
||||
{
|
||||
u32 n;
|
||||
for (n = Audit_equal; n < Audit_bad && audit_ops[n] != op; n++)
|
||||
;
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
/* Translate struct audit_rule to kernel's rule respresentation.
|
||||
* Exists for backward compatibility with userspace. */
|
||||
static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule)
|
||||
{
|
||||
struct audit_entry *entry;
|
||||
struct audit_field *ino_f;
|
||||
int err = 0;
|
||||
int i;
|
||||
|
||||
|
|
@ -427,12 +456,28 @@ static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule)
|
|||
|
||||
for (i = 0; i < rule->field_count; i++) {
|
||||
struct audit_field *f = &entry->rule.fields[i];
|
||||
u32 n;
|
||||
|
||||
n = rule->fields[i] & (AUDIT_NEGATE|AUDIT_OPERATORS);
|
||||
|
||||
/* Support for legacy operators where
|
||||
* AUDIT_NEGATE bit signifies != and otherwise assumes == */
|
||||
if (n & AUDIT_NEGATE)
|
||||
f->op = Audit_not_equal;
|
||||
else if (!n)
|
||||
f->op = Audit_equal;
|
||||
else
|
||||
f->op = audit_to_op(n);
|
||||
|
||||
entry->rule.vers_ops = (n & AUDIT_OPERATORS) ? 2 : 1;
|
||||
|
||||
f->op = rule->fields[i] & (AUDIT_NEGATE|AUDIT_OPERATORS);
|
||||
f->type = rule->fields[i] & ~(AUDIT_NEGATE|AUDIT_OPERATORS);
|
||||
f->val = rule->values[i];
|
||||
|
||||
err = -EINVAL;
|
||||
if (f->op == Audit_bad)
|
||||
goto exit_free;
|
||||
|
||||
switch(f->type) {
|
||||
default:
|
||||
goto exit_free;
|
||||
|
|
@ -454,11 +499,8 @@ static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule)
|
|||
case AUDIT_EXIT:
|
||||
case AUDIT_SUCCESS:
|
||||
/* bit ops are only useful on syscall args */
|
||||
if (f->op == AUDIT_BIT_MASK ||
|
||||
f->op == AUDIT_BIT_TEST) {
|
||||
err = -EINVAL;
|
||||
if (f->op == Audit_bitmask || f->op == Audit_bittest)
|
||||
goto exit_free;
|
||||
}
|
||||
break;
|
||||
case AUDIT_ARG0:
|
||||
case AUDIT_ARG1:
|
||||
|
|
@ -467,11 +509,8 @@ static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule)
|
|||
break;
|
||||
/* arch is only allowed to be = or != */
|
||||
case AUDIT_ARCH:
|
||||
if ((f->op != AUDIT_NOT_EQUAL) && (f->op != AUDIT_EQUAL)
|
||||
&& (f->op != AUDIT_NEGATE) && (f->op)) {
|
||||
err = -EINVAL;
|
||||
if (f->op != Audit_not_equal && f->op != Audit_equal)
|
||||
goto exit_free;
|
||||
}
|
||||
entry->rule.arch_f = f;
|
||||
break;
|
||||
case AUDIT_PERM:
|
||||
|
|
@ -488,33 +527,10 @@ static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule)
|
|||
goto exit_free;
|
||||
break;
|
||||
}
|
||||
|
||||
entry->rule.vers_ops = (f->op & AUDIT_OPERATORS) ? 2 : 1;
|
||||
|
||||
/* Support for legacy operators where
|
||||
* AUDIT_NEGATE bit signifies != and otherwise assumes == */
|
||||
if (f->op & AUDIT_NEGATE)
|
||||
f->op = AUDIT_NOT_EQUAL;
|
||||
else if (!f->op)
|
||||
f->op = AUDIT_EQUAL;
|
||||
else if (f->op == AUDIT_OPERATORS) {
|
||||
err = -EINVAL;
|
||||
goto exit_free;
|
||||
}
|
||||
}
|
||||
|
||||
ino_f = entry->rule.inode_f;
|
||||
if (ino_f) {
|
||||
switch(ino_f->op) {
|
||||
case AUDIT_NOT_EQUAL:
|
||||
entry->rule.inode_f = NULL;
|
||||
case AUDIT_EQUAL:
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
goto exit_free;
|
||||
}
|
||||
}
|
||||
if (entry->rule.inode_f && entry->rule.inode_f->op == Audit_not_equal)
|
||||
entry->rule.inode_f = NULL;
|
||||
|
||||
exit_nofree:
|
||||
return entry;
|
||||
|
|
@ -530,7 +546,6 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
|
|||
{
|
||||
int err = 0;
|
||||
struct audit_entry *entry;
|
||||
struct audit_field *ino_f;
|
||||
void *bufp;
|
||||
size_t remain = datasz - sizeof(struct audit_rule_data);
|
||||
int i;
|
||||
|
|
@ -546,11 +561,11 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
|
|||
struct audit_field *f = &entry->rule.fields[i];
|
||||
|
||||
err = -EINVAL;
|
||||
if (!(data->fieldflags[i] & AUDIT_OPERATORS) ||
|
||||
data->fieldflags[i] & ~AUDIT_OPERATORS)
|
||||
|
||||
f->op = audit_to_op(data->fieldflags[i]);
|
||||
if (f->op == Audit_bad)
|
||||
goto exit_free;
|
||||
|
||||
f->op = data->fieldflags[i] & AUDIT_OPERATORS;
|
||||
f->type = data->fields[i];
|
||||
f->val = data->values[i];
|
||||
f->lsm_str = NULL;
|
||||
|
|
@ -662,18 +677,8 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
|
|||
}
|
||||
}
|
||||
|
||||
ino_f = entry->rule.inode_f;
|
||||
if (ino_f) {
|
||||
switch(ino_f->op) {
|
||||
case AUDIT_NOT_EQUAL:
|
||||
entry->rule.inode_f = NULL;
|
||||
case AUDIT_EQUAL:
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
goto exit_free;
|
||||
}
|
||||
}
|
||||
if (entry->rule.inode_f && entry->rule.inode_f->op == Audit_not_equal)
|
||||
entry->rule.inode_f = NULL;
|
||||
|
||||
exit_nofree:
|
||||
return entry;
|
||||
|
|
@ -713,10 +718,10 @@ static struct audit_rule *audit_krule_to_rule(struct audit_krule *krule)
|
|||
rule->fields[i] = krule->fields[i].type;
|
||||
|
||||
if (krule->vers_ops == 1) {
|
||||
if (krule->fields[i].op & AUDIT_NOT_EQUAL)
|
||||
if (krule->fields[i].op == Audit_not_equal)
|
||||
rule->fields[i] |= AUDIT_NEGATE;
|
||||
} else {
|
||||
rule->fields[i] |= krule->fields[i].op;
|
||||
rule->fields[i] |= audit_ops[krule->fields[i].op];
|
||||
}
|
||||
}
|
||||
for (i = 0; i < AUDIT_BITMASK_SIZE; i++) rule->mask[i] = krule->mask[i];
|
||||
|
|
@ -744,7 +749,7 @@ static struct audit_rule_data *audit_krule_to_data(struct audit_krule *krule)
|
|||
struct audit_field *f = &krule->fields[i];
|
||||
|
||||
data->fields[i] = f->type;
|
||||
data->fieldflags[i] = f->op;
|
||||
data->fieldflags[i] = audit_ops[f->op];
|
||||
switch(f->type) {
|
||||
case AUDIT_SUBJ_USER:
|
||||
case AUDIT_SUBJ_ROLE:
|
||||
|
|
@ -919,6 +924,7 @@ static struct audit_entry *audit_dupe_rule(struct audit_krule *old,
|
|||
new->action = old->action;
|
||||
for (i = 0; i < AUDIT_BITMASK_SIZE; i++)
|
||||
new->mask[i] = old->mask[i];
|
||||
new->prio = old->prio;
|
||||
new->buflen = old->buflen;
|
||||
new->inode_f = old->inode_f;
|
||||
new->watch = NULL;
|
||||
|
|
@ -987,9 +993,8 @@ static void audit_update_watch(struct audit_parent *parent,
|
|||
|
||||
/* If the update involves invalidating rules, do the inode-based
|
||||
* filtering now, so we don't omit records. */
|
||||
if (invalidating && current->audit_context &&
|
||||
audit_filter_inodes(current, current->audit_context) == AUDIT_RECORD_CONTEXT)
|
||||
audit_set_auditable(current->audit_context);
|
||||
if (invalidating && current->audit_context)
|
||||
audit_filter_inodes(current, current->audit_context);
|
||||
|
||||
nwatch = audit_dupe_watch(owatch);
|
||||
if (IS_ERR(nwatch)) {
|
||||
|
|
@ -1007,12 +1012,15 @@ static void audit_update_watch(struct audit_parent *parent,
|
|||
list_del_rcu(&oentry->list);
|
||||
|
||||
nentry = audit_dupe_rule(&oentry->rule, nwatch);
|
||||
if (IS_ERR(nentry))
|
||||
if (IS_ERR(nentry)) {
|
||||
list_del(&oentry->rule.list);
|
||||
audit_panic("error updating watch, removing");
|
||||
else {
|
||||
} else {
|
||||
int h = audit_hash_ino((u32)ino);
|
||||
list_add(&nentry->rule.rlist, &nwatch->rules);
|
||||
list_add_rcu(&nentry->list, &audit_inode_hash[h]);
|
||||
list_replace(&oentry->rule.list,
|
||||
&nentry->rule.list);
|
||||
}
|
||||
|
||||
call_rcu(&oentry->rcu, audit_free_rule_rcu);
|
||||
|
|
@ -1077,6 +1085,7 @@ static void audit_remove_parent_watches(struct audit_parent *parent)
|
|||
audit_log_end(ab);
|
||||
}
|
||||
list_del(&r->rlist);
|
||||
list_del(&r->list);
|
||||
list_del_rcu(&e->list);
|
||||
call_rcu(&e->rcu, audit_free_rule_rcu);
|
||||
}
|
||||
|
|
@ -1102,12 +1111,16 @@ static void audit_inotify_unregister(struct list_head *in_list)
|
|||
/* Find an existing audit rule.
|
||||
* Caller must hold audit_filter_mutex to prevent stale rule data. */
|
||||
static struct audit_entry *audit_find_rule(struct audit_entry *entry,
|
||||
struct list_head *list)
|
||||
struct list_head **p)
|
||||
{
|
||||
struct audit_entry *e, *found = NULL;
|
||||
struct list_head *list;
|
||||
int h;
|
||||
|
||||
if (entry->rule.watch) {
|
||||
if (entry->rule.inode_f) {
|
||||
h = audit_hash_ino(entry->rule.inode_f->val);
|
||||
*p = list = &audit_inode_hash[h];
|
||||
} else if (entry->rule.watch) {
|
||||
/* we don't know the inode number, so must walk entire hash */
|
||||
for (h = 0; h < AUDIT_INODE_BUCKETS; h++) {
|
||||
list = &audit_inode_hash[h];
|
||||
|
|
@ -1118,6 +1131,8 @@ static struct audit_entry *audit_find_rule(struct audit_entry *entry,
|
|||
}
|
||||
}
|
||||
goto out;
|
||||
} else {
|
||||
*p = list = &audit_filter_list[entry->rule.listnr];
|
||||
}
|
||||
|
||||
list_for_each_entry(e, list, list)
|
||||
|
|
@ -1258,15 +1273,17 @@ static int audit_add_watch(struct audit_krule *krule, struct nameidata *ndp,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static u64 prio_low = ~0ULL/2;
|
||||
static u64 prio_high = ~0ULL/2 - 1;
|
||||
|
||||
/* Add rule to given filterlist if not a duplicate. */
|
||||
static inline int audit_add_rule(struct audit_entry *entry,
|
||||
struct list_head *list)
|
||||
static inline int audit_add_rule(struct audit_entry *entry)
|
||||
{
|
||||
struct audit_entry *e;
|
||||
struct audit_field *inode_f = entry->rule.inode_f;
|
||||
struct audit_watch *watch = entry->rule.watch;
|
||||
struct audit_tree *tree = entry->rule.tree;
|
||||
struct nameidata *ndp = NULL, *ndw = NULL;
|
||||
struct list_head *list;
|
||||
int h, err;
|
||||
#ifdef CONFIG_AUDITSYSCALL
|
||||
int dont_count = 0;
|
||||
|
|
@ -1277,13 +1294,8 @@ static inline int audit_add_rule(struct audit_entry *entry,
|
|||
dont_count = 1;
|
||||
#endif
|
||||
|
||||
if (inode_f) {
|
||||
h = audit_hash_ino(inode_f->val);
|
||||
list = &audit_inode_hash[h];
|
||||
}
|
||||
|
||||
mutex_lock(&audit_filter_mutex);
|
||||
e = audit_find_rule(entry, list);
|
||||
e = audit_find_rule(entry, &list);
|
||||
mutex_unlock(&audit_filter_mutex);
|
||||
if (e) {
|
||||
err = -EEXIST;
|
||||
|
|
@ -1319,10 +1331,22 @@ static inline int audit_add_rule(struct audit_entry *entry,
|
|||
}
|
||||
}
|
||||
|
||||
entry->rule.prio = ~0ULL;
|
||||
if (entry->rule.listnr == AUDIT_FILTER_EXIT) {
|
||||
if (entry->rule.flags & AUDIT_FILTER_PREPEND)
|
||||
entry->rule.prio = ++prio_high;
|
||||
else
|
||||
entry->rule.prio = --prio_low;
|
||||
}
|
||||
|
||||
if (entry->rule.flags & AUDIT_FILTER_PREPEND) {
|
||||
list_add(&entry->rule.list,
|
||||
&audit_rules_list[entry->rule.listnr]);
|
||||
list_add_rcu(&entry->list, list);
|
||||
entry->rule.flags &= ~AUDIT_FILTER_PREPEND;
|
||||
} else {
|
||||
list_add_tail(&entry->rule.list,
|
||||
&audit_rules_list[entry->rule.listnr]);
|
||||
list_add_tail_rcu(&entry->list, list);
|
||||
}
|
||||
#ifdef CONFIG_AUDITSYSCALL
|
||||
|
|
@ -1345,15 +1369,14 @@ error:
|
|||
}
|
||||
|
||||
/* Remove an existing rule from filterlist. */
|
||||
static inline int audit_del_rule(struct audit_entry *entry,
|
||||
struct list_head *list)
|
||||
static inline int audit_del_rule(struct audit_entry *entry)
|
||||
{
|
||||
struct audit_entry *e;
|
||||
struct audit_field *inode_f = entry->rule.inode_f;
|
||||
struct audit_watch *watch, *tmp_watch = entry->rule.watch;
|
||||
struct audit_tree *tree = entry->rule.tree;
|
||||
struct list_head *list;
|
||||
LIST_HEAD(inotify_list);
|
||||
int h, ret = 0;
|
||||
int ret = 0;
|
||||
#ifdef CONFIG_AUDITSYSCALL
|
||||
int dont_count = 0;
|
||||
|
||||
|
|
@ -1363,13 +1386,8 @@ static inline int audit_del_rule(struct audit_entry *entry,
|
|||
dont_count = 1;
|
||||
#endif
|
||||
|
||||
if (inode_f) {
|
||||
h = audit_hash_ino(inode_f->val);
|
||||
list = &audit_inode_hash[h];
|
||||
}
|
||||
|
||||
mutex_lock(&audit_filter_mutex);
|
||||
e = audit_find_rule(entry, list);
|
||||
e = audit_find_rule(entry, &list);
|
||||
if (!e) {
|
||||
mutex_unlock(&audit_filter_mutex);
|
||||
ret = -ENOENT;
|
||||
|
|
@ -1404,6 +1422,7 @@ static inline int audit_del_rule(struct audit_entry *entry,
|
|||
audit_remove_tree_rule(&e->rule);
|
||||
|
||||
list_del_rcu(&e->list);
|
||||
list_del(&e->rule.list);
|
||||
call_rcu(&e->rcu, audit_free_rule_rcu);
|
||||
|
||||
#ifdef CONFIG_AUDITSYSCALL
|
||||
|
|
@ -1432,30 +1451,16 @@ out:
|
|||
static void audit_list(int pid, int seq, struct sk_buff_head *q)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct audit_entry *entry;
|
||||
struct audit_krule *r;
|
||||
int i;
|
||||
|
||||
/* This is a blocking read, so use audit_filter_mutex instead of rcu
|
||||
* iterator to sync with list writers. */
|
||||
for (i=0; i<AUDIT_NR_FILTERS; i++) {
|
||||
list_for_each_entry(entry, &audit_filter_list[i], list) {
|
||||
list_for_each_entry(r, &audit_rules_list[i], list) {
|
||||
struct audit_rule *rule;
|
||||
|
||||
rule = audit_krule_to_rule(&entry->rule);
|
||||
if (unlikely(!rule))
|
||||
break;
|
||||
skb = audit_make_reply(pid, seq, AUDIT_LIST, 0, 1,
|
||||
rule, sizeof(*rule));
|
||||
if (skb)
|
||||
skb_queue_tail(q, skb);
|
||||
kfree(rule);
|
||||
}
|
||||
}
|
||||
for (i = 0; i < AUDIT_INODE_BUCKETS; i++) {
|
||||
list_for_each_entry(entry, &audit_inode_hash[i], list) {
|
||||
struct audit_rule *rule;
|
||||
|
||||
rule = audit_krule_to_rule(&entry->rule);
|
||||
rule = audit_krule_to_rule(r);
|
||||
if (unlikely(!rule))
|
||||
break;
|
||||
skb = audit_make_reply(pid, seq, AUDIT_LIST, 0, 1,
|
||||
|
|
@ -1474,30 +1479,16 @@ static void audit_list(int pid, int seq, struct sk_buff_head *q)
|
|||
static void audit_list_rules(int pid, int seq, struct sk_buff_head *q)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct audit_entry *e;
|
||||
struct audit_krule *r;
|
||||
int i;
|
||||
|
||||
/* This is a blocking read, so use audit_filter_mutex instead of rcu
|
||||
* iterator to sync with list writers. */
|
||||
for (i=0; i<AUDIT_NR_FILTERS; i++) {
|
||||
list_for_each_entry(e, &audit_filter_list[i], list) {
|
||||
list_for_each_entry(r, &audit_rules_list[i], list) {
|
||||
struct audit_rule_data *data;
|
||||
|
||||
data = audit_krule_to_data(&e->rule);
|
||||
if (unlikely(!data))
|
||||
break;
|
||||
skb = audit_make_reply(pid, seq, AUDIT_LIST_RULES, 0, 1,
|
||||
data, sizeof(*data) + data->buflen);
|
||||
if (skb)
|
||||
skb_queue_tail(q, skb);
|
||||
kfree(data);
|
||||
}
|
||||
}
|
||||
for (i=0; i< AUDIT_INODE_BUCKETS; i++) {
|
||||
list_for_each_entry(e, &audit_inode_hash[i], list) {
|
||||
struct audit_rule_data *data;
|
||||
|
||||
data = audit_krule_to_data(&e->rule);
|
||||
data = audit_krule_to_data(r);
|
||||
if (unlikely(!data))
|
||||
break;
|
||||
skb = audit_make_reply(pid, seq, AUDIT_LIST_RULES, 0, 1,
|
||||
|
|
@ -1603,8 +1594,7 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data,
|
|||
if (IS_ERR(entry))
|
||||
return PTR_ERR(entry);
|
||||
|
||||
err = audit_add_rule(entry,
|
||||
&audit_filter_list[entry->rule.listnr]);
|
||||
err = audit_add_rule(entry);
|
||||
audit_log_rule_change(loginuid, sessionid, sid, "add",
|
||||
&entry->rule, !err);
|
||||
|
||||
|
|
@ -1620,8 +1610,7 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data,
|
|||
if (IS_ERR(entry))
|
||||
return PTR_ERR(entry);
|
||||
|
||||
err = audit_del_rule(entry,
|
||||
&audit_filter_list[entry->rule.listnr]);
|
||||
err = audit_del_rule(entry);
|
||||
audit_log_rule_change(loginuid, sessionid, sid, "remove",
|
||||
&entry->rule, !err);
|
||||
|
||||
|
|
@ -1634,28 +1623,29 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data,
|
|||
return err;
|
||||
}
|
||||
|
||||
int audit_comparator(const u32 left, const u32 op, const u32 right)
|
||||
int audit_comparator(u32 left, u32 op, u32 right)
|
||||
{
|
||||
switch (op) {
|
||||
case AUDIT_EQUAL:
|
||||
case Audit_equal:
|
||||
return (left == right);
|
||||
case AUDIT_NOT_EQUAL:
|
||||
case Audit_not_equal:
|
||||
return (left != right);
|
||||
case AUDIT_LESS_THAN:
|
||||
case Audit_lt:
|
||||
return (left < right);
|
||||
case AUDIT_LESS_THAN_OR_EQUAL:
|
||||
case Audit_le:
|
||||
return (left <= right);
|
||||
case AUDIT_GREATER_THAN:
|
||||
case Audit_gt:
|
||||
return (left > right);
|
||||
case AUDIT_GREATER_THAN_OR_EQUAL:
|
||||
case Audit_ge:
|
||||
return (left >= right);
|
||||
case AUDIT_BIT_MASK:
|
||||
case Audit_bitmask:
|
||||
return (left & right);
|
||||
case AUDIT_BIT_TEST:
|
||||
case Audit_bittest:
|
||||
return ((left & right) == right);
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Compare given dentry name with last component in given path,
|
||||
|
|
@ -1778,6 +1768,43 @@ unlock_and_return:
|
|||
return result;
|
||||
}
|
||||
|
||||
static int update_lsm_rule(struct audit_krule *r)
|
||||
{
|
||||
struct audit_entry *entry = container_of(r, struct audit_entry, rule);
|
||||
struct audit_entry *nentry;
|
||||
struct audit_watch *watch;
|
||||
struct audit_tree *tree;
|
||||
int err = 0;
|
||||
|
||||
if (!security_audit_rule_known(r))
|
||||
return 0;
|
||||
|
||||
watch = r->watch;
|
||||
tree = r->tree;
|
||||
nentry = audit_dupe_rule(r, watch);
|
||||
if (IS_ERR(nentry)) {
|
||||
/* save the first error encountered for the
|
||||
* return value */
|
||||
err = PTR_ERR(nentry);
|
||||
audit_panic("error updating LSM filters");
|
||||
if (watch)
|
||||
list_del(&r->rlist);
|
||||
list_del_rcu(&entry->list);
|
||||
list_del(&r->list);
|
||||
} else {
|
||||
if (watch) {
|
||||
list_add(&nentry->rule.rlist, &watch->rules);
|
||||
list_del(&r->rlist);
|
||||
} else if (tree)
|
||||
list_replace_init(&r->rlist, &nentry->rule.rlist);
|
||||
list_replace_rcu(&entry->list, &nentry->list);
|
||||
list_replace(&r->list, &nentry->rule.list);
|
||||
}
|
||||
call_rcu(&entry->rcu, audit_free_rule_rcu);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* This function will re-initialize the lsm_rule field of all applicable rules.
|
||||
* It will traverse the filter lists serarching for rules that contain LSM
|
||||
* specific filter fields. When such a rule is found, it is copied, the
|
||||
|
|
@ -1785,45 +1812,19 @@ unlock_and_return:
|
|||
* updated rule. */
|
||||
int audit_update_lsm_rules(void)
|
||||
{
|
||||
struct audit_entry *entry, *n, *nentry;
|
||||
struct audit_watch *watch;
|
||||
struct audit_tree *tree;
|
||||
struct audit_krule *r, *n;
|
||||
int i, err = 0;
|
||||
|
||||
/* audit_filter_mutex synchronizes the writers */
|
||||
mutex_lock(&audit_filter_mutex);
|
||||
|
||||
for (i = 0; i < AUDIT_NR_FILTERS; i++) {
|
||||
list_for_each_entry_safe(entry, n, &audit_filter_list[i], list) {
|
||||
if (!security_audit_rule_known(&entry->rule))
|
||||
continue;
|
||||
|
||||
watch = entry->rule.watch;
|
||||
tree = entry->rule.tree;
|
||||
nentry = audit_dupe_rule(&entry->rule, watch);
|
||||
if (IS_ERR(nentry)) {
|
||||
/* save the first error encountered for the
|
||||
* return value */
|
||||
if (!err)
|
||||
err = PTR_ERR(nentry);
|
||||
audit_panic("error updating LSM filters");
|
||||
if (watch)
|
||||
list_del(&entry->rule.rlist);
|
||||
list_del_rcu(&entry->list);
|
||||
} else {
|
||||
if (watch) {
|
||||
list_add(&nentry->rule.rlist,
|
||||
&watch->rules);
|
||||
list_del(&entry->rule.rlist);
|
||||
} else if (tree)
|
||||
list_replace_init(&entry->rule.rlist,
|
||||
&nentry->rule.rlist);
|
||||
list_replace_rcu(&entry->list, &nentry->list);
|
||||
}
|
||||
call_rcu(&entry->rcu, audit_free_rule_rcu);
|
||||
list_for_each_entry_safe(r, n, &audit_rules_list[i], list) {
|
||||
int res = update_lsm_rule(r);
|
||||
if (!err)
|
||||
err = res;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&audit_filter_mutex);
|
||||
|
||||
return err;
|
||||
|
|
|
|||
934
kernel/auditsc.c
934
kernel/auditsc.c
File diff suppressed because it is too large
Load diff
|
|
@ -7,6 +7,7 @@
|
|||
* 30 May 2002: Cleanup, Robert M. Love <rml@tech9.net>
|
||||
*/
|
||||
|
||||
#include <linux/audit.h>
|
||||
#include <linux/capability.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/module.h>
|
||||
|
|
@ -14,12 +15,7 @@
|
|||
#include <linux/syscalls.h>
|
||||
#include <linux/pid_namespace.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
/*
|
||||
* This lock protects task->cap_* for all tasks including current.
|
||||
* Locking rule: acquire this prior to tasklist_lock.
|
||||
*/
|
||||
static DEFINE_SPINLOCK(task_capability_lock);
|
||||
#include "cred-internals.h"
|
||||
|
||||
/*
|
||||
* Leveraged for setting/resetting capabilities
|
||||
|
|
@ -33,6 +29,17 @@ EXPORT_SYMBOL(__cap_empty_set);
|
|||
EXPORT_SYMBOL(__cap_full_set);
|
||||
EXPORT_SYMBOL(__cap_init_eff_set);
|
||||
|
||||
#ifdef CONFIG_SECURITY_FILE_CAPABILITIES
|
||||
int file_caps_enabled = 1;
|
||||
|
||||
static int __init file_caps_disable(char *str)
|
||||
{
|
||||
file_caps_enabled = 0;
|
||||
return 1;
|
||||
}
|
||||
__setup("no_file_caps", file_caps_disable);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* More recent versions of libcap are available from:
|
||||
*
|
||||
|
|
@ -115,167 +122,12 @@ static int cap_validate_magic(cap_user_header_t header, unsigned *tocopy)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_SECURITY_FILE_CAPABILITIES
|
||||
|
||||
/*
|
||||
* Without filesystem capability support, we nominally support one process
|
||||
* setting the capabilities of another
|
||||
*/
|
||||
static inline int cap_get_target_pid(pid_t pid, kernel_cap_t *pEp,
|
||||
kernel_cap_t *pIp, kernel_cap_t *pPp)
|
||||
{
|
||||
struct task_struct *target;
|
||||
int ret;
|
||||
|
||||
spin_lock(&task_capability_lock);
|
||||
read_lock(&tasklist_lock);
|
||||
|
||||
if (pid && pid != task_pid_vnr(current)) {
|
||||
target = find_task_by_vpid(pid);
|
||||
if (!target) {
|
||||
ret = -ESRCH;
|
||||
goto out;
|
||||
}
|
||||
} else
|
||||
target = current;
|
||||
|
||||
ret = security_capget(target, pEp, pIp, pPp);
|
||||
|
||||
out:
|
||||
read_unlock(&tasklist_lock);
|
||||
spin_unlock(&task_capability_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* cap_set_pg - set capabilities for all processes in a given process
|
||||
* group. We call this holding task_capability_lock and tasklist_lock.
|
||||
*/
|
||||
static inline int cap_set_pg(int pgrp_nr, kernel_cap_t *effective,
|
||||
kernel_cap_t *inheritable,
|
||||
kernel_cap_t *permitted)
|
||||
{
|
||||
struct task_struct *g, *target;
|
||||
int ret = -EPERM;
|
||||
int found = 0;
|
||||
struct pid *pgrp;
|
||||
|
||||
spin_lock(&task_capability_lock);
|
||||
read_lock(&tasklist_lock);
|
||||
|
||||
pgrp = find_vpid(pgrp_nr);
|
||||
do_each_pid_task(pgrp, PIDTYPE_PGID, g) {
|
||||
target = g;
|
||||
while_each_thread(g, target) {
|
||||
if (!security_capset_check(target, effective,
|
||||
inheritable, permitted)) {
|
||||
security_capset_set(target, effective,
|
||||
inheritable, permitted);
|
||||
ret = 0;
|
||||
}
|
||||
found = 1;
|
||||
}
|
||||
} while_each_pid_task(pgrp, PIDTYPE_PGID, g);
|
||||
|
||||
read_unlock(&tasklist_lock);
|
||||
spin_unlock(&task_capability_lock);
|
||||
|
||||
if (!found)
|
||||
ret = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* cap_set_all - set capabilities for all processes other than init
|
||||
* and self. We call this holding task_capability_lock and tasklist_lock.
|
||||
*/
|
||||
static inline int cap_set_all(kernel_cap_t *effective,
|
||||
kernel_cap_t *inheritable,
|
||||
kernel_cap_t *permitted)
|
||||
{
|
||||
struct task_struct *g, *target;
|
||||
int ret = -EPERM;
|
||||
int found = 0;
|
||||
|
||||
spin_lock(&task_capability_lock);
|
||||
read_lock(&tasklist_lock);
|
||||
|
||||
do_each_thread(g, target) {
|
||||
if (target == current
|
||||
|| is_container_init(target->group_leader))
|
||||
continue;
|
||||
found = 1;
|
||||
if (security_capset_check(target, effective, inheritable,
|
||||
permitted))
|
||||
continue;
|
||||
ret = 0;
|
||||
security_capset_set(target, effective, inheritable, permitted);
|
||||
} while_each_thread(g, target);
|
||||
|
||||
read_unlock(&tasklist_lock);
|
||||
spin_unlock(&task_capability_lock);
|
||||
|
||||
if (!found)
|
||||
ret = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Given the target pid does not refer to the current process we
|
||||
* need more elaborate support... (This support is not present when
|
||||
* filesystem capabilities are configured.)
|
||||
*/
|
||||
static inline int do_sys_capset_other_tasks(pid_t pid, kernel_cap_t *effective,
|
||||
kernel_cap_t *inheritable,
|
||||
kernel_cap_t *permitted)
|
||||
{
|
||||
struct task_struct *target;
|
||||
int ret;
|
||||
|
||||
if (!capable(CAP_SETPCAP))
|
||||
return -EPERM;
|
||||
|
||||
if (pid == -1) /* all procs other than current and init */
|
||||
return cap_set_all(effective, inheritable, permitted);
|
||||
|
||||
else if (pid < 0) /* all procs in process group */
|
||||
return cap_set_pg(-pid, effective, inheritable, permitted);
|
||||
|
||||
/* target != current */
|
||||
spin_lock(&task_capability_lock);
|
||||
read_lock(&tasklist_lock);
|
||||
|
||||
target = find_task_by_vpid(pid);
|
||||
if (!target)
|
||||
ret = -ESRCH;
|
||||
else {
|
||||
ret = security_capset_check(target, effective, inheritable,
|
||||
permitted);
|
||||
|
||||
/* having verified that the proposed changes are legal,
|
||||
we now put them into effect. */
|
||||
if (!ret)
|
||||
security_capset_set(target, effective, inheritable,
|
||||
permitted);
|
||||
}
|
||||
|
||||
read_unlock(&tasklist_lock);
|
||||
spin_unlock(&task_capability_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#else /* ie., def CONFIG_SECURITY_FILE_CAPABILITIES */
|
||||
|
||||
/*
|
||||
* If we have configured with filesystem capability support, then the
|
||||
* only thing that can change the capabilities of the current process
|
||||
* is the current process. As such, we can't be in this code at the
|
||||
* same time as we are in the process of setting capabilities in this
|
||||
* process. The net result is that we can limit our use of locks to
|
||||
* when we are reading the caps of another process.
|
||||
* The only thing that can change the capabilities of the current
|
||||
* process is the current process. As such, we can't be in this code
|
||||
* at the same time as we are in the process of setting capabilities
|
||||
* in this process. The net result is that we can limit our use of
|
||||
* locks to when we are reading the caps of another process.
|
||||
*/
|
||||
static inline int cap_get_target_pid(pid_t pid, kernel_cap_t *pEp,
|
||||
kernel_cap_t *pIp, kernel_cap_t *pPp)
|
||||
|
|
@ -285,7 +137,6 @@ static inline int cap_get_target_pid(pid_t pid, kernel_cap_t *pEp,
|
|||
if (pid && (pid != task_pid_vnr(current))) {
|
||||
struct task_struct *target;
|
||||
|
||||
spin_lock(&task_capability_lock);
|
||||
read_lock(&tasklist_lock);
|
||||
|
||||
target = find_task_by_vpid(pid);
|
||||
|
|
@ -295,50 +146,12 @@ static inline int cap_get_target_pid(pid_t pid, kernel_cap_t *pEp,
|
|||
ret = security_capget(target, pEp, pIp, pPp);
|
||||
|
||||
read_unlock(&tasklist_lock);
|
||||
spin_unlock(&task_capability_lock);
|
||||
} else
|
||||
ret = security_capget(current, pEp, pIp, pPp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* With filesystem capability support configured, the kernel does not
|
||||
* permit the changing of capabilities in one process by another
|
||||
* process. (CAP_SETPCAP has much less broad semantics when configured
|
||||
* this way.)
|
||||
*/
|
||||
static inline int do_sys_capset_other_tasks(pid_t pid,
|
||||
kernel_cap_t *effective,
|
||||
kernel_cap_t *inheritable,
|
||||
kernel_cap_t *permitted)
|
||||
{
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
#endif /* ie., ndef CONFIG_SECURITY_FILE_CAPABILITIES */
|
||||
|
||||
/*
|
||||
* Atomically modify the effective capabilities returning the original
|
||||
* value. No permission check is performed here - it is assumed that the
|
||||
* caller is permitted to set the desired effective capabilities.
|
||||
*/
|
||||
kernel_cap_t cap_set_effective(const kernel_cap_t pE_new)
|
||||
{
|
||||
kernel_cap_t pE_old;
|
||||
|
||||
spin_lock(&task_capability_lock);
|
||||
|
||||
pE_old = current->cap_effective;
|
||||
current->cap_effective = pE_new;
|
||||
|
||||
spin_unlock(&task_capability_lock);
|
||||
|
||||
return pE_old;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(cap_set_effective);
|
||||
|
||||
/**
|
||||
* sys_capget - get the capabilities of a given process.
|
||||
* @header: pointer to struct that contains capability version and
|
||||
|
|
@ -348,7 +161,7 @@ EXPORT_SYMBOL(cap_set_effective);
|
|||
*
|
||||
* Returns 0 on success and < 0 on error.
|
||||
*/
|
||||
asmlinkage long sys_capget(cap_user_header_t header, cap_user_data_t dataptr)
|
||||
SYSCALL_DEFINE2(capget, cap_user_header_t, header, cap_user_data_t, dataptr)
|
||||
{
|
||||
int ret = 0;
|
||||
pid_t pid;
|
||||
|
|
@ -366,7 +179,6 @@ asmlinkage long sys_capget(cap_user_header_t header, cap_user_data_t dataptr)
|
|||
return -EINVAL;
|
||||
|
||||
ret = cap_get_target_pid(pid, &pE, &pI, &pP);
|
||||
|
||||
if (!ret) {
|
||||
struct __user_cap_data_struct kdata[_KERNEL_CAPABILITY_U32S];
|
||||
unsigned i;
|
||||
|
|
@ -412,24 +224,23 @@ asmlinkage long sys_capget(cap_user_header_t header, cap_user_data_t dataptr)
|
|||
* @data: pointer to struct that contains the effective, permitted,
|
||||
* and inheritable capabilities
|
||||
*
|
||||
* Set capabilities for a given process, all processes, or all
|
||||
* processes in a given process group.
|
||||
* Set capabilities for the current process only. The ability to any other
|
||||
* process(es) has been deprecated and removed.
|
||||
*
|
||||
* The restrictions on setting capabilities are specified as:
|
||||
*
|
||||
* [pid is for the 'target' task. 'current' is the calling task.]
|
||||
*
|
||||
* I: any raised capabilities must be a subset of the (old current) permitted
|
||||
* P: any raised capabilities must be a subset of the (old current) permitted
|
||||
* E: must be set to a subset of (new target) permitted
|
||||
* I: any raised capabilities must be a subset of the old permitted
|
||||
* P: any raised capabilities must be a subset of the old permitted
|
||||
* E: must be set to a subset of new permitted
|
||||
*
|
||||
* Returns 0 on success and < 0 on error.
|
||||
*/
|
||||
asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data)
|
||||
SYSCALL_DEFINE2(capset, cap_user_header_t, header, const cap_user_data_t, data)
|
||||
{
|
||||
struct __user_cap_data_struct kdata[_KERNEL_CAPABILITY_U32S];
|
||||
unsigned i, tocopy;
|
||||
kernel_cap_t inheritable, permitted, effective;
|
||||
struct cred *new;
|
||||
int ret;
|
||||
pid_t pid;
|
||||
|
||||
|
|
@ -440,10 +251,13 @@ asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data)
|
|||
if (get_user(pid, &header->pid))
|
||||
return -EFAULT;
|
||||
|
||||
if (copy_from_user(&kdata, data, tocopy
|
||||
* sizeof(struct __user_cap_data_struct))) {
|
||||
/* may only affect current now */
|
||||
if (pid != 0 && pid != task_pid_vnr(current))
|
||||
return -EPERM;
|
||||
|
||||
if (copy_from_user(&kdata, data,
|
||||
tocopy * sizeof(struct __user_cap_data_struct)))
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
for (i = 0; i < tocopy; i++) {
|
||||
effective.cap[i] = kdata[i].effective;
|
||||
|
|
@ -457,32 +271,21 @@ asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data)
|
|||
i++;
|
||||
}
|
||||
|
||||
if (pid && (pid != task_pid_vnr(current)))
|
||||
ret = do_sys_capset_other_tasks(pid, &effective, &inheritable,
|
||||
&permitted);
|
||||
else {
|
||||
/*
|
||||
* This lock is required even when filesystem
|
||||
* capability support is configured - it protects the
|
||||
* sys_capget() call from returning incorrect data in
|
||||
* the case that the targeted process is not the
|
||||
* current one.
|
||||
*/
|
||||
spin_lock(&task_capability_lock);
|
||||
new = prepare_creds();
|
||||
if (!new)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = security_capset_check(current, &effective, &inheritable,
|
||||
&permitted);
|
||||
/*
|
||||
* Having verified that the proposed changes are
|
||||
* legal, we now put them into effect.
|
||||
*/
|
||||
if (!ret)
|
||||
security_capset_set(current, &effective, &inheritable,
|
||||
&permitted);
|
||||
spin_unlock(&task_capability_lock);
|
||||
}
|
||||
ret = security_capset(new, current_cred(),
|
||||
&effective, &inheritable, &permitted);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
audit_log_capset(pid, new, current_cred());
|
||||
|
||||
return commit_creds(new);
|
||||
|
||||
error:
|
||||
abort_creds(new);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -498,7 +301,12 @@ asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data)
|
|||
*/
|
||||
int capable(int cap)
|
||||
{
|
||||
if (has_capability(current, cap)) {
|
||||
if (unlikely(!cap_valid(cap))) {
|
||||
printk(KERN_CRIT "capable() called with invalid cap=%u\n", cap);
|
||||
BUG();
|
||||
}
|
||||
|
||||
if (security_capable(cap) == 0) {
|
||||
current->flags |= PF_SUPERPRIV;
|
||||
return 1;
|
||||
}
|
||||
|
|
|
|||
333
kernel/cgroup.c
333
kernel/cgroup.c
|
|
@ -84,7 +84,7 @@ struct cgroupfs_root {
|
|||
/* Tracks how many cgroups are currently defined in hierarchy.*/
|
||||
int number_of_cgroups;
|
||||
|
||||
/* A list running through the mounted hierarchies */
|
||||
/* A list running through the active hierarchies */
|
||||
struct list_head root_list;
|
||||
|
||||
/* Hierarchy-specific flags */
|
||||
|
|
@ -116,7 +116,6 @@ static int root_count;
|
|||
* be called.
|
||||
*/
|
||||
static int need_forkexit_callback __read_mostly;
|
||||
static int need_mm_owner_callback __read_mostly;
|
||||
|
||||
/* convenient tests for these bits */
|
||||
inline int cgroup_is_removed(const struct cgroup *cgrp)
|
||||
|
|
@ -149,8 +148,8 @@ static int notify_on_release(const struct cgroup *cgrp)
|
|||
#define for_each_subsys(_root, _ss) \
|
||||
list_for_each_entry(_ss, &_root->subsys_list, sibling)
|
||||
|
||||
/* for_each_root() allows you to iterate across the active hierarchies */
|
||||
#define for_each_root(_root) \
|
||||
/* for_each_active_root() allows you to iterate across the active hierarchies */
|
||||
#define for_each_active_root(_root) \
|
||||
list_for_each_entry(_root, &roots, root_list)
|
||||
|
||||
/* the list of cgroups eligible for automatic release. Protected by
|
||||
|
|
@ -272,7 +271,7 @@ static void __put_css_set(struct css_set *cg, int taskexit)
|
|||
|
||||
rcu_read_lock();
|
||||
for (i = 0; i < CGROUP_SUBSYS_COUNT; i++) {
|
||||
struct cgroup *cgrp = cg->subsys[i]->cgroup;
|
||||
struct cgroup *cgrp = rcu_dereference(cg->subsys[i]->cgroup);
|
||||
if (atomic_dec_and_test(&cgrp->count) &&
|
||||
notify_on_release(cgrp)) {
|
||||
if (taskexit)
|
||||
|
|
@ -385,6 +384,25 @@ static int allocate_cg_links(int count, struct list_head *tmp)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* link_css_set - a helper function to link a css_set to a cgroup
|
||||
* @tmp_cg_links: cg_cgroup_link objects allocated by allocate_cg_links()
|
||||
* @cg: the css_set to be linked
|
||||
* @cgrp: the destination cgroup
|
||||
*/
|
||||
static void link_css_set(struct list_head *tmp_cg_links,
|
||||
struct css_set *cg, struct cgroup *cgrp)
|
||||
{
|
||||
struct cg_cgroup_link *link;
|
||||
|
||||
BUG_ON(list_empty(tmp_cg_links));
|
||||
link = list_first_entry(tmp_cg_links, struct cg_cgroup_link,
|
||||
cgrp_link_list);
|
||||
link->cg = cg;
|
||||
list_move(&link->cgrp_link_list, &cgrp->css_sets);
|
||||
list_add(&link->cg_link_list, &cg->cg_links);
|
||||
}
|
||||
|
||||
/*
|
||||
* find_css_set() takes an existing cgroup group and a
|
||||
* cgroup object, and returns a css_set object that's
|
||||
|
|
@ -400,7 +418,6 @@ static struct css_set *find_css_set(
|
|||
int i;
|
||||
|
||||
struct list_head tmp_cg_links;
|
||||
struct cg_cgroup_link *link;
|
||||
|
||||
struct hlist_head *hhead;
|
||||
|
||||
|
|
@ -445,26 +462,11 @@ static struct css_set *find_css_set(
|
|||
* only do it for the first subsystem in each
|
||||
* hierarchy
|
||||
*/
|
||||
if (ss->root->subsys_list.next == &ss->sibling) {
|
||||
BUG_ON(list_empty(&tmp_cg_links));
|
||||
link = list_entry(tmp_cg_links.next,
|
||||
struct cg_cgroup_link,
|
||||
cgrp_link_list);
|
||||
list_del(&link->cgrp_link_list);
|
||||
list_add(&link->cgrp_link_list, &cgrp->css_sets);
|
||||
link->cg = res;
|
||||
list_add(&link->cg_link_list, &res->cg_links);
|
||||
}
|
||||
}
|
||||
if (list_empty(&rootnode.subsys_list)) {
|
||||
link = list_entry(tmp_cg_links.next,
|
||||
struct cg_cgroup_link,
|
||||
cgrp_link_list);
|
||||
list_del(&link->cgrp_link_list);
|
||||
list_add(&link->cgrp_link_list, &dummytop->css_sets);
|
||||
link->cg = res;
|
||||
list_add(&link->cg_link_list, &res->cg_links);
|
||||
if (ss->root->subsys_list.next == &ss->sibling)
|
||||
link_css_set(&tmp_cg_links, res, cgrp);
|
||||
}
|
||||
if (list_empty(&rootnode.subsys_list))
|
||||
link_css_set(&tmp_cg_links, res, dummytop);
|
||||
|
||||
BUG_ON(!list_empty(&tmp_cg_links));
|
||||
|
||||
|
|
@ -571,9 +573,8 @@ static struct inode *cgroup_new_inode(mode_t mode, struct super_block *sb)
|
|||
|
||||
if (inode) {
|
||||
inode->i_mode = mode;
|
||||
inode->i_uid = current->fsuid;
|
||||
inode->i_gid = current->fsgid;
|
||||
inode->i_blocks = 0;
|
||||
inode->i_uid = current_fsuid();
|
||||
inode->i_gid = current_fsgid();
|
||||
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
|
||||
inode->i_mapping->backing_dev_info = &cgroup_backing_dev_info;
|
||||
}
|
||||
|
|
@ -588,11 +589,18 @@ static void cgroup_call_pre_destroy(struct cgroup *cgrp)
|
|||
{
|
||||
struct cgroup_subsys *ss;
|
||||
for_each_subsys(cgrp->root, ss)
|
||||
if (ss->pre_destroy && cgrp->subsys[ss->subsys_id])
|
||||
if (ss->pre_destroy)
|
||||
ss->pre_destroy(ss, cgrp);
|
||||
return;
|
||||
}
|
||||
|
||||
static void free_cgroup_rcu(struct rcu_head *obj)
|
||||
{
|
||||
struct cgroup *cgrp = container_of(obj, struct cgroup, rcu_head);
|
||||
|
||||
kfree(cgrp);
|
||||
}
|
||||
|
||||
static void cgroup_diput(struct dentry *dentry, struct inode *inode)
|
||||
{
|
||||
/* is dentry a directory ? if so, kfree() associated cgroup */
|
||||
|
|
@ -612,19 +620,19 @@ static void cgroup_diput(struct dentry *dentry, struct inode *inode)
|
|||
/*
|
||||
* Release the subsystem state objects.
|
||||
*/
|
||||
for_each_subsys(cgrp->root, ss) {
|
||||
if (cgrp->subsys[ss->subsys_id])
|
||||
ss->destroy(ss, cgrp);
|
||||
}
|
||||
for_each_subsys(cgrp->root, ss)
|
||||
ss->destroy(ss, cgrp);
|
||||
|
||||
cgrp->root->number_of_cgroups--;
|
||||
mutex_unlock(&cgroup_mutex);
|
||||
|
||||
/* Drop the active superblock reference that we took when we
|
||||
* created the cgroup */
|
||||
/*
|
||||
* Drop the active superblock reference that we took when we
|
||||
* created the cgroup
|
||||
*/
|
||||
deactivate_super(cgrp->root->sb);
|
||||
|
||||
kfree(cgrp);
|
||||
call_rcu(&cgrp->rcu_head, free_cgroup_rcu);
|
||||
}
|
||||
iput(inode);
|
||||
}
|
||||
|
|
@ -714,23 +722,26 @@ static int rebind_subsystems(struct cgroupfs_root *root,
|
|||
BUG_ON(cgrp->subsys[i]);
|
||||
BUG_ON(!dummytop->subsys[i]);
|
||||
BUG_ON(dummytop->subsys[i]->cgroup != dummytop);
|
||||
mutex_lock(&ss->hierarchy_mutex);
|
||||
cgrp->subsys[i] = dummytop->subsys[i];
|
||||
cgrp->subsys[i]->cgroup = cgrp;
|
||||
list_add(&ss->sibling, &root->subsys_list);
|
||||
rcu_assign_pointer(ss->root, root);
|
||||
list_move(&ss->sibling, &root->subsys_list);
|
||||
ss->root = root;
|
||||
if (ss->bind)
|
||||
ss->bind(ss, cgrp);
|
||||
|
||||
mutex_unlock(&ss->hierarchy_mutex);
|
||||
} else if (bit & removed_bits) {
|
||||
/* We're removing this subsystem */
|
||||
BUG_ON(cgrp->subsys[i] != dummytop->subsys[i]);
|
||||
BUG_ON(cgrp->subsys[i]->cgroup != cgrp);
|
||||
mutex_lock(&ss->hierarchy_mutex);
|
||||
if (ss->bind)
|
||||
ss->bind(ss, dummytop);
|
||||
dummytop->subsys[i]->cgroup = dummytop;
|
||||
cgrp->subsys[i] = NULL;
|
||||
rcu_assign_pointer(subsys[i]->root, &rootnode);
|
||||
list_del(&ss->sibling);
|
||||
subsys[i]->root = &rootnode;
|
||||
list_move(&ss->sibling, &rootnode.subsys_list);
|
||||
mutex_unlock(&ss->hierarchy_mutex);
|
||||
} else if (bit & final_bits) {
|
||||
/* Subsystem state should already exist */
|
||||
BUG_ON(!cgrp->subsys[i]);
|
||||
|
|
@ -992,7 +1003,7 @@ static int cgroup_get_sb(struct file_system_type *fs_type,
|
|||
root = NULL;
|
||||
} else {
|
||||
/* New superblock */
|
||||
struct cgroup *cgrp = &root->top_cgroup;
|
||||
struct cgroup *root_cgrp = &root->top_cgroup;
|
||||
struct inode *inode;
|
||||
int i;
|
||||
|
||||
|
|
@ -1033,7 +1044,7 @@ static int cgroup_get_sb(struct file_system_type *fs_type,
|
|||
list_add(&root->root_list, &roots);
|
||||
root_count++;
|
||||
|
||||
sb->s_root->d_fsdata = &root->top_cgroup;
|
||||
sb->s_root->d_fsdata = root_cgrp;
|
||||
root->top_cgroup.dentry = sb->s_root;
|
||||
|
||||
/* Link the top cgroup in this hierarchy into all
|
||||
|
|
@ -1044,29 +1055,18 @@ static int cgroup_get_sb(struct file_system_type *fs_type,
|
|||
struct hlist_node *node;
|
||||
struct css_set *cg;
|
||||
|
||||
hlist_for_each_entry(cg, node, hhead, hlist) {
|
||||
struct cg_cgroup_link *link;
|
||||
|
||||
BUG_ON(list_empty(&tmp_cg_links));
|
||||
link = list_entry(tmp_cg_links.next,
|
||||
struct cg_cgroup_link,
|
||||
cgrp_link_list);
|
||||
list_del(&link->cgrp_link_list);
|
||||
link->cg = cg;
|
||||
list_add(&link->cgrp_link_list,
|
||||
&root->top_cgroup.css_sets);
|
||||
list_add(&link->cg_link_list, &cg->cg_links);
|
||||
}
|
||||
hlist_for_each_entry(cg, node, hhead, hlist)
|
||||
link_css_set(&tmp_cg_links, cg, root_cgrp);
|
||||
}
|
||||
write_unlock(&css_set_lock);
|
||||
|
||||
free_cg_links(&tmp_cg_links);
|
||||
|
||||
BUG_ON(!list_empty(&cgrp->sibling));
|
||||
BUG_ON(!list_empty(&cgrp->children));
|
||||
BUG_ON(!list_empty(&root_cgrp->sibling));
|
||||
BUG_ON(!list_empty(&root_cgrp->children));
|
||||
BUG_ON(root->number_of_cgroups != 1);
|
||||
|
||||
cgroup_populate_dir(cgrp);
|
||||
cgroup_populate_dir(root_cgrp);
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
mutex_unlock(&cgroup_mutex);
|
||||
}
|
||||
|
|
@ -1115,10 +1115,9 @@ static void cgroup_kill_sb(struct super_block *sb) {
|
|||
}
|
||||
write_unlock(&css_set_lock);
|
||||
|
||||
if (!list_empty(&root->root_list)) {
|
||||
list_del(&root->root_list);
|
||||
root_count--;
|
||||
}
|
||||
list_del(&root->root_list);
|
||||
root_count--;
|
||||
|
||||
mutex_unlock(&cgroup_mutex);
|
||||
|
||||
kfree(root);
|
||||
|
|
@ -1147,14 +1146,16 @@ static inline struct cftype *__d_cft(struct dentry *dentry)
|
|||
* @buf: the buffer to write the path into
|
||||
* @buflen: the length of the buffer
|
||||
*
|
||||
* Called with cgroup_mutex held. Writes path of cgroup into buf.
|
||||
* Returns 0 on success, -errno on error.
|
||||
* Called with cgroup_mutex held or else with an RCU-protected cgroup
|
||||
* reference. Writes path of cgroup into buf. Returns 0 on success,
|
||||
* -errno on error.
|
||||
*/
|
||||
int cgroup_path(const struct cgroup *cgrp, char *buf, int buflen)
|
||||
{
|
||||
char *start;
|
||||
struct dentry *dentry = rcu_dereference(cgrp->dentry);
|
||||
|
||||
if (cgrp == dummytop) {
|
||||
if (!dentry || cgrp == dummytop) {
|
||||
/*
|
||||
* Inactive subsystems have no dentry for their root
|
||||
* cgroup
|
||||
|
|
@ -1167,13 +1168,14 @@ int cgroup_path(const struct cgroup *cgrp, char *buf, int buflen)
|
|||
|
||||
*--start = '\0';
|
||||
for (;;) {
|
||||
int len = cgrp->dentry->d_name.len;
|
||||
int len = dentry->d_name.len;
|
||||
if ((start -= len) < buf)
|
||||
return -ENAMETOOLONG;
|
||||
memcpy(start, cgrp->dentry->d_name.name, len);
|
||||
cgrp = cgrp->parent;
|
||||
if (!cgrp)
|
||||
break;
|
||||
dentry = rcu_dereference(cgrp->dentry);
|
||||
if (!cgrp->parent)
|
||||
continue;
|
||||
if (--start < buf)
|
||||
|
|
@ -1218,7 +1220,7 @@ int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk)
|
|||
int retval = 0;
|
||||
struct cgroup_subsys *ss;
|
||||
struct cgroup *oldcgrp;
|
||||
struct css_set *cg = tsk->cgroups;
|
||||
struct css_set *cg;
|
||||
struct css_set *newcg;
|
||||
struct cgroupfs_root *root = cgrp->root;
|
||||
int subsys_id;
|
||||
|
|
@ -1238,11 +1240,16 @@ int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk)
|
|||
}
|
||||
}
|
||||
|
||||
task_lock(tsk);
|
||||
cg = tsk->cgroups;
|
||||
get_css_set(cg);
|
||||
task_unlock(tsk);
|
||||
/*
|
||||
* Locate or allocate a new css_set for this task,
|
||||
* based on its final set of cgroups
|
||||
*/
|
||||
newcg = find_css_set(cg, cgrp);
|
||||
put_css_set(cg);
|
||||
if (!newcg)
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
@ -1280,6 +1287,7 @@ int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk)
|
|||
static int attach_task_by_pid(struct cgroup *cgrp, u64 pid)
|
||||
{
|
||||
struct task_struct *tsk;
|
||||
const struct cred *cred = current_cred(), *tcred;
|
||||
int ret;
|
||||
|
||||
if (pid) {
|
||||
|
|
@ -1289,14 +1297,16 @@ static int attach_task_by_pid(struct cgroup *cgrp, u64 pid)
|
|||
rcu_read_unlock();
|
||||
return -ESRCH;
|
||||
}
|
||||
get_task_struct(tsk);
|
||||
rcu_read_unlock();
|
||||
|
||||
if ((current->euid) && (current->euid != tsk->uid)
|
||||
&& (current->euid != tsk->suid)) {
|
||||
put_task_struct(tsk);
|
||||
tcred = __task_cred(tsk);
|
||||
if (cred->euid &&
|
||||
cred->euid != tcred->uid &&
|
||||
cred->euid != tcred->suid) {
|
||||
rcu_read_unlock();
|
||||
return -EACCES;
|
||||
}
|
||||
get_task_struct(tsk);
|
||||
rcu_read_unlock();
|
||||
} else {
|
||||
tsk = current;
|
||||
get_task_struct(tsk);
|
||||
|
|
@ -1444,7 +1454,7 @@ static ssize_t cgroup_file_write(struct file *file, const char __user *buf,
|
|||
struct cftype *cft = __d_cft(file->f_dentry);
|
||||
struct cgroup *cgrp = __d_cgrp(file->f_dentry->d_parent);
|
||||
|
||||
if (!cft || cgroup_is_removed(cgrp))
|
||||
if (cgroup_is_removed(cgrp))
|
||||
return -ENODEV;
|
||||
if (cft->write)
|
||||
return cft->write(cgrp, cft, file, buf, nbytes, ppos);
|
||||
|
|
@ -1489,7 +1499,7 @@ static ssize_t cgroup_file_read(struct file *file, char __user *buf,
|
|||
struct cftype *cft = __d_cft(file->f_dentry);
|
||||
struct cgroup *cgrp = __d_cgrp(file->f_dentry->d_parent);
|
||||
|
||||
if (!cft || cgroup_is_removed(cgrp))
|
||||
if (cgroup_is_removed(cgrp))
|
||||
return -ENODEV;
|
||||
|
||||
if (cft->read)
|
||||
|
|
@ -1553,10 +1563,8 @@ static int cgroup_file_open(struct inode *inode, struct file *file)
|
|||
err = generic_file_open(inode, file);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
cft = __d_cft(file->f_dentry);
|
||||
if (!cft)
|
||||
return -ENODEV;
|
||||
|
||||
if (cft->read_map || cft->read_seq_string) {
|
||||
struct cgroup_seqfile_state *state =
|
||||
kzalloc(sizeof(*state), GFP_USER);
|
||||
|
|
@ -1670,7 +1678,7 @@ static int cgroup_create_dir(struct cgroup *cgrp, struct dentry *dentry,
|
|||
if (!error) {
|
||||
dentry->d_fsdata = cgrp;
|
||||
inc_nlink(parent->d_inode);
|
||||
cgrp->dentry = dentry;
|
||||
rcu_assign_pointer(cgrp->dentry, dentry);
|
||||
dget(dentry);
|
||||
}
|
||||
dput(dentry);
|
||||
|
|
@ -1811,6 +1819,7 @@ struct task_struct *cgroup_iter_next(struct cgroup *cgrp,
|
|||
{
|
||||
struct task_struct *res;
|
||||
struct list_head *l = it->task;
|
||||
struct cg_cgroup_link *link;
|
||||
|
||||
/* If the iterator cg is NULL, we have no tasks */
|
||||
if (!it->cg_link)
|
||||
|
|
@ -1818,7 +1827,8 @@ struct task_struct *cgroup_iter_next(struct cgroup *cgrp,
|
|||
res = list_entry(l, struct task_struct, cg_list);
|
||||
/* Advance iterator to find next entry */
|
||||
l = l->next;
|
||||
if (l == &res->cgroups->tasks) {
|
||||
link = list_entry(it->cg_link, struct cg_cgroup_link, cgrp_link_list);
|
||||
if (l == &link->cg->tasks) {
|
||||
/* We reached the end of this task list - move on to
|
||||
* the next cg_cgroup_link */
|
||||
cgroup_advance_iter(cgrp, it);
|
||||
|
|
@ -2012,14 +2022,16 @@ int cgroup_scan_tasks(struct cgroup_scanner *scan)
|
|||
*/
|
||||
static int pid_array_load(pid_t *pidarray, int npids, struct cgroup *cgrp)
|
||||
{
|
||||
int n = 0;
|
||||
int n = 0, pid;
|
||||
struct cgroup_iter it;
|
||||
struct task_struct *tsk;
|
||||
cgroup_iter_start(cgrp, &it);
|
||||
while ((tsk = cgroup_iter_next(cgrp, &it))) {
|
||||
if (unlikely(n == npids))
|
||||
break;
|
||||
pidarray[n++] = task_pid_vnr(tsk);
|
||||
pid = task_pid_vnr(tsk);
|
||||
if (pid > 0)
|
||||
pidarray[n++] = pid;
|
||||
}
|
||||
cgroup_iter_end(cgrp, &it);
|
||||
return n;
|
||||
|
|
@ -2051,7 +2063,6 @@ int cgroupstats_build(struct cgroupstats *stats, struct dentry *dentry)
|
|||
|
||||
ret = 0;
|
||||
cgrp = dentry->d_fsdata;
|
||||
rcu_read_lock();
|
||||
|
||||
cgroup_iter_start(cgrp, &it);
|
||||
while ((tsk = cgroup_iter_next(cgrp, &it))) {
|
||||
|
|
@ -2076,7 +2087,6 @@ int cgroupstats_build(struct cgroupstats *stats, struct dentry *dentry)
|
|||
}
|
||||
cgroup_iter_end(cgrp, &it);
|
||||
|
||||
rcu_read_unlock();
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -2323,7 +2333,7 @@ static void init_cgroup_css(struct cgroup_subsys_state *css,
|
|||
struct cgroup *cgrp)
|
||||
{
|
||||
css->cgroup = cgrp;
|
||||
atomic_set(&css->refcnt, 0);
|
||||
atomic_set(&css->refcnt, 1);
|
||||
css->flags = 0;
|
||||
if (cgrp == dummytop)
|
||||
set_bit(CSS_ROOT, &css->flags);
|
||||
|
|
@ -2331,6 +2341,29 @@ static void init_cgroup_css(struct cgroup_subsys_state *css,
|
|||
cgrp->subsys[ss->subsys_id] = css;
|
||||
}
|
||||
|
||||
static void cgroup_lock_hierarchy(struct cgroupfs_root *root)
|
||||
{
|
||||
/* We need to take each hierarchy_mutex in a consistent order */
|
||||
int i;
|
||||
|
||||
for (i = 0; i < CGROUP_SUBSYS_COUNT; i++) {
|
||||
struct cgroup_subsys *ss = subsys[i];
|
||||
if (ss->root == root)
|
||||
mutex_lock_nested(&ss->hierarchy_mutex, i);
|
||||
}
|
||||
}
|
||||
|
||||
static void cgroup_unlock_hierarchy(struct cgroupfs_root *root)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < CGROUP_SUBSYS_COUNT; i++) {
|
||||
struct cgroup_subsys *ss = subsys[i];
|
||||
if (ss->root == root)
|
||||
mutex_unlock(&ss->hierarchy_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* cgroup_create - create a cgroup
|
||||
* @parent: cgroup that will be parent of the new cgroup
|
||||
|
|
@ -2379,7 +2412,9 @@ static long cgroup_create(struct cgroup *parent, struct dentry *dentry,
|
|||
init_cgroup_css(css, ss, cgrp);
|
||||
}
|
||||
|
||||
cgroup_lock_hierarchy(root);
|
||||
list_add(&cgrp->sibling, &cgrp->parent->children);
|
||||
cgroup_unlock_hierarchy(root);
|
||||
root->number_of_cgroups++;
|
||||
|
||||
err = cgroup_create_dir(cgrp, dentry, mode);
|
||||
|
|
@ -2430,7 +2465,7 @@ static int cgroup_has_css_refs(struct cgroup *cgrp)
|
|||
{
|
||||
/* Check the reference count on each subsystem. Since we
|
||||
* already established that there are no tasks in the
|
||||
* cgroup, if the css refcount is also 0, then there should
|
||||
* cgroup, if the css refcount is also 1, then there should
|
||||
* be no outstanding references, so the subsystem is safe to
|
||||
* destroy. We scan across all subsystems rather than using
|
||||
* the per-hierarchy linked list of mounted subsystems since
|
||||
|
|
@ -2451,19 +2486,67 @@ static int cgroup_has_css_refs(struct cgroup *cgrp)
|
|||
* matter, since it can only happen if the cgroup
|
||||
* has been deleted and hence no longer needs the
|
||||
* release agent to be called anyway. */
|
||||
if (css && atomic_read(&css->refcnt))
|
||||
if (css && (atomic_read(&css->refcnt) > 1))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Atomically mark all (or else none) of the cgroup's CSS objects as
|
||||
* CSS_REMOVED. Return true on success, or false if the cgroup has
|
||||
* busy subsystems. Call with cgroup_mutex held
|
||||
*/
|
||||
|
||||
static int cgroup_clear_css_refs(struct cgroup *cgrp)
|
||||
{
|
||||
struct cgroup_subsys *ss;
|
||||
unsigned long flags;
|
||||
bool failed = false;
|
||||
local_irq_save(flags);
|
||||
for_each_subsys(cgrp->root, ss) {
|
||||
struct cgroup_subsys_state *css = cgrp->subsys[ss->subsys_id];
|
||||
int refcnt;
|
||||
do {
|
||||
/* We can only remove a CSS with a refcnt==1 */
|
||||
refcnt = atomic_read(&css->refcnt);
|
||||
if (refcnt > 1) {
|
||||
failed = true;
|
||||
goto done;
|
||||
}
|
||||
BUG_ON(!refcnt);
|
||||
/*
|
||||
* Drop the refcnt to 0 while we check other
|
||||
* subsystems. This will cause any racing
|
||||
* css_tryget() to spin until we set the
|
||||
* CSS_REMOVED bits or abort
|
||||
*/
|
||||
} while (atomic_cmpxchg(&css->refcnt, refcnt, 0) != refcnt);
|
||||
}
|
||||
done:
|
||||
for_each_subsys(cgrp->root, ss) {
|
||||
struct cgroup_subsys_state *css = cgrp->subsys[ss->subsys_id];
|
||||
if (failed) {
|
||||
/*
|
||||
* Restore old refcnt if we previously managed
|
||||
* to clear it from 1 to 0
|
||||
*/
|
||||
if (!atomic_read(&css->refcnt))
|
||||
atomic_set(&css->refcnt, 1);
|
||||
} else {
|
||||
/* Commit the fact that the CSS is removed */
|
||||
set_bit(CSS_REMOVED, &css->flags);
|
||||
}
|
||||
}
|
||||
local_irq_restore(flags);
|
||||
return !failed;
|
||||
}
|
||||
|
||||
static int cgroup_rmdir(struct inode *unused_dir, struct dentry *dentry)
|
||||
{
|
||||
struct cgroup *cgrp = dentry->d_fsdata;
|
||||
struct dentry *d;
|
||||
struct cgroup *parent;
|
||||
struct super_block *sb;
|
||||
struct cgroupfs_root *root;
|
||||
|
||||
/* the vfs holds both inode->i_mutex already */
|
||||
|
||||
|
|
@ -2486,12 +2569,10 @@ static int cgroup_rmdir(struct inode *unused_dir, struct dentry *dentry)
|
|||
|
||||
mutex_lock(&cgroup_mutex);
|
||||
parent = cgrp->parent;
|
||||
root = cgrp->root;
|
||||
sb = root->sb;
|
||||
|
||||
if (atomic_read(&cgrp->count)
|
||||
|| !list_empty(&cgrp->children)
|
||||
|| cgroup_has_css_refs(cgrp)) {
|
||||
|| !cgroup_clear_css_refs(cgrp)) {
|
||||
mutex_unlock(&cgroup_mutex);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
|
@ -2501,8 +2582,12 @@ static int cgroup_rmdir(struct inode *unused_dir, struct dentry *dentry)
|
|||
if (!list_empty(&cgrp->release_list))
|
||||
list_del(&cgrp->release_list);
|
||||
spin_unlock(&release_list_lock);
|
||||
/* delete my sibling from parent->children */
|
||||
|
||||
cgroup_lock_hierarchy(cgrp->root);
|
||||
/* delete this cgroup from parent->children */
|
||||
list_del(&cgrp->sibling);
|
||||
cgroup_unlock_hierarchy(cgrp->root);
|
||||
|
||||
spin_lock(&cgrp->dentry->d_lock);
|
||||
d = dget(cgrp->dentry);
|
||||
spin_unlock(&d->d_lock);
|
||||
|
|
@ -2524,6 +2609,7 @@ static void __init cgroup_init_subsys(struct cgroup_subsys *ss)
|
|||
printk(KERN_INFO "Initializing cgroup subsys %s\n", ss->name);
|
||||
|
||||
/* Create the top cgroup state for this subsystem */
|
||||
list_add(&ss->sibling, &rootnode.subsys_list);
|
||||
ss->root = &rootnode;
|
||||
css = ss->create(ss, dummytop);
|
||||
/* We don't handle early failures gracefully */
|
||||
|
|
@ -2537,13 +2623,13 @@ static void __init cgroup_init_subsys(struct cgroup_subsys *ss)
|
|||
init_css_set.subsys[ss->subsys_id] = dummytop->subsys[ss->subsys_id];
|
||||
|
||||
need_forkexit_callback |= ss->fork || ss->exit;
|
||||
need_mm_owner_callback |= !!ss->mm_owner_changed;
|
||||
|
||||
/* At system boot, before all subsystems have been
|
||||
* registered, no tasks have been forked, so we don't
|
||||
* need to invoke fork callbacks here. */
|
||||
BUG_ON(!list_empty(&init_task.tasks));
|
||||
|
||||
mutex_init(&ss->hierarchy_mutex);
|
||||
ss->active = 1;
|
||||
}
|
||||
|
||||
|
|
@ -2562,7 +2648,6 @@ int __init cgroup_init_early(void)
|
|||
INIT_HLIST_NODE(&init_css_set.hlist);
|
||||
css_set_count = 1;
|
||||
init_cgroup_root(&rootnode);
|
||||
list_add(&rootnode.root_list, &roots);
|
||||
root_count = 1;
|
||||
init_task.cgroups = &init_css_set;
|
||||
|
||||
|
|
@ -2669,15 +2754,12 @@ static int proc_cgroup_show(struct seq_file *m, void *v)
|
|||
|
||||
mutex_lock(&cgroup_mutex);
|
||||
|
||||
for_each_root(root) {
|
||||
for_each_active_root(root) {
|
||||
struct cgroup_subsys *ss;
|
||||
struct cgroup *cgrp;
|
||||
int subsys_id;
|
||||
int count = 0;
|
||||
|
||||
/* Skip this hierarchy if it has no active subsystems */
|
||||
if (!root->actual_subsys_bits)
|
||||
continue;
|
||||
seq_printf(m, "%lu:", root->subsys_bits);
|
||||
for_each_subsys(root, ss)
|
||||
seq_printf(m, "%s%s", count++ ? "," : "", ss->name);
|
||||
|
|
@ -2787,37 +2869,6 @@ void cgroup_fork_callbacks(struct task_struct *child)
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MM_OWNER
|
||||
/**
|
||||
* cgroup_mm_owner_callbacks - run callbacks when the mm->owner changes
|
||||
* @p: the new owner
|
||||
*
|
||||
* Called on every change to mm->owner. mm_init_owner() does not
|
||||
* invoke this routine, since it assigns the mm->owner the first time
|
||||
* and does not change it.
|
||||
*
|
||||
* The callbacks are invoked with mmap_sem held in read mode.
|
||||
*/
|
||||
void cgroup_mm_owner_callbacks(struct task_struct *old, struct task_struct *new)
|
||||
{
|
||||
struct cgroup *oldcgrp, *newcgrp = NULL;
|
||||
|
||||
if (need_mm_owner_callback) {
|
||||
int i;
|
||||
for (i = 0; i < CGROUP_SUBSYS_COUNT; i++) {
|
||||
struct cgroup_subsys *ss = subsys[i];
|
||||
oldcgrp = task_cgroup(old, ss->subsys_id);
|
||||
if (new)
|
||||
newcgrp = task_cgroup(new, ss->subsys_id);
|
||||
if (oldcgrp == newcgrp)
|
||||
continue;
|
||||
if (ss->mm_owner_changed)
|
||||
ss->mm_owner_changed(ss, oldcgrp, newcgrp, new);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_MM_OWNER */
|
||||
|
||||
/**
|
||||
* cgroup_post_fork - called on a new task after adding it to the task list
|
||||
* @child: the task in question
|
||||
|
|
@ -2831,8 +2882,10 @@ void cgroup_post_fork(struct task_struct *child)
|
|||
{
|
||||
if (use_task_css_set_links) {
|
||||
write_lock(&css_set_lock);
|
||||
task_lock(child);
|
||||
if (list_empty(&child->cg_list))
|
||||
list_add(&child->cg_list, &child->cgroups->tasks);
|
||||
task_unlock(child);
|
||||
write_unlock(&css_set_lock);
|
||||
}
|
||||
}
|
||||
|
|
@ -2938,14 +2991,20 @@ int cgroup_clone(struct task_struct *tsk, struct cgroup_subsys *subsys,
|
|||
mutex_unlock(&cgroup_mutex);
|
||||
return 0;
|
||||
}
|
||||
task_lock(tsk);
|
||||
cg = tsk->cgroups;
|
||||
parent = task_cgroup(tsk, subsys->subsys_id);
|
||||
|
||||
/* Pin the hierarchy */
|
||||
atomic_inc(&parent->root->sb->s_active);
|
||||
if (!atomic_inc_not_zero(&parent->root->sb->s_active)) {
|
||||
/* We race with the final deactivate_super() */
|
||||
mutex_unlock(&cgroup_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Keep the cgroup alive */
|
||||
get_css_set(cg);
|
||||
task_unlock(tsk);
|
||||
mutex_unlock(&cgroup_mutex);
|
||||
|
||||
/* Now do the VFS work to create a cgroup */
|
||||
|
|
@ -2964,7 +3023,7 @@ int cgroup_clone(struct task_struct *tsk, struct cgroup_subsys *subsys,
|
|||
}
|
||||
|
||||
/* Create the cgroup directory, which also creates the cgroup */
|
||||
ret = vfs_mkdir(inode, dentry, S_IFDIR | 0755);
|
||||
ret = vfs_mkdir(inode, dentry, 0755);
|
||||
child = __d_cgrp(dentry);
|
||||
dput(dentry);
|
||||
if (ret) {
|
||||
|
|
@ -2974,13 +3033,6 @@ int cgroup_clone(struct task_struct *tsk, struct cgroup_subsys *subsys,
|
|||
goto out_release;
|
||||
}
|
||||
|
||||
if (!child) {
|
||||
printk(KERN_INFO
|
||||
"Couldn't find new cgroup %s\n", nodename);
|
||||
ret = -ENOMEM;
|
||||
goto out_release;
|
||||
}
|
||||
|
||||
/* The cgroup now exists. Retake cgroup_mutex and check
|
||||
* that we're still in the same state that we thought we
|
||||
* were. */
|
||||
|
|
@ -3076,7 +3128,8 @@ void __css_put(struct cgroup_subsys_state *css)
|
|||
{
|
||||
struct cgroup *cgrp = css->cgroup;
|
||||
rcu_read_lock();
|
||||
if (atomic_dec_and_test(&css->refcnt) && notify_on_release(cgrp)) {
|
||||
if ((atomic_dec_return(&css->refcnt) == 1) &&
|
||||
notify_on_release(cgrp)) {
|
||||
set_bit(CGRP_RELEASABLE, &cgrp->flags);
|
||||
check_for_release(cgrp);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
#include <linux/migrate.h>
|
||||
#include <linux/posix-timers.h>
|
||||
#include <linux/times.h>
|
||||
#include <linux/ptrace.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
|
|
@ -229,6 +230,7 @@ asmlinkage long compat_sys_times(struct compat_tms __user *tbuf)
|
|||
if (copy_to_user(tbuf, &tmp, sizeof(tmp)))
|
||||
return -EFAULT;
|
||||
}
|
||||
force_successful_syscall_return();
|
||||
return compat_jiffies_to_clock_t(jiffies);
|
||||
}
|
||||
|
||||
|
|
@ -454,16 +456,16 @@ asmlinkage long compat_sys_waitid(int which, compat_pid_t pid,
|
|||
}
|
||||
|
||||
static int compat_get_user_cpu_mask(compat_ulong_t __user *user_mask_ptr,
|
||||
unsigned len, cpumask_t *new_mask)
|
||||
unsigned len, struct cpumask *new_mask)
|
||||
{
|
||||
unsigned long *k;
|
||||
|
||||
if (len < sizeof(cpumask_t))
|
||||
memset(new_mask, 0, sizeof(cpumask_t));
|
||||
else if (len > sizeof(cpumask_t))
|
||||
len = sizeof(cpumask_t);
|
||||
if (len < cpumask_size())
|
||||
memset(new_mask, 0, cpumask_size());
|
||||
else if (len > cpumask_size())
|
||||
len = cpumask_size();
|
||||
|
||||
k = cpus_addr(*new_mask);
|
||||
k = cpumask_bits(new_mask);
|
||||
return compat_get_bitmap(k, user_mask_ptr, len * 8);
|
||||
}
|
||||
|
||||
|
|
@ -471,40 +473,51 @@ asmlinkage long compat_sys_sched_setaffinity(compat_pid_t pid,
|
|||
unsigned int len,
|
||||
compat_ulong_t __user *user_mask_ptr)
|
||||
{
|
||||
cpumask_t new_mask;
|
||||
cpumask_var_t new_mask;
|
||||
int retval;
|
||||
|
||||
retval = compat_get_user_cpu_mask(user_mask_ptr, len, &new_mask);
|
||||
if (retval)
|
||||
return retval;
|
||||
if (!alloc_cpumask_var(&new_mask, GFP_KERNEL))
|
||||
return -ENOMEM;
|
||||
|
||||
return sched_setaffinity(pid, &new_mask);
|
||||
retval = compat_get_user_cpu_mask(user_mask_ptr, len, new_mask);
|
||||
if (retval)
|
||||
goto out;
|
||||
|
||||
retval = sched_setaffinity(pid, new_mask);
|
||||
out:
|
||||
free_cpumask_var(new_mask);
|
||||
return retval;
|
||||
}
|
||||
|
||||
asmlinkage long compat_sys_sched_getaffinity(compat_pid_t pid, unsigned int len,
|
||||
compat_ulong_t __user *user_mask_ptr)
|
||||
{
|
||||
int ret;
|
||||
cpumask_t mask;
|
||||
cpumask_var_t mask;
|
||||
unsigned long *k;
|
||||
unsigned int min_length = sizeof(cpumask_t);
|
||||
unsigned int min_length = cpumask_size();
|
||||
|
||||
if (NR_CPUS <= BITS_PER_COMPAT_LONG)
|
||||
if (nr_cpu_ids <= BITS_PER_COMPAT_LONG)
|
||||
min_length = sizeof(compat_ulong_t);
|
||||
|
||||
if (len < min_length)
|
||||
return -EINVAL;
|
||||
|
||||
ret = sched_getaffinity(pid, &mask);
|
||||
if (!alloc_cpumask_var(&mask, GFP_KERNEL))
|
||||
return -ENOMEM;
|
||||
|
||||
ret = sched_getaffinity(pid, mask);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto out;
|
||||
|
||||
k = cpus_addr(mask);
|
||||
k = cpumask_bits(mask);
|
||||
ret = compat_put_bitmap(user_mask_ptr, k, min_length * 8);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (ret == 0)
|
||||
ret = min_length;
|
||||
|
||||
return min_length;
|
||||
out:
|
||||
free_cpumask_var(mask);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int get_compat_itimerspec(struct itimerspec *dst,
|
||||
|
|
@ -883,8 +896,9 @@ asmlinkage long compat_sys_time(compat_time_t __user * tloc)
|
|||
|
||||
if (tloc) {
|
||||
if (put_user(i,tloc))
|
||||
i = -EFAULT;
|
||||
return -EFAULT;
|
||||
}
|
||||
force_successful_syscall_return();
|
||||
return i;
|
||||
}
|
||||
|
||||
|
|
|
|||
157
kernel/cpu.c
157
kernel/cpu.c
|
|
@ -15,29 +15,8 @@
|
|||
#include <linux/stop_machine.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
/*
|
||||
* Represents all cpu's present in the system
|
||||
* In systems capable of hotplug, this map could dynamically grow
|
||||
* as new cpu's are detected in the system via any platform specific
|
||||
* method, such as ACPI for e.g.
|
||||
*/
|
||||
cpumask_t cpu_present_map __read_mostly;
|
||||
EXPORT_SYMBOL(cpu_present_map);
|
||||
|
||||
#ifndef CONFIG_SMP
|
||||
|
||||
/*
|
||||
* Represents all cpu's that are currently online.
|
||||
*/
|
||||
cpumask_t cpu_online_map __read_mostly = CPU_MASK_ALL;
|
||||
EXPORT_SYMBOL(cpu_online_map);
|
||||
|
||||
cpumask_t cpu_possible_map __read_mostly = CPU_MASK_ALL;
|
||||
EXPORT_SYMBOL(cpu_possible_map);
|
||||
|
||||
#else /* CONFIG_SMP */
|
||||
|
||||
/* Serializes the updates to cpu_online_map, cpu_present_map */
|
||||
#ifdef CONFIG_SMP
|
||||
/* Serializes the updates to cpu_online_mask, cpu_present_mask */
|
||||
static DEFINE_MUTEX(cpu_add_remove_lock);
|
||||
|
||||
static __cpuinitdata RAW_NOTIFIER_HEAD(cpu_chain);
|
||||
|
|
@ -64,8 +43,6 @@ void __init cpu_hotplug_init(void)
|
|||
cpu_hotplug.refcount = 0;
|
||||
}
|
||||
|
||||
cpumask_t cpu_active_map;
|
||||
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
|
||||
void get_online_cpus(void)
|
||||
|
|
@ -96,7 +73,7 @@ EXPORT_SYMBOL_GPL(put_online_cpus);
|
|||
|
||||
/*
|
||||
* The following two API's must be used when attempting
|
||||
* to serialize the updates to cpu_online_map, cpu_present_map.
|
||||
* to serialize the updates to cpu_online_mask, cpu_present_mask.
|
||||
*/
|
||||
void cpu_maps_update_begin(void)
|
||||
{
|
||||
|
|
@ -217,7 +194,7 @@ static int __ref take_cpu_down(void *_param)
|
|||
static int __ref _cpu_down(unsigned int cpu, int tasks_frozen)
|
||||
{
|
||||
int err, nr_calls = 0;
|
||||
cpumask_t old_allowed, tmp;
|
||||
cpumask_var_t old_allowed;
|
||||
void *hcpu = (void *)(long)cpu;
|
||||
unsigned long mod = tasks_frozen ? CPU_TASKS_FROZEN : 0;
|
||||
struct take_cpu_down_param tcd_param = {
|
||||
|
|
@ -231,6 +208,9 @@ static int __ref _cpu_down(unsigned int cpu, int tasks_frozen)
|
|||
if (!cpu_online(cpu))
|
||||
return -EINVAL;
|
||||
|
||||
if (!alloc_cpumask_var(&old_allowed, GFP_KERNEL))
|
||||
return -ENOMEM;
|
||||
|
||||
cpu_hotplug_begin();
|
||||
err = __raw_notifier_call_chain(&cpu_chain, CPU_DOWN_PREPARE | mod,
|
||||
hcpu, -1, &nr_calls);
|
||||
|
|
@ -245,13 +225,11 @@ static int __ref _cpu_down(unsigned int cpu, int tasks_frozen)
|
|||
}
|
||||
|
||||
/* Ensure that we are not runnable on dying cpu */
|
||||
old_allowed = current->cpus_allowed;
|
||||
cpus_setall(tmp);
|
||||
cpu_clear(cpu, tmp);
|
||||
set_cpus_allowed_ptr(current, &tmp);
|
||||
tmp = cpumask_of_cpu(cpu);
|
||||
cpumask_copy(old_allowed, ¤t->cpus_allowed);
|
||||
set_cpus_allowed_ptr(current,
|
||||
cpumask_of(cpumask_any_but(cpu_online_mask, cpu)));
|
||||
|
||||
err = __stop_machine(take_cpu_down, &tcd_param, &tmp);
|
||||
err = __stop_machine(take_cpu_down, &tcd_param, cpumask_of(cpu));
|
||||
if (err) {
|
||||
/* CPU didn't die: tell everyone. Can't complain. */
|
||||
if (raw_notifier_call_chain(&cpu_chain, CPU_DOWN_FAILED | mod,
|
||||
|
|
@ -277,7 +255,7 @@ static int __ref _cpu_down(unsigned int cpu, int tasks_frozen)
|
|||
check_for_tasks(cpu);
|
||||
|
||||
out_allowed:
|
||||
set_cpus_allowed_ptr(current, &old_allowed);
|
||||
set_cpus_allowed_ptr(current, old_allowed);
|
||||
out_release:
|
||||
cpu_hotplug_done();
|
||||
if (!err) {
|
||||
|
|
@ -285,13 +263,17 @@ out_release:
|
|||
hcpu) == NOTIFY_BAD)
|
||||
BUG();
|
||||
}
|
||||
free_cpumask_var(old_allowed);
|
||||
return err;
|
||||
}
|
||||
|
||||
int __ref cpu_down(unsigned int cpu)
|
||||
{
|
||||
int err = 0;
|
||||
int err;
|
||||
|
||||
err = stop_machine_create();
|
||||
if (err)
|
||||
return err;
|
||||
cpu_maps_update_begin();
|
||||
|
||||
if (cpu_hotplug_disabled) {
|
||||
|
|
@ -303,7 +285,7 @@ int __ref cpu_down(unsigned int cpu)
|
|||
|
||||
/*
|
||||
* Make sure the all cpus did the reschedule and are not
|
||||
* using stale version of the cpu_active_map.
|
||||
* using stale version of the cpu_active_mask.
|
||||
* This is not strictly necessary becuase stop_machine()
|
||||
* that we run down the line already provides the required
|
||||
* synchronization. But it's really a side effect and we do not
|
||||
|
|
@ -318,6 +300,7 @@ int __ref cpu_down(unsigned int cpu)
|
|||
|
||||
out:
|
||||
cpu_maps_update_done();
|
||||
stop_machine_destroy();
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(cpu_down);
|
||||
|
|
@ -367,7 +350,7 @@ out_notify:
|
|||
int __cpuinit cpu_up(unsigned int cpu)
|
||||
{
|
||||
int err = 0;
|
||||
if (!cpu_isset(cpu, cpu_possible_map)) {
|
||||
if (!cpu_possible(cpu)) {
|
||||
printk(KERN_ERR "can't online cpu %d because it is not "
|
||||
"configured as may-hotadd at boot time\n", cpu);
|
||||
#if defined(CONFIG_IA64) || defined(CONFIG_X86_64)
|
||||
|
|
@ -392,25 +375,28 @@ out:
|
|||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP_SMP
|
||||
static cpumask_t frozen_cpus;
|
||||
static cpumask_var_t frozen_cpus;
|
||||
|
||||
int disable_nonboot_cpus(void)
|
||||
{
|
||||
int cpu, first_cpu, error = 0;
|
||||
int cpu, first_cpu, error;
|
||||
|
||||
error = stop_machine_create();
|
||||
if (error)
|
||||
return error;
|
||||
cpu_maps_update_begin();
|
||||
first_cpu = first_cpu(cpu_online_map);
|
||||
first_cpu = cpumask_first(cpu_online_mask);
|
||||
/* We take down all of the non-boot CPUs in one shot to avoid races
|
||||
* with the userspace trying to use the CPU hotplug at the same time
|
||||
*/
|
||||
cpus_clear(frozen_cpus);
|
||||
cpumask_clear(frozen_cpus);
|
||||
printk("Disabling non-boot CPUs ...\n");
|
||||
for_each_online_cpu(cpu) {
|
||||
if (cpu == first_cpu)
|
||||
continue;
|
||||
error = _cpu_down(cpu, 1);
|
||||
if (!error) {
|
||||
cpu_set(cpu, frozen_cpus);
|
||||
cpumask_set_cpu(cpu, frozen_cpus);
|
||||
printk("CPU%d is down\n", cpu);
|
||||
} else {
|
||||
printk(KERN_ERR "Error taking CPU%d down: %d\n",
|
||||
|
|
@ -426,6 +412,7 @@ int disable_nonboot_cpus(void)
|
|||
printk(KERN_ERR "Non-boot CPUs are not disabled\n");
|
||||
}
|
||||
cpu_maps_update_done();
|
||||
stop_machine_destroy();
|
||||
return error;
|
||||
}
|
||||
|
||||
|
|
@ -436,11 +423,11 @@ void __ref enable_nonboot_cpus(void)
|
|||
/* Allow everyone to use the CPU hotplug again */
|
||||
cpu_maps_update_begin();
|
||||
cpu_hotplug_disabled = 0;
|
||||
if (cpus_empty(frozen_cpus))
|
||||
if (cpumask_empty(frozen_cpus))
|
||||
goto out;
|
||||
|
||||
printk("Enabling non-boot CPUs ...\n");
|
||||
for_each_cpu_mask_nr(cpu, frozen_cpus) {
|
||||
for_each_cpu(cpu, frozen_cpus) {
|
||||
error = _cpu_up(cpu, 1);
|
||||
if (!error) {
|
||||
printk("CPU%d is up\n", cpu);
|
||||
|
|
@ -448,10 +435,18 @@ void __ref enable_nonboot_cpus(void)
|
|||
}
|
||||
printk(KERN_WARNING "Error taking CPU%d up: %d\n", cpu, error);
|
||||
}
|
||||
cpus_clear(frozen_cpus);
|
||||
cpumask_clear(frozen_cpus);
|
||||
out:
|
||||
cpu_maps_update_done();
|
||||
}
|
||||
|
||||
static int alloc_frozen_cpus(void)
|
||||
{
|
||||
if (!alloc_cpumask_var(&frozen_cpus, GFP_KERNEL|__GFP_ZERO))
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
core_initcall(alloc_frozen_cpus);
|
||||
#endif /* CONFIG_PM_SLEEP_SMP */
|
||||
|
||||
/**
|
||||
|
|
@ -467,7 +462,7 @@ void __cpuinit notify_cpu_starting(unsigned int cpu)
|
|||
unsigned long val = CPU_STARTING;
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP_SMP
|
||||
if (cpu_isset(cpu, frozen_cpus))
|
||||
if (frozen_cpus != NULL && cpumask_test_cpu(cpu, frozen_cpus))
|
||||
val = CPU_STARTING_FROZEN;
|
||||
#endif /* CONFIG_PM_SLEEP_SMP */
|
||||
raw_notifier_call_chain(&cpu_chain, val, (void *)(long)cpu);
|
||||
|
|
@ -479,7 +474,7 @@ void __cpuinit notify_cpu_starting(unsigned int cpu)
|
|||
* cpu_bit_bitmap[] is a special, "compressed" data structure that
|
||||
* represents all NR_CPUS bits binary values of 1<<nr.
|
||||
*
|
||||
* It is used by cpumask_of_cpu() to get a constant address to a CPU
|
||||
* It is used by cpumask_of() to get a constant address to a CPU
|
||||
* mask value that has a single bit set only.
|
||||
*/
|
||||
|
||||
|
|
@ -502,3 +497,71 @@ EXPORT_SYMBOL_GPL(cpu_bit_bitmap);
|
|||
|
||||
const DECLARE_BITMAP(cpu_all_bits, NR_CPUS) = CPU_BITS_ALL;
|
||||
EXPORT_SYMBOL(cpu_all_bits);
|
||||
|
||||
#ifdef CONFIG_INIT_ALL_POSSIBLE
|
||||
static DECLARE_BITMAP(cpu_possible_bits, CONFIG_NR_CPUS) __read_mostly
|
||||
= CPU_BITS_ALL;
|
||||
#else
|
||||
static DECLARE_BITMAP(cpu_possible_bits, CONFIG_NR_CPUS) __read_mostly;
|
||||
#endif
|
||||
const struct cpumask *const cpu_possible_mask = to_cpumask(cpu_possible_bits);
|
||||
EXPORT_SYMBOL(cpu_possible_mask);
|
||||
|
||||
static DECLARE_BITMAP(cpu_online_bits, CONFIG_NR_CPUS) __read_mostly;
|
||||
const struct cpumask *const cpu_online_mask = to_cpumask(cpu_online_bits);
|
||||
EXPORT_SYMBOL(cpu_online_mask);
|
||||
|
||||
static DECLARE_BITMAP(cpu_present_bits, CONFIG_NR_CPUS) __read_mostly;
|
||||
const struct cpumask *const cpu_present_mask = to_cpumask(cpu_present_bits);
|
||||
EXPORT_SYMBOL(cpu_present_mask);
|
||||
|
||||
static DECLARE_BITMAP(cpu_active_bits, CONFIG_NR_CPUS) __read_mostly;
|
||||
const struct cpumask *const cpu_active_mask = to_cpumask(cpu_active_bits);
|
||||
EXPORT_SYMBOL(cpu_active_mask);
|
||||
|
||||
void set_cpu_possible(unsigned int cpu, bool possible)
|
||||
{
|
||||
if (possible)
|
||||
cpumask_set_cpu(cpu, to_cpumask(cpu_possible_bits));
|
||||
else
|
||||
cpumask_clear_cpu(cpu, to_cpumask(cpu_possible_bits));
|
||||
}
|
||||
|
||||
void set_cpu_present(unsigned int cpu, bool present)
|
||||
{
|
||||
if (present)
|
||||
cpumask_set_cpu(cpu, to_cpumask(cpu_present_bits));
|
||||
else
|
||||
cpumask_clear_cpu(cpu, to_cpumask(cpu_present_bits));
|
||||
}
|
||||
|
||||
void set_cpu_online(unsigned int cpu, bool online)
|
||||
{
|
||||
if (online)
|
||||
cpumask_set_cpu(cpu, to_cpumask(cpu_online_bits));
|
||||
else
|
||||
cpumask_clear_cpu(cpu, to_cpumask(cpu_online_bits));
|
||||
}
|
||||
|
||||
void set_cpu_active(unsigned int cpu, bool active)
|
||||
{
|
||||
if (active)
|
||||
cpumask_set_cpu(cpu, to_cpumask(cpu_active_bits));
|
||||
else
|
||||
cpumask_clear_cpu(cpu, to_cpumask(cpu_active_bits));
|
||||
}
|
||||
|
||||
void init_cpu_present(const struct cpumask *src)
|
||||
{
|
||||
cpumask_copy(to_cpumask(cpu_present_bits), src);
|
||||
}
|
||||
|
||||
void init_cpu_possible(const struct cpumask *src)
|
||||
{
|
||||
cpumask_copy(to_cpumask(cpu_possible_bits), src);
|
||||
}
|
||||
|
||||
void init_cpu_online(const struct cpumask *src)
|
||||
{
|
||||
cpumask_copy(to_cpumask(cpu_online_bits), src);
|
||||
}
|
||||
|
|
|
|||
291
kernel/cpuset.c
291
kernel/cpuset.c
|
|
@ -84,7 +84,7 @@ struct cpuset {
|
|||
struct cgroup_subsys_state css;
|
||||
|
||||
unsigned long flags; /* "unsigned long" so bitops work */
|
||||
cpumask_t cpus_allowed; /* CPUs allowed to tasks in cpuset */
|
||||
cpumask_var_t cpus_allowed; /* CPUs allowed to tasks in cpuset */
|
||||
nodemask_t mems_allowed; /* Memory Nodes allowed to tasks */
|
||||
|
||||
struct cpuset *parent; /* my parent */
|
||||
|
|
@ -195,8 +195,6 @@ static int cpuset_mems_generation;
|
|||
|
||||
static struct cpuset top_cpuset = {
|
||||
.flags = ((1 << CS_CPU_EXCLUSIVE) | (1 << CS_MEM_EXCLUSIVE)),
|
||||
.cpus_allowed = CPU_MASK_ALL,
|
||||
.mems_allowed = NODE_MASK_ALL,
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
@ -239,6 +237,17 @@ static struct cpuset top_cpuset = {
|
|||
|
||||
static DEFINE_MUTEX(callback_mutex);
|
||||
|
||||
/*
|
||||
* cpuset_buffer_lock protects both the cpuset_name and cpuset_nodelist
|
||||
* buffers. They are statically allocated to prevent using excess stack
|
||||
* when calling cpuset_print_task_mems_allowed().
|
||||
*/
|
||||
#define CPUSET_NAME_LEN (128)
|
||||
#define CPUSET_NODELIST_LEN (256)
|
||||
static char cpuset_name[CPUSET_NAME_LEN];
|
||||
static char cpuset_nodelist[CPUSET_NODELIST_LEN];
|
||||
static DEFINE_SPINLOCK(cpuset_buffer_lock);
|
||||
|
||||
/*
|
||||
* This is ugly, but preserves the userspace API for existing cpuset
|
||||
* users. If someone tries to mount the "cpuset" filesystem, we
|
||||
|
|
@ -267,7 +276,7 @@ static struct file_system_type cpuset_fs_type = {
|
|||
};
|
||||
|
||||
/*
|
||||
* Return in *pmask the portion of a cpusets's cpus_allowed that
|
||||
* 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,
|
||||
|
|
@ -280,15 +289,16 @@ static struct file_system_type cpuset_fs_type = {
|
|||
* Call with callback_mutex held.
|
||||
*/
|
||||
|
||||
static void guarantee_online_cpus(const struct cpuset *cs, cpumask_t *pmask)
|
||||
static void guarantee_online_cpus(const struct cpuset *cs,
|
||||
struct cpumask *pmask)
|
||||
{
|
||||
while (cs && !cpus_intersects(cs->cpus_allowed, cpu_online_map))
|
||||
while (cs && !cpumask_intersects(cs->cpus_allowed, cpu_online_mask))
|
||||
cs = cs->parent;
|
||||
if (cs)
|
||||
cpus_and(*pmask, cs->cpus_allowed, cpu_online_map);
|
||||
cpumask_and(pmask, cs->cpus_allowed, cpu_online_mask);
|
||||
else
|
||||
*pmask = cpu_online_map;
|
||||
BUG_ON(!cpus_intersects(*pmask, cpu_online_map));
|
||||
cpumask_copy(pmask, cpu_online_mask);
|
||||
BUG_ON(!cpumask_intersects(pmask, cpu_online_mask));
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -364,14 +374,9 @@ void cpuset_update_task_memory_state(void)
|
|||
struct task_struct *tsk = current;
|
||||
struct cpuset *cs;
|
||||
|
||||
if (task_cs(tsk) == &top_cpuset) {
|
||||
/* Don't need rcu for top_cpuset. It's never freed. */
|
||||
my_cpusets_mem_gen = top_cpuset.mems_generation;
|
||||
} else {
|
||||
rcu_read_lock();
|
||||
my_cpusets_mem_gen = task_cs(tsk)->mems_generation;
|
||||
rcu_read_unlock();
|
||||
}
|
||||
rcu_read_lock();
|
||||
my_cpusets_mem_gen = task_cs(tsk)->mems_generation;
|
||||
rcu_read_unlock();
|
||||
|
||||
if (my_cpusets_mem_gen != tsk->cpuset_mems_generation) {
|
||||
mutex_lock(&callback_mutex);
|
||||
|
|
@ -403,12 +408,43 @@ void cpuset_update_task_memory_state(void)
|
|||
|
||||
static int is_cpuset_subset(const struct cpuset *p, const struct cpuset *q)
|
||||
{
|
||||
return cpus_subset(p->cpus_allowed, q->cpus_allowed) &&
|
||||
return cpumask_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);
|
||||
}
|
||||
|
||||
/**
|
||||
* alloc_trial_cpuset - allocate a trial cpuset
|
||||
* @cs: the cpuset that the trial cpuset duplicates
|
||||
*/
|
||||
static struct cpuset *alloc_trial_cpuset(const struct cpuset *cs)
|
||||
{
|
||||
struct cpuset *trial;
|
||||
|
||||
trial = kmemdup(cs, sizeof(*cs), GFP_KERNEL);
|
||||
if (!trial)
|
||||
return NULL;
|
||||
|
||||
if (!alloc_cpumask_var(&trial->cpus_allowed, GFP_KERNEL)) {
|
||||
kfree(trial);
|
||||
return NULL;
|
||||
}
|
||||
cpumask_copy(trial->cpus_allowed, cs->cpus_allowed);
|
||||
|
||||
return trial;
|
||||
}
|
||||
|
||||
/**
|
||||
* free_trial_cpuset - free the trial cpuset
|
||||
* @trial: the trial cpuset to be freed
|
||||
*/
|
||||
static void free_trial_cpuset(struct cpuset *trial)
|
||||
{
|
||||
free_cpumask_var(trial->cpus_allowed);
|
||||
kfree(trial);
|
||||
}
|
||||
|
||||
/*
|
||||
* validate_change() - Used to validate that any proposed cpuset change
|
||||
* follows the structural rules for cpusets.
|
||||
|
|
@ -458,7 +494,7 @@ static int validate_change(const struct cpuset *cur, const struct cpuset *trial)
|
|||
c = cgroup_cs(cont);
|
||||
if ((is_cpu_exclusive(trial) || is_cpu_exclusive(c)) &&
|
||||
c != cur &&
|
||||
cpus_intersects(trial->cpus_allowed, c->cpus_allowed))
|
||||
cpumask_intersects(trial->cpus_allowed, c->cpus_allowed))
|
||||
return -EINVAL;
|
||||
if ((is_mem_exclusive(trial) || is_mem_exclusive(c)) &&
|
||||
c != cur &&
|
||||
|
|
@ -468,7 +504,7 @@ static int validate_change(const struct cpuset *cur, const struct cpuset *trial)
|
|||
|
||||
/* 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) ||
|
||||
if (cpumask_empty(trial->cpus_allowed) ||
|
||||
nodes_empty(trial->mems_allowed)) {
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
|
@ -483,7 +519,7 @@ static int validate_change(const struct cpuset *cur, const struct cpuset *trial)
|
|||
*/
|
||||
static int cpusets_overlap(struct cpuset *a, struct cpuset *b)
|
||||
{
|
||||
return cpus_intersects(a->cpus_allowed, b->cpus_allowed);
|
||||
return cpumask_intersects(a->cpus_allowed, b->cpus_allowed);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -508,7 +544,7 @@ update_domain_attr_tree(struct sched_domain_attr *dattr, struct cpuset *c)
|
|||
cp = list_first_entry(&q, struct cpuset, stack_list);
|
||||
list_del(q.next);
|
||||
|
||||
if (cpus_empty(cp->cpus_allowed))
|
||||
if (cpumask_empty(cp->cpus_allowed))
|
||||
continue;
|
||||
|
||||
if (is_sched_load_balance(cp))
|
||||
|
|
@ -532,7 +568,7 @@ update_domain_attr_tree(struct sched_domain_attr *dattr, struct cpuset *c)
|
|||
* load balancing domains (sched domains) as specified by that partial
|
||||
* partition.
|
||||
*
|
||||
* See "What is sched_load_balance" in Documentation/cpusets.txt
|
||||
* See "What is sched_load_balance" in Documentation/cgroups/cpusets.txt
|
||||
* for a background explanation of this.
|
||||
*
|
||||
* Does not return errors, on the theory that the callers of this
|
||||
|
|
@ -575,7 +611,8 @@ update_domain_attr_tree(struct sched_domain_attr *dattr, struct cpuset *c)
|
|||
* element of the partition (one sched domain) to be passed to
|
||||
* partition_sched_domains().
|
||||
*/
|
||||
static int generate_sched_domains(cpumask_t **domains,
|
||||
/* FIXME: see the FIXME in partition_sched_domains() */
|
||||
static int generate_sched_domains(struct cpumask **domains,
|
||||
struct sched_domain_attr **attributes)
|
||||
{
|
||||
LIST_HEAD(q); /* queue of cpusets to be scanned */
|
||||
|
|
@ -583,10 +620,10 @@ static int generate_sched_domains(cpumask_t **domains,
|
|||
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 */
|
||||
struct cpumask *doms; /* resulting partition; i.e. sched domains */
|
||||
struct sched_domain_attr *dattr; /* attributes for custom domains */
|
||||
int ndoms = 0; /* number of sched domains in result */
|
||||
int nslot; /* next empty doms[] cpumask_t slot */
|
||||
int nslot; /* next empty doms[] struct cpumask slot */
|
||||
|
||||
doms = NULL;
|
||||
dattr = NULL;
|
||||
|
|
@ -594,7 +631,7 @@ static int generate_sched_domains(cpumask_t **domains,
|
|||
|
||||
/* Special case for the 99% of systems with one, full, sched domain */
|
||||
if (is_sched_load_balance(&top_cpuset)) {
|
||||
doms = kmalloc(sizeof(cpumask_t), GFP_KERNEL);
|
||||
doms = kmalloc(cpumask_size(), GFP_KERNEL);
|
||||
if (!doms)
|
||||
goto done;
|
||||
|
||||
|
|
@ -603,7 +640,7 @@ static int generate_sched_domains(cpumask_t **domains,
|
|||
*dattr = SD_ATTR_INIT;
|
||||
update_domain_attr_tree(dattr, &top_cpuset);
|
||||
}
|
||||
*doms = top_cpuset.cpus_allowed;
|
||||
cpumask_copy(doms, top_cpuset.cpus_allowed);
|
||||
|
||||
ndoms = 1;
|
||||
goto done;
|
||||
|
|
@ -622,7 +659,7 @@ static int generate_sched_domains(cpumask_t **domains,
|
|||
cp = list_first_entry(&q, struct cpuset, stack_list);
|
||||
list_del(q.next);
|
||||
|
||||
if (cpus_empty(cp->cpus_allowed))
|
||||
if (cpumask_empty(cp->cpus_allowed))
|
||||
continue;
|
||||
|
||||
/*
|
||||
|
|
@ -673,7 +710,7 @@ restart:
|
|||
* Now we know how many domains to create.
|
||||
* Convert <csn, csa> to <ndoms, doms> and populate cpu masks.
|
||||
*/
|
||||
doms = kmalloc(ndoms * sizeof(cpumask_t), GFP_KERNEL);
|
||||
doms = kmalloc(ndoms * cpumask_size(), GFP_KERNEL);
|
||||
if (!doms)
|
||||
goto done;
|
||||
|
||||
|
|
@ -685,7 +722,7 @@ restart:
|
|||
|
||||
for (nslot = 0, i = 0; i < csn; i++) {
|
||||
struct cpuset *a = csa[i];
|
||||
cpumask_t *dp;
|
||||
struct cpumask *dp;
|
||||
int apn = a->pn;
|
||||
|
||||
if (apn < 0) {
|
||||
|
|
@ -708,14 +745,14 @@ restart:
|
|||
continue;
|
||||
}
|
||||
|
||||
cpus_clear(*dp);
|
||||
cpumask_clear(dp);
|
||||
if (dattr)
|
||||
*(dattr + nslot) = SD_ATTR_INIT;
|
||||
for (j = i; j < csn; j++) {
|
||||
struct cpuset *b = csa[j];
|
||||
|
||||
if (apn == b->pn) {
|
||||
cpus_or(*dp, *dp, b->cpus_allowed);
|
||||
cpumask_or(dp, dp, b->cpus_allowed);
|
||||
if (dattr)
|
||||
update_domain_attr_tree(dattr + nslot, b);
|
||||
|
||||
|
|
@ -755,7 +792,7 @@ done:
|
|||
static void do_rebuild_sched_domains(struct work_struct *unused)
|
||||
{
|
||||
struct sched_domain_attr *attr;
|
||||
cpumask_t *doms;
|
||||
struct cpumask *doms;
|
||||
int ndoms;
|
||||
|
||||
get_online_cpus();
|
||||
|
|
@ -824,7 +861,7 @@ void rebuild_sched_domains(void)
|
|||
static int cpuset_test_cpumask(struct task_struct *tsk,
|
||||
struct cgroup_scanner *scan)
|
||||
{
|
||||
return !cpus_equal(tsk->cpus_allowed,
|
||||
return !cpumask_equal(&tsk->cpus_allowed,
|
||||
(cgroup_cs(scan->cg))->cpus_allowed);
|
||||
}
|
||||
|
||||
|
|
@ -842,7 +879,7 @@ static int cpuset_test_cpumask(struct task_struct *tsk,
|
|||
static void cpuset_change_cpumask(struct task_struct *tsk,
|
||||
struct cgroup_scanner *scan)
|
||||
{
|
||||
set_cpus_allowed_ptr(tsk, &((cgroup_cs(scan->cg))->cpus_allowed));
|
||||
set_cpus_allowed_ptr(tsk, ((cgroup_cs(scan->cg))->cpus_allowed));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -874,10 +911,10 @@ static void update_tasks_cpumask(struct cpuset *cs, struct ptr_heap *heap)
|
|||
* @cs: the cpuset to consider
|
||||
* @buf: buffer of cpu numbers written to this cpuset
|
||||
*/
|
||||
static int update_cpumask(struct cpuset *cs, const char *buf)
|
||||
static int update_cpumask(struct cpuset *cs, struct cpuset *trialcs,
|
||||
const char *buf)
|
||||
{
|
||||
struct ptr_heap heap;
|
||||
struct cpuset trialcs;
|
||||
int retval;
|
||||
int is_load_balanced;
|
||||
|
||||
|
|
@ -885,8 +922,6 @@ static int update_cpumask(struct cpuset *cs, const char *buf)
|
|||
if (cs == &top_cpuset)
|
||||
return -EACCES;
|
||||
|
||||
trialcs = *cs;
|
||||
|
||||
/*
|
||||
* An empty cpus_allowed is ok only if the cpuset has no tasks.
|
||||
* Since cpulist_parse() fails on an empty mask, we special case
|
||||
|
|
@ -894,31 +929,31 @@ static int update_cpumask(struct cpuset *cs, const char *buf)
|
|||
* with tasks have cpus.
|
||||
*/
|
||||
if (!*buf) {
|
||||
cpus_clear(trialcs.cpus_allowed);
|
||||
cpumask_clear(trialcs->cpus_allowed);
|
||||
} else {
|
||||
retval = cpulist_parse(buf, trialcs.cpus_allowed);
|
||||
retval = cpulist_parse(buf, trialcs->cpus_allowed);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
|
||||
if (!cpus_subset(trialcs.cpus_allowed, cpu_online_map))
|
||||
if (!cpumask_subset(trialcs->cpus_allowed, cpu_online_mask))
|
||||
return -EINVAL;
|
||||
}
|
||||
retval = validate_change(cs, &trialcs);
|
||||
retval = validate_change(cs, trialcs);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
|
||||
/* Nothing to do if the cpus didn't change */
|
||||
if (cpus_equal(cs->cpus_allowed, trialcs.cpus_allowed))
|
||||
if (cpumask_equal(cs->cpus_allowed, trialcs->cpus_allowed))
|
||||
return 0;
|
||||
|
||||
retval = heap_init(&heap, PAGE_SIZE, GFP_KERNEL, NULL);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
is_load_balanced = is_sched_load_balance(&trialcs);
|
||||
is_load_balanced = is_sched_load_balance(trialcs);
|
||||
|
||||
mutex_lock(&callback_mutex);
|
||||
cs->cpus_allowed = trialcs.cpus_allowed;
|
||||
cpumask_copy(cs->cpus_allowed, trialcs->cpus_allowed);
|
||||
mutex_unlock(&callback_mutex);
|
||||
|
||||
/*
|
||||
|
|
@ -1006,7 +1041,7 @@ static int update_tasks_nodemask(struct cpuset *cs, const nodemask_t *oldmem)
|
|||
cpuset_being_rebound = cs; /* causes mpol_dup() rebind */
|
||||
|
||||
fudge = 10; /* spare mmarray[] slots */
|
||||
fudge += cpus_weight(cs->cpus_allowed); /* imagine one fork-bomb/cpu */
|
||||
fudge += cpumask_weight(cs->cpus_allowed);/* imagine 1 fork-bomb/cpu */
|
||||
retval = -ENOMEM;
|
||||
|
||||
/*
|
||||
|
|
@ -1093,9 +1128,9 @@ done:
|
|||
* lock each such tasks mm->mmap_sem, scan its vma's and rebind
|
||||
* their mempolicies to the cpusets new mems_allowed.
|
||||
*/
|
||||
static int update_nodemask(struct cpuset *cs, const char *buf)
|
||||
static int update_nodemask(struct cpuset *cs, struct cpuset *trialcs,
|
||||
const char *buf)
|
||||
{
|
||||
struct cpuset trialcs;
|
||||
nodemask_t oldmem;
|
||||
int retval;
|
||||
|
||||
|
|
@ -1106,8 +1141,6 @@ static int update_nodemask(struct cpuset *cs, const char *buf)
|
|||
if (cs == &top_cpuset)
|
||||
return -EACCES;
|
||||
|
||||
trialcs = *cs;
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
|
@ -1115,27 +1148,27 @@ static int update_nodemask(struct cpuset *cs, const char *buf)
|
|||
* with tasks have memory.
|
||||
*/
|
||||
if (!*buf) {
|
||||
nodes_clear(trialcs.mems_allowed);
|
||||
nodes_clear(trialcs->mems_allowed);
|
||||
} else {
|
||||
retval = nodelist_parse(buf, trialcs.mems_allowed);
|
||||
retval = nodelist_parse(buf, trialcs->mems_allowed);
|
||||
if (retval < 0)
|
||||
goto done;
|
||||
|
||||
if (!nodes_subset(trialcs.mems_allowed,
|
||||
if (!nodes_subset(trialcs->mems_allowed,
|
||||
node_states[N_HIGH_MEMORY]))
|
||||
return -EINVAL;
|
||||
}
|
||||
oldmem = cs->mems_allowed;
|
||||
if (nodes_equal(oldmem, trialcs.mems_allowed)) {
|
||||
if (nodes_equal(oldmem, trialcs->mems_allowed)) {
|
||||
retval = 0; /* Too easy - nothing to do */
|
||||
goto done;
|
||||
}
|
||||
retval = validate_change(cs, &trialcs);
|
||||
retval = validate_change(cs, trialcs);
|
||||
if (retval < 0)
|
||||
goto done;
|
||||
|
||||
mutex_lock(&callback_mutex);
|
||||
cs->mems_allowed = trialcs.mems_allowed;
|
||||
cs->mems_allowed = trialcs->mems_allowed;
|
||||
cs->mems_generation = cpuset_mems_generation++;
|
||||
mutex_unlock(&callback_mutex);
|
||||
|
||||
|
|
@ -1156,7 +1189,8 @@ static int update_relax_domain_level(struct cpuset *cs, s64 val)
|
|||
|
||||
if (val != cs->relax_domain_level) {
|
||||
cs->relax_domain_level = val;
|
||||
if (!cpus_empty(cs->cpus_allowed) && is_sched_load_balance(cs))
|
||||
if (!cpumask_empty(cs->cpus_allowed) &&
|
||||
is_sched_load_balance(cs))
|
||||
async_rebuild_sched_domains();
|
||||
}
|
||||
|
||||
|
|
@ -1175,31 +1209,36 @@ static int update_relax_domain_level(struct cpuset *cs, s64 val)
|
|||
static int update_flag(cpuset_flagbits_t bit, struct cpuset *cs,
|
||||
int turning_on)
|
||||
{
|
||||
struct cpuset trialcs;
|
||||
struct cpuset *trialcs;
|
||||
int err;
|
||||
int balance_flag_changed;
|
||||
|
||||
trialcs = *cs;
|
||||
if (turning_on)
|
||||
set_bit(bit, &trialcs.flags);
|
||||
else
|
||||
clear_bit(bit, &trialcs.flags);
|
||||
trialcs = alloc_trial_cpuset(cs);
|
||||
if (!trialcs)
|
||||
return -ENOMEM;
|
||||
|
||||
err = validate_change(cs, &trialcs);
|
||||
if (turning_on)
|
||||
set_bit(bit, &trialcs->flags);
|
||||
else
|
||||
clear_bit(bit, &trialcs->flags);
|
||||
|
||||
err = validate_change(cs, trialcs);
|
||||
if (err < 0)
|
||||
return err;
|
||||
goto out;
|
||||
|
||||
balance_flag_changed = (is_sched_load_balance(cs) !=
|
||||
is_sched_load_balance(&trialcs));
|
||||
is_sched_load_balance(trialcs));
|
||||
|
||||
mutex_lock(&callback_mutex);
|
||||
cs->flags = trialcs.flags;
|
||||
cs->flags = trialcs->flags;
|
||||
mutex_unlock(&callback_mutex);
|
||||
|
||||
if (!cpus_empty(trialcs.cpus_allowed) && balance_flag_changed)
|
||||
if (!cpumask_empty(trialcs->cpus_allowed) && balance_flag_changed)
|
||||
async_rebuild_sched_domains();
|
||||
|
||||
return 0;
|
||||
out:
|
||||
free_trial_cpuset(trialcs);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1300,42 +1339,47 @@ static int fmeter_getrate(struct fmeter *fmp)
|
|||
return val;
|
||||
}
|
||||
|
||||
/* Protected by cgroup_lock */
|
||||
static cpumask_var_t cpus_attach;
|
||||
|
||||
/* Called by cgroups to determine if a cpuset is usable; cgroup_mutex held */
|
||||
static int cpuset_can_attach(struct cgroup_subsys *ss,
|
||||
struct cgroup *cont, struct task_struct *tsk)
|
||||
{
|
||||
struct cpuset *cs = cgroup_cs(cont);
|
||||
int ret = 0;
|
||||
|
||||
if (cpus_empty(cs->cpus_allowed) || nodes_empty(cs->mems_allowed))
|
||||
if (cpumask_empty(cs->cpus_allowed) || nodes_empty(cs->mems_allowed))
|
||||
return -ENOSPC;
|
||||
if (tsk->flags & PF_THREAD_BOUND) {
|
||||
cpumask_t mask;
|
||||
|
||||
if (tsk->flags & PF_THREAD_BOUND) {
|
||||
mutex_lock(&callback_mutex);
|
||||
mask = cs->cpus_allowed;
|
||||
if (!cpumask_equal(&tsk->cpus_allowed, cs->cpus_allowed))
|
||||
ret = -EINVAL;
|
||||
mutex_unlock(&callback_mutex);
|
||||
if (!cpus_equal(tsk->cpus_allowed, mask))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return security_task_setscheduler(tsk, 0, NULL);
|
||||
return ret < 0 ? ret : security_task_setscheduler(tsk, 0, NULL);
|
||||
}
|
||||
|
||||
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);
|
||||
int err;
|
||||
|
||||
mutex_lock(&callback_mutex);
|
||||
guarantee_online_cpus(cs, &cpus);
|
||||
err = set_cpus_allowed_ptr(tsk, &cpus);
|
||||
mutex_unlock(&callback_mutex);
|
||||
if (cs == &top_cpuset) {
|
||||
cpumask_copy(cpus_attach, cpu_possible_mask);
|
||||
} else {
|
||||
mutex_lock(&callback_mutex);
|
||||
guarantee_online_cpus(cs, cpus_attach);
|
||||
mutex_unlock(&callback_mutex);
|
||||
}
|
||||
err = set_cpus_allowed_ptr(tsk, cpus_attach);
|
||||
if (err)
|
||||
return;
|
||||
|
||||
|
|
@ -1348,7 +1392,6 @@ static void cpuset_attach(struct cgroup_subsys *ss,
|
|||
cpuset_migrate_mm(mm, &from, &to);
|
||||
mmput(mm);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* The various types of files and directories in a cpuset file system */
|
||||
|
|
@ -1443,21 +1486,29 @@ static int cpuset_write_resmask(struct cgroup *cgrp, struct cftype *cft,
|
|||
const char *buf)
|
||||
{
|
||||
int retval = 0;
|
||||
struct cpuset *cs = cgroup_cs(cgrp);
|
||||
struct cpuset *trialcs;
|
||||
|
||||
if (!cgroup_lock_live_group(cgrp))
|
||||
return -ENODEV;
|
||||
|
||||
trialcs = alloc_trial_cpuset(cs);
|
||||
if (!trialcs)
|
||||
return -ENOMEM;
|
||||
|
||||
switch (cft->private) {
|
||||
case FILE_CPULIST:
|
||||
retval = update_cpumask(cgroup_cs(cgrp), buf);
|
||||
retval = update_cpumask(cs, trialcs, buf);
|
||||
break;
|
||||
case FILE_MEMLIST:
|
||||
retval = update_nodemask(cgroup_cs(cgrp), buf);
|
||||
retval = update_nodemask(cs, trialcs, buf);
|
||||
break;
|
||||
default:
|
||||
retval = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
free_trial_cpuset(trialcs);
|
||||
cgroup_unlock();
|
||||
return retval;
|
||||
}
|
||||
|
|
@ -1476,13 +1527,13 @@ static int cpuset_write_resmask(struct cgroup *cgrp, struct cftype *cft,
|
|||
|
||||
static int cpuset_sprintf_cpulist(char *page, struct cpuset *cs)
|
||||
{
|
||||
cpumask_t mask;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&callback_mutex);
|
||||
mask = cs->cpus_allowed;
|
||||
ret = cpulist_scnprintf(page, PAGE_SIZE, cs->cpus_allowed);
|
||||
mutex_unlock(&callback_mutex);
|
||||
|
||||
return cpulist_scnprintf(page, PAGE_SIZE, mask);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cpuset_sprintf_memlist(char *page, struct cpuset *cs)
|
||||
|
|
@ -1718,7 +1769,7 @@ static void cpuset_post_clone(struct cgroup_subsys *ss,
|
|||
parent_cs = cgroup_cs(parent);
|
||||
|
||||
cs->mems_allowed = parent_cs->mems_allowed;
|
||||
cs->cpus_allowed = parent_cs->cpus_allowed;
|
||||
cpumask_copy(cs->cpus_allowed, parent_cs->cpus_allowed);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1744,6 +1795,10 @@ static struct cgroup_subsys_state *cpuset_create(
|
|||
cs = kmalloc(sizeof(*cs), GFP_KERNEL);
|
||||
if (!cs)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
if (!alloc_cpumask_var(&cs->cpus_allowed, GFP_KERNEL)) {
|
||||
kfree(cs);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
cpuset_update_task_memory_state();
|
||||
cs->flags = 0;
|
||||
|
|
@ -1752,7 +1807,7 @@ static struct cgroup_subsys_state *cpuset_create(
|
|||
if (is_spread_slab(parent))
|
||||
set_bit(CS_SPREAD_SLAB, &cs->flags);
|
||||
set_bit(CS_SCHED_LOAD_BALANCE, &cs->flags);
|
||||
cpus_clear(cs->cpus_allowed);
|
||||
cpumask_clear(cs->cpus_allowed);
|
||||
nodes_clear(cs->mems_allowed);
|
||||
cs->mems_generation = cpuset_mems_generation++;
|
||||
fmeter_init(&cs->fmeter);
|
||||
|
|
@ -1779,6 +1834,7 @@ static void cpuset_destroy(struct cgroup_subsys *ss, struct cgroup *cont)
|
|||
update_flag(CS_SCHED_LOAD_BALANCE, cs, 0);
|
||||
|
||||
number_of_cpusets--;
|
||||
free_cpumask_var(cs->cpus_allowed);
|
||||
kfree(cs);
|
||||
}
|
||||
|
||||
|
|
@ -1802,6 +1858,8 @@ struct cgroup_subsys cpuset_subsys = {
|
|||
|
||||
int __init cpuset_init_early(void)
|
||||
{
|
||||
alloc_bootmem_cpumask_var(&top_cpuset.cpus_allowed);
|
||||
|
||||
top_cpuset.mems_generation = cpuset_mems_generation++;
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1817,7 +1875,7 @@ int __init cpuset_init(void)
|
|||
{
|
||||
int err = 0;
|
||||
|
||||
cpus_setall(top_cpuset.cpus_allowed);
|
||||
cpumask_setall(top_cpuset.cpus_allowed);
|
||||
nodes_setall(top_cpuset.mems_allowed);
|
||||
|
||||
fmeter_init(&top_cpuset.fmeter);
|
||||
|
|
@ -1829,6 +1887,9 @@ int __init cpuset_init(void)
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (!alloc_cpumask_var(&cpus_attach, GFP_KERNEL))
|
||||
BUG();
|
||||
|
||||
number_of_cpusets = 1;
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1903,7 +1964,7 @@ static void remove_tasks_in_empty_cpuset(struct cpuset *cs)
|
|||
* has online cpus, so can't be empty).
|
||||
*/
|
||||
parent = cs->parent;
|
||||
while (cpus_empty(parent->cpus_allowed) ||
|
||||
while (cpumask_empty(parent->cpus_allowed) ||
|
||||
nodes_empty(parent->mems_allowed))
|
||||
parent = parent->parent;
|
||||
|
||||
|
|
@ -1944,7 +2005,7 @@ static void scan_for_empty_cpusets(struct cpuset *root)
|
|||
}
|
||||
|
||||
/* Continue past cpusets with all cpus, mems online */
|
||||
if (cpus_subset(cp->cpus_allowed, cpu_online_map) &&
|
||||
if (cpumask_subset(cp->cpus_allowed, cpu_online_mask) &&
|
||||
nodes_subset(cp->mems_allowed, node_states[N_HIGH_MEMORY]))
|
||||
continue;
|
||||
|
||||
|
|
@ -1952,13 +2013,14 @@ static void scan_for_empty_cpusets(struct cpuset *root)
|
|||
|
||||
/* Remove offline cpus and mems from this cpuset. */
|
||||
mutex_lock(&callback_mutex);
|
||||
cpus_and(cp->cpus_allowed, cp->cpus_allowed, cpu_online_map);
|
||||
cpumask_and(cp->cpus_allowed, cp->cpus_allowed,
|
||||
cpu_online_mask);
|
||||
nodes_and(cp->mems_allowed, cp->mems_allowed,
|
||||
node_states[N_HIGH_MEMORY]);
|
||||
mutex_unlock(&callback_mutex);
|
||||
|
||||
/* Move tasks from the empty cpuset to a parent */
|
||||
if (cpus_empty(cp->cpus_allowed) ||
|
||||
if (cpumask_empty(cp->cpus_allowed) ||
|
||||
nodes_empty(cp->mems_allowed))
|
||||
remove_tasks_in_empty_cpuset(cp);
|
||||
else {
|
||||
|
|
@ -1984,7 +2046,7 @@ static int cpuset_track_online_cpus(struct notifier_block *unused_nb,
|
|||
unsigned long phase, void *unused_cpu)
|
||||
{
|
||||
struct sched_domain_attr *attr;
|
||||
cpumask_t *doms;
|
||||
struct cpumask *doms;
|
||||
int ndoms;
|
||||
|
||||
switch (phase) {
|
||||
|
|
@ -1999,7 +2061,7 @@ static int cpuset_track_online_cpus(struct notifier_block *unused_nb,
|
|||
}
|
||||
|
||||
cgroup_lock();
|
||||
top_cpuset.cpus_allowed = cpu_online_map;
|
||||
cpumask_copy(top_cpuset.cpus_allowed, cpu_online_mask);
|
||||
scan_for_empty_cpusets(&top_cpuset);
|
||||
ndoms = generate_sched_domains(&doms, &attr);
|
||||
cgroup_unlock();
|
||||
|
|
@ -2044,7 +2106,7 @@ static int cpuset_track_online_nodes(struct notifier_block *self,
|
|||
|
||||
void __init cpuset_init_smp(void)
|
||||
{
|
||||
top_cpuset.cpus_allowed = cpu_online_map;
|
||||
cpumask_copy(top_cpuset.cpus_allowed, cpu_online_mask);
|
||||
top_cpuset.mems_allowed = node_states[N_HIGH_MEMORY];
|
||||
|
||||
hotcpu_notifier(cpuset_track_online_cpus, 0);
|
||||
|
|
@ -2054,15 +2116,15 @@ void __init cpuset_init_smp(void)
|
|||
/**
|
||||
* cpuset_cpus_allowed - return cpus_allowed mask from a tasks cpuset.
|
||||
* @tsk: pointer to task_struct from which to obtain cpuset->cpus_allowed.
|
||||
* @pmask: pointer to cpumask_t variable to receive cpus_allowed set.
|
||||
* @pmask: pointer to struct cpumask variable to receive cpus_allowed set.
|
||||
*
|
||||
* Description: Returns the cpumask_t cpus_allowed of the cpuset
|
||||
* Description: Returns the cpumask_var_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.
|
||||
**/
|
||||
|
||||
void cpuset_cpus_allowed(struct task_struct *tsk, cpumask_t *pmask)
|
||||
void cpuset_cpus_allowed(struct task_struct *tsk, struct cpumask *pmask)
|
||||
{
|
||||
mutex_lock(&callback_mutex);
|
||||
cpuset_cpus_allowed_locked(tsk, pmask);
|
||||
|
|
@ -2073,7 +2135,7 @@ void cpuset_cpus_allowed(struct task_struct *tsk, cpumask_t *pmask)
|
|||
* cpuset_cpus_allowed_locked - return cpus_allowed mask from a tasks cpuset.
|
||||
* Must be called with callback_mutex held.
|
||||
**/
|
||||
void cpuset_cpus_allowed_locked(struct task_struct *tsk, cpumask_t *pmask)
|
||||
void cpuset_cpus_allowed_locked(struct task_struct *tsk, struct cpumask *pmask)
|
||||
{
|
||||
task_lock(tsk);
|
||||
guarantee_online_cpus(task_cs(tsk), pmask);
|
||||
|
|
@ -2356,6 +2418,29 @@ int cpuset_mems_allowed_intersects(const struct task_struct *tsk1,
|
|||
return nodes_intersects(tsk1->mems_allowed, tsk2->mems_allowed);
|
||||
}
|
||||
|
||||
/**
|
||||
* cpuset_print_task_mems_allowed - prints task's cpuset and mems_allowed
|
||||
* @task: pointer to task_struct of some task.
|
||||
*
|
||||
* Description: Prints @task's name, cpuset name, and cached copy of its
|
||||
* mems_allowed to the kernel log. Must hold task_lock(task) to allow
|
||||
* dereferencing task_cs(task).
|
||||
*/
|
||||
void cpuset_print_task_mems_allowed(struct task_struct *tsk)
|
||||
{
|
||||
struct dentry *dentry;
|
||||
|
||||
dentry = task_cs(tsk)->css.cgroup->dentry;
|
||||
spin_lock(&cpuset_buffer_lock);
|
||||
snprintf(cpuset_name, CPUSET_NAME_LEN,
|
||||
dentry ? (const char *)dentry->d_name.name : "/");
|
||||
nodelist_scnprintf(cpuset_nodelist, CPUSET_NODELIST_LEN,
|
||||
tsk->mems_allowed);
|
||||
printk(KERN_INFO "%s cpuset=%s mems_allowed=%s\n",
|
||||
tsk->comm, cpuset_name, cpuset_nodelist);
|
||||
spin_unlock(&cpuset_buffer_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Collection of memory_pressure is suppressed unless
|
||||
* this flag is enabled by writing "1" to the special
|
||||
|
|
|
|||
21
kernel/cred-internals.h
Normal file
21
kernel/cred-internals.h
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/* Internal credentials stuff
|
||||
*
|
||||
* Copyright (C) 2008 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public Licence
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the Licence, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
/*
|
||||
* user.c
|
||||
*/
|
||||
static inline void sched_switch_user(struct task_struct *p)
|
||||
{
|
||||
#ifdef CONFIG_USER_SCHED
|
||||
sched_move_task(p);
|
||||
#endif /* CONFIG_USER_SCHED */
|
||||
}
|
||||
|
||||
591
kernel/cred.c
Normal file
591
kernel/cred.c
Normal file
|
|
@ -0,0 +1,591 @@
|
|||
/* Task credentials management - see Documentation/credentials.txt
|
||||
*
|
||||
* Copyright (C) 2008 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public Licence
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the Licence, or (at your option) any later version.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/cred.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/key.h>
|
||||
#include <linux/keyctl.h>
|
||||
#include <linux/init_task.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/cn_proc.h>
|
||||
#include "cred-internals.h"
|
||||
|
||||
static struct kmem_cache *cred_jar;
|
||||
|
||||
/*
|
||||
* The common credentials for the initial task's thread group
|
||||
*/
|
||||
#ifdef CONFIG_KEYS
|
||||
static struct thread_group_cred init_tgcred = {
|
||||
.usage = ATOMIC_INIT(2),
|
||||
.tgid = 0,
|
||||
.lock = SPIN_LOCK_UNLOCKED,
|
||||
};
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The initial credentials for the initial task
|
||||
*/
|
||||
struct cred init_cred = {
|
||||
.usage = ATOMIC_INIT(4),
|
||||
.securebits = SECUREBITS_DEFAULT,
|
||||
.cap_inheritable = CAP_INIT_INH_SET,
|
||||
.cap_permitted = CAP_FULL_SET,
|
||||
.cap_effective = CAP_INIT_EFF_SET,
|
||||
.cap_bset = CAP_INIT_BSET,
|
||||
.user = INIT_USER,
|
||||
.group_info = &init_groups,
|
||||
#ifdef CONFIG_KEYS
|
||||
.tgcred = &init_tgcred,
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
* Dispose of the shared task group credentials
|
||||
*/
|
||||
#ifdef CONFIG_KEYS
|
||||
static void release_tgcred_rcu(struct rcu_head *rcu)
|
||||
{
|
||||
struct thread_group_cred *tgcred =
|
||||
container_of(rcu, struct thread_group_cred, rcu);
|
||||
|
||||
BUG_ON(atomic_read(&tgcred->usage) != 0);
|
||||
|
||||
key_put(tgcred->session_keyring);
|
||||
key_put(tgcred->process_keyring);
|
||||
kfree(tgcred);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Release a set of thread group credentials.
|
||||
*/
|
||||
static void release_tgcred(struct cred *cred)
|
||||
{
|
||||
#ifdef CONFIG_KEYS
|
||||
struct thread_group_cred *tgcred = cred->tgcred;
|
||||
|
||||
if (atomic_dec_and_test(&tgcred->usage))
|
||||
call_rcu(&tgcred->rcu, release_tgcred_rcu);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* The RCU callback to actually dispose of a set of credentials
|
||||
*/
|
||||
static void put_cred_rcu(struct rcu_head *rcu)
|
||||
{
|
||||
struct cred *cred = container_of(rcu, struct cred, rcu);
|
||||
|
||||
if (atomic_read(&cred->usage) != 0)
|
||||
panic("CRED: put_cred_rcu() sees %p with usage %d\n",
|
||||
cred, atomic_read(&cred->usage));
|
||||
|
||||
security_cred_free(cred);
|
||||
key_put(cred->thread_keyring);
|
||||
key_put(cred->request_key_auth);
|
||||
release_tgcred(cred);
|
||||
put_group_info(cred->group_info);
|
||||
free_uid(cred->user);
|
||||
kmem_cache_free(cred_jar, cred);
|
||||
}
|
||||
|
||||
/**
|
||||
* __put_cred - Destroy a set of credentials
|
||||
* @cred: The record to release
|
||||
*
|
||||
* Destroy a set of credentials on which no references remain.
|
||||
*/
|
||||
void __put_cred(struct cred *cred)
|
||||
{
|
||||
BUG_ON(atomic_read(&cred->usage) != 0);
|
||||
|
||||
call_rcu(&cred->rcu, put_cred_rcu);
|
||||
}
|
||||
EXPORT_SYMBOL(__put_cred);
|
||||
|
||||
/**
|
||||
* prepare_creds - Prepare a new set of credentials for modification
|
||||
*
|
||||
* Prepare a new set of task credentials for modification. A task's creds
|
||||
* shouldn't generally be modified directly, therefore this function is used to
|
||||
* prepare a new copy, which the caller then modifies and then commits by
|
||||
* calling commit_creds().
|
||||
*
|
||||
* Preparation involves making a copy of the objective creds for modification.
|
||||
*
|
||||
* Returns a pointer to the new creds-to-be if successful, NULL otherwise.
|
||||
*
|
||||
* Call commit_creds() or abort_creds() to clean up.
|
||||
*/
|
||||
struct cred *prepare_creds(void)
|
||||
{
|
||||
struct task_struct *task = current;
|
||||
const struct cred *old;
|
||||
struct cred *new;
|
||||
|
||||
BUG_ON(atomic_read(&task->real_cred->usage) < 1);
|
||||
|
||||
new = kmem_cache_alloc(cred_jar, GFP_KERNEL);
|
||||
if (!new)
|
||||
return NULL;
|
||||
|
||||
old = task->cred;
|
||||
memcpy(new, old, sizeof(struct cred));
|
||||
|
||||
atomic_set(&new->usage, 1);
|
||||
get_group_info(new->group_info);
|
||||
get_uid(new->user);
|
||||
|
||||
#ifdef CONFIG_KEYS
|
||||
key_get(new->thread_keyring);
|
||||
key_get(new->request_key_auth);
|
||||
atomic_inc(&new->tgcred->usage);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SECURITY
|
||||
new->security = NULL;
|
||||
#endif
|
||||
|
||||
if (security_prepare_creds(new, old, GFP_KERNEL) < 0)
|
||||
goto error;
|
||||
return new;
|
||||
|
||||
error:
|
||||
abort_creds(new);
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(prepare_creds);
|
||||
|
||||
/*
|
||||
* Prepare credentials for current to perform an execve()
|
||||
* - The caller must hold current->cred_exec_mutex
|
||||
*/
|
||||
struct cred *prepare_exec_creds(void)
|
||||
{
|
||||
struct thread_group_cred *tgcred = NULL;
|
||||
struct cred *new;
|
||||
|
||||
#ifdef CONFIG_KEYS
|
||||
tgcred = kmalloc(sizeof(*tgcred), GFP_KERNEL);
|
||||
if (!tgcred)
|
||||
return NULL;
|
||||
#endif
|
||||
|
||||
new = prepare_creds();
|
||||
if (!new) {
|
||||
kfree(tgcred);
|
||||
return new;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KEYS
|
||||
/* newly exec'd tasks don't get a thread keyring */
|
||||
key_put(new->thread_keyring);
|
||||
new->thread_keyring = NULL;
|
||||
|
||||
/* create a new per-thread-group creds for all this set of threads to
|
||||
* share */
|
||||
memcpy(tgcred, new->tgcred, sizeof(struct thread_group_cred));
|
||||
|
||||
atomic_set(&tgcred->usage, 1);
|
||||
spin_lock_init(&tgcred->lock);
|
||||
|
||||
/* inherit the session keyring; new process keyring */
|
||||
key_get(tgcred->session_keyring);
|
||||
tgcred->process_keyring = NULL;
|
||||
|
||||
release_tgcred(new);
|
||||
new->tgcred = tgcred;
|
||||
#endif
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
/*
|
||||
* prepare new credentials for the usermode helper dispatcher
|
||||
*/
|
||||
struct cred *prepare_usermodehelper_creds(void)
|
||||
{
|
||||
#ifdef CONFIG_KEYS
|
||||
struct thread_group_cred *tgcred = NULL;
|
||||
#endif
|
||||
struct cred *new;
|
||||
|
||||
#ifdef CONFIG_KEYS
|
||||
tgcred = kzalloc(sizeof(*new->tgcred), GFP_ATOMIC);
|
||||
if (!tgcred)
|
||||
return NULL;
|
||||
#endif
|
||||
|
||||
new = kmem_cache_alloc(cred_jar, GFP_ATOMIC);
|
||||
if (!new)
|
||||
return NULL;
|
||||
|
||||
memcpy(new, &init_cred, sizeof(struct cred));
|
||||
|
||||
atomic_set(&new->usage, 1);
|
||||
get_group_info(new->group_info);
|
||||
get_uid(new->user);
|
||||
|
||||
#ifdef CONFIG_KEYS
|
||||
new->thread_keyring = NULL;
|
||||
new->request_key_auth = NULL;
|
||||
new->jit_keyring = KEY_REQKEY_DEFL_DEFAULT;
|
||||
|
||||
atomic_set(&tgcred->usage, 1);
|
||||
spin_lock_init(&tgcred->lock);
|
||||
new->tgcred = tgcred;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SECURITY
|
||||
new->security = NULL;
|
||||
#endif
|
||||
if (security_prepare_creds(new, &init_cred, GFP_ATOMIC) < 0)
|
||||
goto error;
|
||||
|
||||
BUG_ON(atomic_read(&new->usage) != 1);
|
||||
return new;
|
||||
|
||||
error:
|
||||
put_cred(new);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy credentials for the new process created by fork()
|
||||
*
|
||||
* We share if we can, but under some circumstances we have to generate a new
|
||||
* set.
|
||||
*
|
||||
* The new process gets the current process's subjective credentials as its
|
||||
* objective and subjective credentials
|
||||
*/
|
||||
int copy_creds(struct task_struct *p, unsigned long clone_flags)
|
||||
{
|
||||
#ifdef CONFIG_KEYS
|
||||
struct thread_group_cred *tgcred;
|
||||
#endif
|
||||
struct cred *new;
|
||||
int ret;
|
||||
|
||||
mutex_init(&p->cred_exec_mutex);
|
||||
|
||||
if (
|
||||
#ifdef CONFIG_KEYS
|
||||
!p->cred->thread_keyring &&
|
||||
#endif
|
||||
clone_flags & CLONE_THREAD
|
||||
) {
|
||||
p->real_cred = get_cred(p->cred);
|
||||
get_cred(p->cred);
|
||||
atomic_inc(&p->cred->user->processes);
|
||||
return 0;
|
||||
}
|
||||
|
||||
new = prepare_creds();
|
||||
if (!new)
|
||||
return -ENOMEM;
|
||||
|
||||
if (clone_flags & CLONE_NEWUSER) {
|
||||
ret = create_user_ns(new);
|
||||
if (ret < 0)
|
||||
goto error_put;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KEYS
|
||||
/* new threads get their own thread keyrings if their parent already
|
||||
* had one */
|
||||
if (new->thread_keyring) {
|
||||
key_put(new->thread_keyring);
|
||||
new->thread_keyring = NULL;
|
||||
if (clone_flags & CLONE_THREAD)
|
||||
install_thread_keyring_to_cred(new);
|
||||
}
|
||||
|
||||
/* we share the process and session keyrings between all the threads in
|
||||
* a process - this is slightly icky as we violate COW credentials a
|
||||
* bit */
|
||||
if (!(clone_flags & CLONE_THREAD)) {
|
||||
tgcred = kmalloc(sizeof(*tgcred), GFP_KERNEL);
|
||||
if (!tgcred) {
|
||||
ret = -ENOMEM;
|
||||
goto error_put;
|
||||
}
|
||||
atomic_set(&tgcred->usage, 1);
|
||||
spin_lock_init(&tgcred->lock);
|
||||
tgcred->process_keyring = NULL;
|
||||
tgcred->session_keyring = key_get(new->tgcred->session_keyring);
|
||||
|
||||
release_tgcred(new);
|
||||
new->tgcred = tgcred;
|
||||
}
|
||||
#endif
|
||||
|
||||
atomic_inc(&new->user->processes);
|
||||
p->cred = p->real_cred = get_cred(new);
|
||||
return 0;
|
||||
|
||||
error_put:
|
||||
put_cred(new);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* commit_creds - Install new credentials upon the current task
|
||||
* @new: The credentials to be assigned
|
||||
*
|
||||
* Install a new set of credentials to the current task, using RCU to replace
|
||||
* the old set. Both the objective and the subjective credentials pointers are
|
||||
* updated. This function may not be called if the subjective credentials are
|
||||
* in an overridden state.
|
||||
*
|
||||
* This function eats the caller's reference to the new credentials.
|
||||
*
|
||||
* Always returns 0 thus allowing this function to be tail-called at the end
|
||||
* of, say, sys_setgid().
|
||||
*/
|
||||
int commit_creds(struct cred *new)
|
||||
{
|
||||
struct task_struct *task = current;
|
||||
const struct cred *old;
|
||||
|
||||
BUG_ON(task->cred != task->real_cred);
|
||||
BUG_ON(atomic_read(&task->real_cred->usage) < 2);
|
||||
BUG_ON(atomic_read(&new->usage) < 1);
|
||||
|
||||
old = task->real_cred;
|
||||
security_commit_creds(new, old);
|
||||
|
||||
get_cred(new); /* we will require a ref for the subj creds too */
|
||||
|
||||
/* dumpability changes */
|
||||
if (old->euid != new->euid ||
|
||||
old->egid != new->egid ||
|
||||
old->fsuid != new->fsuid ||
|
||||
old->fsgid != new->fsgid ||
|
||||
!cap_issubset(new->cap_permitted, old->cap_permitted)) {
|
||||
if (task->mm)
|
||||
set_dumpable(task->mm, suid_dumpable);
|
||||
task->pdeath_signal = 0;
|
||||
smp_wmb();
|
||||
}
|
||||
|
||||
/* alter the thread keyring */
|
||||
if (new->fsuid != old->fsuid)
|
||||
key_fsuid_changed(task);
|
||||
if (new->fsgid != old->fsgid)
|
||||
key_fsgid_changed(task);
|
||||
|
||||
/* do it
|
||||
* - What if a process setreuid()'s and this brings the
|
||||
* new uid over his NPROC rlimit? We can check this now
|
||||
* cheaply with the new uid cache, so if it matters
|
||||
* we should be checking for it. -DaveM
|
||||
*/
|
||||
if (new->user != old->user)
|
||||
atomic_inc(&new->user->processes);
|
||||
rcu_assign_pointer(task->real_cred, new);
|
||||
rcu_assign_pointer(task->cred, new);
|
||||
if (new->user != old->user)
|
||||
atomic_dec(&old->user->processes);
|
||||
|
||||
sched_switch_user(task);
|
||||
|
||||
/* send notifications */
|
||||
if (new->uid != old->uid ||
|
||||
new->euid != old->euid ||
|
||||
new->suid != old->suid ||
|
||||
new->fsuid != old->fsuid)
|
||||
proc_id_connector(task, PROC_EVENT_UID);
|
||||
|
||||
if (new->gid != old->gid ||
|
||||
new->egid != old->egid ||
|
||||
new->sgid != old->sgid ||
|
||||
new->fsgid != old->fsgid)
|
||||
proc_id_connector(task, PROC_EVENT_GID);
|
||||
|
||||
/* release the old obj and subj refs both */
|
||||
put_cred(old);
|
||||
put_cred(old);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(commit_creds);
|
||||
|
||||
/**
|
||||
* abort_creds - Discard a set of credentials and unlock the current task
|
||||
* @new: The credentials that were going to be applied
|
||||
*
|
||||
* Discard a set of credentials that were under construction and unlock the
|
||||
* current task.
|
||||
*/
|
||||
void abort_creds(struct cred *new)
|
||||
{
|
||||
BUG_ON(atomic_read(&new->usage) < 1);
|
||||
put_cred(new);
|
||||
}
|
||||
EXPORT_SYMBOL(abort_creds);
|
||||
|
||||
/**
|
||||
* override_creds - Override the current process's subjective credentials
|
||||
* @new: The credentials to be assigned
|
||||
*
|
||||
* Install a set of temporary override subjective credentials on the current
|
||||
* process, returning the old set for later reversion.
|
||||
*/
|
||||
const struct cred *override_creds(const struct cred *new)
|
||||
{
|
||||
const struct cred *old = current->cred;
|
||||
|
||||
rcu_assign_pointer(current->cred, get_cred(new));
|
||||
return old;
|
||||
}
|
||||
EXPORT_SYMBOL(override_creds);
|
||||
|
||||
/**
|
||||
* revert_creds - Revert a temporary subjective credentials override
|
||||
* @old: The credentials to be restored
|
||||
*
|
||||
* Revert a temporary set of override subjective credentials to an old set,
|
||||
* discarding the override set.
|
||||
*/
|
||||
void revert_creds(const struct cred *old)
|
||||
{
|
||||
const struct cred *override = current->cred;
|
||||
|
||||
rcu_assign_pointer(current->cred, old);
|
||||
put_cred(override);
|
||||
}
|
||||
EXPORT_SYMBOL(revert_creds);
|
||||
|
||||
/*
|
||||
* initialise the credentials stuff
|
||||
*/
|
||||
void __init cred_init(void)
|
||||
{
|
||||
/* allocate a slab in which we can store credentials */
|
||||
cred_jar = kmem_cache_create("cred_jar", sizeof(struct cred),
|
||||
0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* prepare_kernel_cred - Prepare a set of credentials for a kernel service
|
||||
* @daemon: A userspace daemon to be used as a reference
|
||||
*
|
||||
* Prepare a set of credentials for a kernel service. This can then be used to
|
||||
* override a task's own credentials so that work can be done on behalf of that
|
||||
* task that requires a different subjective context.
|
||||
*
|
||||
* @daemon is used to provide a base for the security record, but can be NULL.
|
||||
* If @daemon is supplied, then the security data will be derived from that;
|
||||
* otherwise they'll be set to 0 and no groups, full capabilities and no keys.
|
||||
*
|
||||
* The caller may change these controls afterwards if desired.
|
||||
*
|
||||
* Returns the new credentials or NULL if out of memory.
|
||||
*
|
||||
* Does not take, and does not return holding current->cred_replace_mutex.
|
||||
*/
|
||||
struct cred *prepare_kernel_cred(struct task_struct *daemon)
|
||||
{
|
||||
const struct cred *old;
|
||||
struct cred *new;
|
||||
|
||||
new = kmem_cache_alloc(cred_jar, GFP_KERNEL);
|
||||
if (!new)
|
||||
return NULL;
|
||||
|
||||
if (daemon)
|
||||
old = get_task_cred(daemon);
|
||||
else
|
||||
old = get_cred(&init_cred);
|
||||
|
||||
*new = *old;
|
||||
get_uid(new->user);
|
||||
get_group_info(new->group_info);
|
||||
|
||||
#ifdef CONFIG_KEYS
|
||||
atomic_inc(&init_tgcred.usage);
|
||||
new->tgcred = &init_tgcred;
|
||||
new->request_key_auth = NULL;
|
||||
new->thread_keyring = NULL;
|
||||
new->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SECURITY
|
||||
new->security = NULL;
|
||||
#endif
|
||||
if (security_prepare_creds(new, old, GFP_KERNEL) < 0)
|
||||
goto error;
|
||||
|
||||
atomic_set(&new->usage, 1);
|
||||
put_cred(old);
|
||||
return new;
|
||||
|
||||
error:
|
||||
put_cred(new);
|
||||
put_cred(old);
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(prepare_kernel_cred);
|
||||
|
||||
/**
|
||||
* set_security_override - Set the security ID in a set of credentials
|
||||
* @new: The credentials to alter
|
||||
* @secid: The LSM security ID to set
|
||||
*
|
||||
* Set the LSM security ID in a set of credentials so that the subjective
|
||||
* security is overridden when an alternative set of credentials is used.
|
||||
*/
|
||||
int set_security_override(struct cred *new, u32 secid)
|
||||
{
|
||||
return security_kernel_act_as(new, secid);
|
||||
}
|
||||
EXPORT_SYMBOL(set_security_override);
|
||||
|
||||
/**
|
||||
* set_security_override_from_ctx - Set the security ID in a set of credentials
|
||||
* @new: The credentials to alter
|
||||
* @secctx: The LSM security context to generate the security ID from.
|
||||
*
|
||||
* Set the LSM security ID in a set of credentials so that the subjective
|
||||
* security is overridden when an alternative set of credentials is used. The
|
||||
* security ID is specified in string form as a security context to be
|
||||
* interpreted by the LSM.
|
||||
*/
|
||||
int set_security_override_from_ctx(struct cred *new, const char *secctx)
|
||||
{
|
||||
u32 secid;
|
||||
int ret;
|
||||
|
||||
ret = security_secctx_to_secid(secctx, strlen(secctx), &secid);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return set_security_override(new, secid);
|
||||
}
|
||||
EXPORT_SYMBOL(set_security_override_from_ctx);
|
||||
|
||||
/**
|
||||
* set_create_files_as - Set the LSM file create context in a set of credentials
|
||||
* @new: The credentials to alter
|
||||
* @inode: The inode to take the context from
|
||||
*
|
||||
* Change the LSM file creation context in a set of credentials to be the same
|
||||
* as the object context of the specified inode, so that the new inodes have
|
||||
* the same MAC context as that inode.
|
||||
*/
|
||||
int set_create_files_as(struct cred *new, struct inode *inode)
|
||||
{
|
||||
new->fsuid = inode->i_uid;
|
||||
new->fsgid = inode->i_gid;
|
||||
return security_kernel_create_files_as(new, inode);
|
||||
}
|
||||
EXPORT_SYMBOL(set_create_files_as);
|
||||
|
|
@ -127,7 +127,7 @@ int __delayacct_add_tsk(struct taskstats *d, struct task_struct *tsk)
|
|||
*/
|
||||
t1 = tsk->sched_info.pcount;
|
||||
t2 = tsk->sched_info.run_delay;
|
||||
t3 = tsk->sched_info.cpu_time;
|
||||
t3 = tsk->se.sum_exec_runtime;
|
||||
|
||||
d->cpu_count += t1;
|
||||
|
||||
|
|
|
|||
|
|
@ -109,20 +109,40 @@ EXPORT_SYMBOL(dma_mark_declared_memory_occupied);
|
|||
int dma_alloc_from_coherent(struct device *dev, ssize_t size,
|
||||
dma_addr_t *dma_handle, void **ret)
|
||||
{
|
||||
struct dma_coherent_mem *mem = dev ? dev->dma_mem : NULL;
|
||||
struct dma_coherent_mem *mem;
|
||||
int order = get_order(size);
|
||||
int pageno;
|
||||
|
||||
if (mem) {
|
||||
int page = bitmap_find_free_region(mem->bitmap, mem->size,
|
||||
order);
|
||||
if (page >= 0) {
|
||||
*dma_handle = mem->device_base + (page << PAGE_SHIFT);
|
||||
*ret = mem->virt_base + (page << PAGE_SHIFT);
|
||||
memset(*ret, 0, size);
|
||||
} else if (mem->flags & DMA_MEMORY_EXCLUSIVE)
|
||||
*ret = NULL;
|
||||
if (!dev)
|
||||
return 0;
|
||||
mem = dev->dma_mem;
|
||||
if (!mem)
|
||||
return 0;
|
||||
if (unlikely(size > mem->size))
|
||||
return 0;
|
||||
|
||||
pageno = bitmap_find_free_region(mem->bitmap, mem->size, order);
|
||||
if (pageno >= 0) {
|
||||
/*
|
||||
* Memory was found in the per-device arena.
|
||||
*/
|
||||
*dma_handle = mem->device_base + (pageno << PAGE_SHIFT);
|
||||
*ret = mem->virt_base + (pageno << PAGE_SHIFT);
|
||||
memset(*ret, 0, size);
|
||||
} else if (mem->flags & DMA_MEMORY_EXCLUSIVE) {
|
||||
/*
|
||||
* The per-device arena is exhausted and we are not
|
||||
* permitted to fall back to generic memory.
|
||||
*/
|
||||
*ret = NULL;
|
||||
} else {
|
||||
/*
|
||||
* The per-device arena is exhausted and we are
|
||||
* permitted to fall back to generic memory.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
return (mem != NULL);
|
||||
return 1;
|
||||
}
|
||||
EXPORT_SYMBOL(dma_alloc_from_coherent);
|
||||
|
||||
|
|
|
|||
|
|
@ -209,8 +209,7 @@ static int __init proc_execdomains_init(void)
|
|||
module_init(proc_execdomains_init);
|
||||
#endif
|
||||
|
||||
asmlinkage long
|
||||
sys_personality(u_long personality)
|
||||
SYSCALL_DEFINE1(personality, u_long, personality)
|
||||
{
|
||||
u_long old = current->personality;
|
||||
|
||||
|
|
|
|||
|
|
@ -46,12 +46,18 @@
|
|||
#include <linux/blkdev.h>
|
||||
#include <linux/task_io_accounting_ops.h>
|
||||
#include <linux/tracehook.h>
|
||||
#include <linux/init_task.h>
|
||||
#include <trace/sched.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/unistd.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/mmu_context.h>
|
||||
#include "cred-internals.h"
|
||||
|
||||
DEFINE_TRACE(sched_process_free);
|
||||
DEFINE_TRACE(sched_process_exit);
|
||||
DEFINE_TRACE(sched_process_wait);
|
||||
|
||||
static void exit_mm(struct task_struct * tsk);
|
||||
|
||||
|
|
@ -164,7 +170,10 @@ void release_task(struct task_struct * p)
|
|||
int zap_leader;
|
||||
repeat:
|
||||
tracehook_prepare_release_task(p);
|
||||
atomic_dec(&p->user->processes);
|
||||
/* don't need to get the RCU readlock here - the process is dead and
|
||||
* can't be modifying its own credentials */
|
||||
atomic_dec(&__task_cred(p)->user->processes);
|
||||
|
||||
proc_flush_task(p);
|
||||
write_lock_irq(&tasklist_lock);
|
||||
tracehook_finish_release_task(p);
|
||||
|
|
@ -339,12 +348,12 @@ static void reparent_to_kthreadd(void)
|
|||
/* cpus_allowed? */
|
||||
/* rt_priority? */
|
||||
/* signals? */
|
||||
security_task_reparent_to_init(current);
|
||||
memcpy(current->signal->rlim, init_task.signal->rlim,
|
||||
sizeof(current->signal->rlim));
|
||||
atomic_inc(&(INIT_USER->__count));
|
||||
|
||||
atomic_inc(&init_cred.usage);
|
||||
commit_creds(&init_cred);
|
||||
write_unlock_irq(&tasklist_lock);
|
||||
switch_uid(INIT_USER);
|
||||
}
|
||||
|
||||
void __set_special_pids(struct pid *pid)
|
||||
|
|
@ -633,35 +642,31 @@ retry:
|
|||
/*
|
||||
* We found no owner yet mm_users > 1: this implies that we are
|
||||
* most likely racing with swapoff (try_to_unuse()) or /proc or
|
||||
* ptrace or page migration (get_task_mm()). Mark owner as NULL,
|
||||
* so that subsystems can understand the callback and take action.
|
||||
* ptrace or page migration (get_task_mm()). Mark owner as NULL.
|
||||
*/
|
||||
down_write(&mm->mmap_sem);
|
||||
cgroup_mm_owner_callbacks(mm->owner, NULL);
|
||||
mm->owner = NULL;
|
||||
up_write(&mm->mmap_sem);
|
||||
return;
|
||||
|
||||
assign_new_owner:
|
||||
BUG_ON(c == p);
|
||||
get_task_struct(c);
|
||||
read_unlock(&tasklist_lock);
|
||||
down_write(&mm->mmap_sem);
|
||||
/*
|
||||
* The task_lock protects c->mm from changing.
|
||||
* We always want mm->owner->mm == mm
|
||||
*/
|
||||
task_lock(c);
|
||||
/*
|
||||
* Delay read_unlock() till we have the task_lock()
|
||||
* to ensure that c does not slip away underneath us
|
||||
*/
|
||||
read_unlock(&tasklist_lock);
|
||||
if (c->mm != mm) {
|
||||
task_unlock(c);
|
||||
up_write(&mm->mmap_sem);
|
||||
put_task_struct(c);
|
||||
goto retry;
|
||||
}
|
||||
cgroup_mm_owner_callbacks(mm->owner, c);
|
||||
mm->owner = c;
|
||||
task_unlock(c);
|
||||
up_write(&mm->mmap_sem);
|
||||
put_task_struct(c);
|
||||
}
|
||||
#endif /* CONFIG_MM_OWNER */
|
||||
|
|
@ -1028,8 +1033,6 @@ NORET_TYPE void do_exit(long code)
|
|||
* task into the wait for ever nirwana as well.
|
||||
*/
|
||||
tsk->flags |= PF_EXITPIDONE;
|
||||
if (tsk->io_context)
|
||||
exit_io_context();
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
schedule();
|
||||
}
|
||||
|
|
@ -1048,10 +1051,7 @@ NORET_TYPE void do_exit(long code)
|
|||
preempt_count());
|
||||
|
||||
acct_update_integrals(tsk);
|
||||
if (tsk->mm) {
|
||||
update_hiwater_rss(tsk->mm);
|
||||
update_hiwater_vm(tsk->mm);
|
||||
}
|
||||
|
||||
group_dead = atomic_dec_and_test(&tsk->signal->live);
|
||||
if (group_dead) {
|
||||
hrtimer_cancel(&tsk->signal->real_timer);
|
||||
|
|
@ -1078,7 +1078,6 @@ NORET_TYPE void do_exit(long code)
|
|||
check_stack_usage();
|
||||
exit_thread();
|
||||
cgroup_exit(tsk, 1);
|
||||
exit_keys(tsk);
|
||||
|
||||
if (group_dead && tsk->signal->leader)
|
||||
disassociate_ctty(1);
|
||||
|
|
@ -1123,7 +1122,6 @@ NORET_TYPE void do_exit(long code)
|
|||
preempt_disable();
|
||||
/* causes final put_task_struct in finish_task_switch(). */
|
||||
tsk->state = TASK_DEAD;
|
||||
|
||||
schedule();
|
||||
BUG();
|
||||
/* Avoid "noreturn function does return". */
|
||||
|
|
@ -1143,7 +1141,7 @@ NORET_TYPE void complete_and_exit(struct completion *comp, long code)
|
|||
|
||||
EXPORT_SYMBOL(complete_and_exit);
|
||||
|
||||
asmlinkage long sys_exit(int error_code)
|
||||
SYSCALL_DEFINE1(exit, int, error_code)
|
||||
{
|
||||
do_exit((error_code&0xff)<<8);
|
||||
}
|
||||
|
|
@ -1184,9 +1182,11 @@ do_group_exit(int exit_code)
|
|||
* wait4()-ing process will get the correct exit code - even if this
|
||||
* thread is not the thread group leader.
|
||||
*/
|
||||
asmlinkage void sys_exit_group(int error_code)
|
||||
SYSCALL_DEFINE1(exit_group, int, error_code)
|
||||
{
|
||||
do_group_exit((error_code & 0xff) << 8);
|
||||
/* NOTREACHED */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct pid *task_pid_type(struct task_struct *task, enum pid_type type)
|
||||
|
|
@ -1263,12 +1263,12 @@ static int wait_task_zombie(struct task_struct *p, int options,
|
|||
unsigned long state;
|
||||
int retval, status, traced;
|
||||
pid_t pid = task_pid_vnr(p);
|
||||
uid_t uid = __task_cred(p)->uid;
|
||||
|
||||
if (!likely(options & WEXITED))
|
||||
return 0;
|
||||
|
||||
if (unlikely(options & WNOWAIT)) {
|
||||
uid_t uid = p->uid;
|
||||
int exit_code = p->exit_code;
|
||||
int why, status;
|
||||
|
||||
|
|
@ -1321,10 +1321,10 @@ static int wait_task_zombie(struct task_struct *p, int options,
|
|||
* group, which consolidates times for all threads in the
|
||||
* group including the group leader.
|
||||
*/
|
||||
thread_group_cputime(p, &cputime);
|
||||
spin_lock_irq(&p->parent->sighand->siglock);
|
||||
psig = p->parent->signal;
|
||||
sig = p->signal;
|
||||
thread_group_cputime(p, &cputime);
|
||||
psig->cutime =
|
||||
cputime_add(psig->cutime,
|
||||
cputime_add(cputime.utime,
|
||||
|
|
@ -1389,7 +1389,7 @@ static int wait_task_zombie(struct task_struct *p, int options,
|
|||
if (!retval && infop)
|
||||
retval = put_user(pid, &infop->si_pid);
|
||||
if (!retval && infop)
|
||||
retval = put_user(p->uid, &infop->si_uid);
|
||||
retval = put_user(uid, &infop->si_uid);
|
||||
if (!retval)
|
||||
retval = pid;
|
||||
|
||||
|
|
@ -1454,7 +1454,8 @@ static int wait_task_stopped(int ptrace, struct task_struct *p,
|
|||
if (!unlikely(options & WNOWAIT))
|
||||
p->exit_code = 0;
|
||||
|
||||
uid = p->uid;
|
||||
/* don't need the RCU readlock here as we're holding a spinlock */
|
||||
uid = __task_cred(p)->uid;
|
||||
unlock_sig:
|
||||
spin_unlock_irq(&p->sighand->siglock);
|
||||
if (!exit_code)
|
||||
|
|
@ -1528,10 +1529,10 @@ static int wait_task_continued(struct task_struct *p, int options,
|
|||
}
|
||||
if (!unlikely(options & WNOWAIT))
|
||||
p->signal->flags &= ~SIGNAL_STOP_CONTINUED;
|
||||
uid = __task_cred(p)->uid;
|
||||
spin_unlock_irq(&p->sighand->siglock);
|
||||
|
||||
pid = task_pid_vnr(p);
|
||||
uid = p->uid;
|
||||
get_task_struct(p);
|
||||
read_unlock(&tasklist_lock);
|
||||
|
||||
|
|
@ -1753,9 +1754,8 @@ end:
|
|||
return retval;
|
||||
}
|
||||
|
||||
asmlinkage long sys_waitid(int which, pid_t upid,
|
||||
struct siginfo __user *infop, int options,
|
||||
struct rusage __user *ru)
|
||||
SYSCALL_DEFINE5(waitid, int, which, pid_t, upid, struct siginfo __user *,
|
||||
infop, int, options, struct rusage __user *, ru)
|
||||
{
|
||||
struct pid *pid = NULL;
|
||||
enum pid_type type;
|
||||
|
|
@ -1794,8 +1794,8 @@ asmlinkage long sys_waitid(int which, pid_t upid,
|
|||
return ret;
|
||||
}
|
||||
|
||||
asmlinkage long sys_wait4(pid_t upid, int __user *stat_addr,
|
||||
int options, struct rusage __user *ru)
|
||||
SYSCALL_DEFINE4(wait4, pid_t, upid, int __user *, stat_addr,
|
||||
int, options, struct rusage __user *, ru)
|
||||
{
|
||||
struct pid *pid = NULL;
|
||||
enum pid_type type;
|
||||
|
|
@ -1832,7 +1832,7 @@ asmlinkage long sys_wait4(pid_t upid, int __user *stat_addr,
|
|||
* sys_waitpid() remains for compatibility. waitpid() should be
|
||||
* implemented by calling sys_wait4() from libc.a.
|
||||
*/
|
||||
asmlinkage long sys_waitpid(pid_t pid, int __user *stat_addr, int options)
|
||||
SYSCALL_DEFINE3(waitpid, pid_t, pid, int __user *, stat_addr, int, options)
|
||||
{
|
||||
return sys_wait4(pid, stat_addr, options, NULL);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ftrace.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/sections.h>
|
||||
|
||||
|
|
@ -40,7 +41,7 @@ const struct exception_table_entry *search_exception_tables(unsigned long addr)
|
|||
return e;
|
||||
}
|
||||
|
||||
int core_kernel_text(unsigned long addr)
|
||||
__notrace_funcgraph int core_kernel_text(unsigned long addr)
|
||||
{
|
||||
if (addr >= (unsigned long)_stext &&
|
||||
addr <= (unsigned long)_etext)
|
||||
|
|
@ -53,7 +54,7 @@ int core_kernel_text(unsigned long addr)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int __kernel_text_address(unsigned long addr)
|
||||
__notrace_funcgraph int __kernel_text_address(unsigned long addr)
|
||||
{
|
||||
if (core_kernel_text(addr))
|
||||
return 1;
|
||||
|
|
@ -66,3 +67,19 @@ int kernel_text_address(unsigned long addr)
|
|||
return 1;
|
||||
return module_text_address(addr) != NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* On some architectures (PPC64, IA64) function pointers
|
||||
* are actually only tokens to some data that then holds the
|
||||
* real function address. As a result, to find if a function
|
||||
* pointer is part of the kernel text, we need to do some
|
||||
* special dereferencing first.
|
||||
*/
|
||||
int func_ptr_is_kernel_text(void *ptr)
|
||||
{
|
||||
unsigned long addr;
|
||||
addr = (unsigned long) dereference_function_descriptor(ptr);
|
||||
if (core_kernel_text(addr))
|
||||
return 1;
|
||||
return module_text_address(addr) != NULL;
|
||||
}
|
||||
|
|
|
|||
109
kernel/fork.c
109
kernel/fork.c
|
|
@ -47,6 +47,7 @@
|
|||
#include <linux/mount.h>
|
||||
#include <linux/audit.h>
|
||||
#include <linux/memcontrol.h>
|
||||
#include <linux/ftrace.h>
|
||||
#include <linux/profile.h>
|
||||
#include <linux/rmap.h>
|
||||
#include <linux/acct.h>
|
||||
|
|
@ -80,6 +81,8 @@ DEFINE_PER_CPU(unsigned long, process_counts) = 0;
|
|||
|
||||
__cacheline_aligned DEFINE_RWLOCK(tasklist_lock); /* outer */
|
||||
|
||||
DEFINE_TRACE(sched_process_fork);
|
||||
|
||||
int nr_processes(void)
|
||||
{
|
||||
int cpu;
|
||||
|
|
@ -137,6 +140,7 @@ void free_task(struct task_struct *tsk)
|
|||
prop_local_destroy_single(&tsk->dirties);
|
||||
free_thread_info(tsk->stack);
|
||||
rt_mutex_debug_task_free(tsk);
|
||||
ftrace_graph_exit_task(tsk);
|
||||
free_task_struct(tsk);
|
||||
}
|
||||
EXPORT_SYMBOL(free_task);
|
||||
|
|
@ -147,9 +151,8 @@ void __put_task_struct(struct task_struct *tsk)
|
|||
WARN_ON(atomic_read(&tsk->usage));
|
||||
WARN_ON(tsk == current);
|
||||
|
||||
security_task_free(tsk);
|
||||
free_uid(tsk->user);
|
||||
put_group_info(tsk->group_info);
|
||||
put_cred(tsk->real_cred);
|
||||
put_cred(tsk->cred);
|
||||
delayacct_tsk_free(tsk);
|
||||
|
||||
if (!profile_handoff_task(tsk))
|
||||
|
|
@ -397,6 +400,18 @@ __cacheline_aligned_in_smp DEFINE_SPINLOCK(mmlist_lock);
|
|||
#define allocate_mm() (kmem_cache_alloc(mm_cachep, GFP_KERNEL))
|
||||
#define free_mm(mm) (kmem_cache_free(mm_cachep, (mm)))
|
||||
|
||||
static unsigned long default_dump_filter = MMF_DUMP_FILTER_DEFAULT;
|
||||
|
||||
static int __init coredump_filter_setup(char *s)
|
||||
{
|
||||
default_dump_filter =
|
||||
(simple_strtoul(s, NULL, 0) << MMF_DUMP_FILTER_SHIFT) &
|
||||
MMF_DUMP_FILTER_MASK;
|
||||
return 1;
|
||||
}
|
||||
|
||||
__setup("coredump_filter=", coredump_filter_setup);
|
||||
|
||||
#include <linux/init_task.h>
|
||||
|
||||
static struct mm_struct * mm_init(struct mm_struct * mm, struct task_struct *p)
|
||||
|
|
@ -405,15 +420,14 @@ static struct mm_struct * mm_init(struct mm_struct * mm, struct task_struct *p)
|
|||
atomic_set(&mm->mm_count, 1);
|
||||
init_rwsem(&mm->mmap_sem);
|
||||
INIT_LIST_HEAD(&mm->mmlist);
|
||||
mm->flags = (current->mm) ? current->mm->flags
|
||||
: MMF_DUMP_FILTER_DEFAULT;
|
||||
mm->flags = (current->mm) ? current->mm->flags : default_dump_filter;
|
||||
mm->core_state = NULL;
|
||||
mm->nr_ptes = 0;
|
||||
set_mm_counter(mm, file_rss, 0);
|
||||
set_mm_counter(mm, anon_rss, 0);
|
||||
spin_lock_init(&mm->page_table_lock);
|
||||
rwlock_init(&mm->ioctx_list_lock);
|
||||
mm->ioctx_list = NULL;
|
||||
spin_lock_init(&mm->ioctx_lock);
|
||||
INIT_HLIST_HEAD(&mm->ioctx_list);
|
||||
mm->free_area_cache = TASK_UNMAPPED_BASE;
|
||||
mm->cached_hole_size = ~0UL;
|
||||
mm_init_owner(mm, p);
|
||||
|
|
@ -755,7 +769,7 @@ static int copy_sighand(unsigned long clone_flags, struct task_struct *tsk)
|
|||
{
|
||||
struct sighand_struct *sig;
|
||||
|
||||
if (clone_flags & (CLONE_SIGHAND | CLONE_THREAD)) {
|
||||
if (clone_flags & CLONE_SIGHAND) {
|
||||
atomic_inc(¤t->sighand->count);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -818,12 +832,6 @@ static int copy_signal(unsigned long clone_flags, struct task_struct *tsk)
|
|||
if (!sig)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = copy_thread_group_keys(tsk);
|
||||
if (ret < 0) {
|
||||
kmem_cache_free(signal_cachep, sig);
|
||||
return ret;
|
||||
}
|
||||
|
||||
atomic_set(&sig->count, 1);
|
||||
atomic_set(&sig->live, 1);
|
||||
init_waitqueue_head(&sig->wait_chldexit);
|
||||
|
|
@ -868,7 +876,6 @@ static int copy_signal(unsigned long clone_flags, struct task_struct *tsk)
|
|||
void __cleanup_signal(struct signal_struct *sig)
|
||||
{
|
||||
thread_group_cputime_free(sig);
|
||||
exit_thread_group_keys(sig);
|
||||
tty_kref_put(sig->tty);
|
||||
kmem_cache_free(signal_cachep, sig);
|
||||
}
|
||||
|
|
@ -894,7 +901,7 @@ static void copy_flags(unsigned long clone_flags, struct task_struct *p)
|
|||
clear_freeze_flag(p);
|
||||
}
|
||||
|
||||
asmlinkage long sys_set_tid_address(int __user *tidptr)
|
||||
SYSCALL_DEFINE1(set_tid_address, int __user *, tidptr)
|
||||
{
|
||||
current->clear_child_tid = tidptr;
|
||||
|
||||
|
|
@ -984,16 +991,16 @@ static struct task_struct *copy_process(unsigned long clone_flags,
|
|||
DEBUG_LOCKS_WARN_ON(!p->softirqs_enabled);
|
||||
#endif
|
||||
retval = -EAGAIN;
|
||||
if (atomic_read(&p->user->processes) >=
|
||||
if (atomic_read(&p->real_cred->user->processes) >=
|
||||
p->signal->rlim[RLIMIT_NPROC].rlim_cur) {
|
||||
if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RESOURCE) &&
|
||||
p->user != current->nsproxy->user_ns->root_user)
|
||||
p->real_cred->user != INIT_USER)
|
||||
goto bad_fork_free;
|
||||
}
|
||||
|
||||
atomic_inc(&p->user->__count);
|
||||
atomic_inc(&p->user->processes);
|
||||
get_group_info(p->group_info);
|
||||
retval = copy_creds(p, clone_flags);
|
||||
if (retval < 0)
|
||||
goto bad_fork_free;
|
||||
|
||||
/*
|
||||
* If multiple threads are within copy_process(), then this check
|
||||
|
|
@ -1048,10 +1055,6 @@ static struct task_struct *copy_process(unsigned long clone_flags,
|
|||
do_posix_clock_monotonic_gettime(&p->start_time);
|
||||
p->real_start_time = p->start_time;
|
||||
monotonic_to_bootbased(&p->real_start_time);
|
||||
#ifdef CONFIG_SECURITY
|
||||
p->security = NULL;
|
||||
#endif
|
||||
p->cap_bset = current->cap_bset;
|
||||
p->io_context = NULL;
|
||||
p->audit_context = NULL;
|
||||
cgroup_fork(p);
|
||||
|
|
@ -1092,14 +1095,14 @@ static struct task_struct *copy_process(unsigned long clone_flags,
|
|||
#ifdef CONFIG_DEBUG_MUTEXES
|
||||
p->blocked_on = NULL; /* not blocked yet */
|
||||
#endif
|
||||
if (unlikely(ptrace_reparented(current)))
|
||||
ptrace_fork(p, clone_flags);
|
||||
|
||||
/* Perform scheduler related setup. Assign this task to a CPU. */
|
||||
sched_fork(p, clone_flags);
|
||||
|
||||
if ((retval = security_task_alloc(p)))
|
||||
goto bad_fork_cleanup_policy;
|
||||
if ((retval = audit_alloc(p)))
|
||||
goto bad_fork_cleanup_security;
|
||||
goto bad_fork_cleanup_policy;
|
||||
/* copy all the process information */
|
||||
if ((retval = copy_semundo(clone_flags, p)))
|
||||
goto bad_fork_cleanup_audit;
|
||||
|
|
@ -1113,10 +1116,8 @@ static struct task_struct *copy_process(unsigned long clone_flags,
|
|||
goto bad_fork_cleanup_sighand;
|
||||
if ((retval = copy_mm(clone_flags, p)))
|
||||
goto bad_fork_cleanup_signal;
|
||||
if ((retval = copy_keys(clone_flags, p)))
|
||||
goto bad_fork_cleanup_mm;
|
||||
if ((retval = copy_namespaces(clone_flags, p)))
|
||||
goto bad_fork_cleanup_keys;
|
||||
goto bad_fork_cleanup_mm;
|
||||
if ((retval = copy_io(clone_flags, p)))
|
||||
goto bad_fork_cleanup_namespaces;
|
||||
retval = copy_thread(0, clone_flags, stack_start, stack_size, p, regs);
|
||||
|
|
@ -1125,17 +1126,19 @@ static struct task_struct *copy_process(unsigned long clone_flags,
|
|||
|
||||
if (pid != &init_struct_pid) {
|
||||
retval = -ENOMEM;
|
||||
pid = alloc_pid(task_active_pid_ns(p));
|
||||
pid = alloc_pid(p->nsproxy->pid_ns);
|
||||
if (!pid)
|
||||
goto bad_fork_cleanup_io;
|
||||
|
||||
if (clone_flags & CLONE_NEWPID) {
|
||||
retval = pid_ns_prepare_proc(task_active_pid_ns(p));
|
||||
retval = pid_ns_prepare_proc(p->nsproxy->pid_ns);
|
||||
if (retval < 0)
|
||||
goto bad_fork_free_pid;
|
||||
}
|
||||
}
|
||||
|
||||
ftrace_graph_init_task(p);
|
||||
|
||||
p->pid = pid_nr(pid);
|
||||
p->tgid = p->pid;
|
||||
if (clone_flags & CLONE_THREAD)
|
||||
|
|
@ -1144,7 +1147,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
|
|||
if (current->nsproxy != p->nsproxy) {
|
||||
retval = ns_cgroup_clone(p, pid);
|
||||
if (retval)
|
||||
goto bad_fork_free_pid;
|
||||
goto bad_fork_free_graph;
|
||||
}
|
||||
|
||||
p->set_child_tid = (clone_flags & CLONE_CHILD_SETTID) ? child_tidptr : NULL;
|
||||
|
|
@ -1237,7 +1240,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
|
|||
spin_unlock(¤t->sighand->siglock);
|
||||
write_unlock_irq(&tasklist_lock);
|
||||
retval = -ERESTARTNOINTR;
|
||||
goto bad_fork_free_pid;
|
||||
goto bad_fork_free_graph;
|
||||
}
|
||||
|
||||
if (clone_flags & CLONE_THREAD) {
|
||||
|
|
@ -1274,6 +1277,8 @@ static struct task_struct *copy_process(unsigned long clone_flags,
|
|||
cgroup_post_fork(p);
|
||||
return p;
|
||||
|
||||
bad_fork_free_graph:
|
||||
ftrace_graph_exit_task(p);
|
||||
bad_fork_free_pid:
|
||||
if (pid != &init_struct_pid)
|
||||
free_pid(pid);
|
||||
|
|
@ -1281,8 +1286,6 @@ bad_fork_cleanup_io:
|
|||
put_io_context(p->io_context);
|
||||
bad_fork_cleanup_namespaces:
|
||||
exit_task_namespaces(p);
|
||||
bad_fork_cleanup_keys:
|
||||
exit_keys(p);
|
||||
bad_fork_cleanup_mm:
|
||||
if (p->mm)
|
||||
mmput(p->mm);
|
||||
|
|
@ -1298,8 +1301,6 @@ bad_fork_cleanup_semundo:
|
|||
exit_sem(p);
|
||||
bad_fork_cleanup_audit:
|
||||
audit_free(p);
|
||||
bad_fork_cleanup_security:
|
||||
security_task_free(p);
|
||||
bad_fork_cleanup_policy:
|
||||
#ifdef CONFIG_NUMA
|
||||
mpol_put(p->mempolicy);
|
||||
|
|
@ -1312,9 +1313,9 @@ bad_fork_cleanup_cgroup:
|
|||
bad_fork_cleanup_put_domain:
|
||||
module_put(task_thread_info(p)->exec_domain->module);
|
||||
bad_fork_cleanup_count:
|
||||
put_group_info(p->group_info);
|
||||
atomic_dec(&p->user->processes);
|
||||
free_uid(p->user);
|
||||
atomic_dec(&p->cred->user->processes);
|
||||
put_cred(p->real_cred);
|
||||
put_cred(p->cred);
|
||||
bad_fork_free:
|
||||
free_task(p);
|
||||
fork_out:
|
||||
|
|
@ -1357,6 +1358,21 @@ long do_fork(unsigned long clone_flags,
|
|||
int trace = 0;
|
||||
long nr;
|
||||
|
||||
/*
|
||||
* Do some preliminary argument and permissions checking before we
|
||||
* actually start allocating stuff
|
||||
*/
|
||||
if (clone_flags & CLONE_NEWUSER) {
|
||||
if (clone_flags & CLONE_THREAD)
|
||||
return -EINVAL;
|
||||
/* hopefully this check will go away when userns support is
|
||||
* complete
|
||||
*/
|
||||
if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SETUID) ||
|
||||
!capable(CAP_SETGID))
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
/*
|
||||
* We hope to recycle these flags after 2.6.26
|
||||
*/
|
||||
|
|
@ -1465,12 +1481,10 @@ void __init proc_caches_init(void)
|
|||
fs_cachep = kmem_cache_create("fs_cache",
|
||||
sizeof(struct fs_struct), 0,
|
||||
SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);
|
||||
vm_area_cachep = kmem_cache_create("vm_area_struct",
|
||||
sizeof(struct vm_area_struct), 0,
|
||||
SLAB_PANIC, NULL);
|
||||
mm_cachep = kmem_cache_create("mm_struct",
|
||||
sizeof(struct mm_struct), ARCH_MIN_MMSTRUCT_ALIGN,
|
||||
SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);
|
||||
mmap_init();
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1589,7 +1603,7 @@ static int unshare_fd(unsigned long unshare_flags, struct files_struct **new_fdp
|
|||
* constructed. Here we are modifying the current, active,
|
||||
* task_struct.
|
||||
*/
|
||||
asmlinkage long sys_unshare(unsigned long unshare_flags)
|
||||
SYSCALL_DEFINE1(unshare, unsigned long, unshare_flags)
|
||||
{
|
||||
int err = 0;
|
||||
struct fs_struct *fs, *new_fs = NULL;
|
||||
|
|
@ -1605,8 +1619,7 @@ asmlinkage long sys_unshare(unsigned long unshare_flags)
|
|||
err = -EINVAL;
|
||||
if (unshare_flags & ~(CLONE_THREAD|CLONE_FS|CLONE_NEWNS|CLONE_SIGHAND|
|
||||
CLONE_VM|CLONE_FILES|CLONE_SYSVSEM|
|
||||
CLONE_NEWUTS|CLONE_NEWIPC|CLONE_NEWUSER|
|
||||
CLONE_NEWNET))
|
||||
CLONE_NEWUTS|CLONE_NEWIPC|CLONE_NEWNET))
|
||||
goto bad_unshare_out;
|
||||
|
||||
/*
|
||||
|
|
|
|||
450
kernel/futex.c
450
kernel/futex.c
|
|
@ -92,11 +92,12 @@ struct futex_pi_state {
|
|||
* A futex_q has a woken state, just like tasks have TASK_RUNNING.
|
||||
* It is considered woken when plist_node_empty(&q->list) || q->lock_ptr == 0.
|
||||
* The order of wakup is always to make the first condition true, then
|
||||
* wake up q->waiters, then make the second condition true.
|
||||
* wake up q->waiter, then make the second condition true.
|
||||
*/
|
||||
struct futex_q {
|
||||
struct plist_node list;
|
||||
wait_queue_head_t waiters;
|
||||
/* There can only be a single waiter */
|
||||
wait_queue_head_t waiter;
|
||||
|
||||
/* Which hash list lock to use: */
|
||||
spinlock_t *lock_ptr;
|
||||
|
|
@ -122,24 +123,6 @@ struct futex_hash_bucket {
|
|||
|
||||
static struct futex_hash_bucket futex_queues[1<<FUTEX_HASHBITS];
|
||||
|
||||
/*
|
||||
* Take mm->mmap_sem, when futex is shared
|
||||
*/
|
||||
static inline void futex_lock_mm(struct rw_semaphore *fshared)
|
||||
{
|
||||
if (fshared)
|
||||
down_read(fshared);
|
||||
}
|
||||
|
||||
/*
|
||||
* Release mm->mmap_sem, when the futex is shared
|
||||
*/
|
||||
static inline void futex_unlock_mm(struct rw_semaphore *fshared)
|
||||
{
|
||||
if (fshared)
|
||||
up_read(fshared);
|
||||
}
|
||||
|
||||
/*
|
||||
* We hash on the keys returned from get_futex_key (see below).
|
||||
*/
|
||||
|
|
@ -161,6 +144,48 @@ static inline int match_futex(union futex_key *key1, union futex_key *key2)
|
|||
&& key1->both.offset == key2->both.offset);
|
||||
}
|
||||
|
||||
/*
|
||||
* Take a reference to the resource addressed by a key.
|
||||
* Can be called while holding spinlocks.
|
||||
*
|
||||
*/
|
||||
static void get_futex_key_refs(union futex_key *key)
|
||||
{
|
||||
if (!key->both.ptr)
|
||||
return;
|
||||
|
||||
switch (key->both.offset & (FUT_OFF_INODE|FUT_OFF_MMSHARED)) {
|
||||
case FUT_OFF_INODE:
|
||||
atomic_inc(&key->shared.inode->i_count);
|
||||
break;
|
||||
case FUT_OFF_MMSHARED:
|
||||
atomic_inc(&key->private.mm->mm_count);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Drop a reference to the resource addressed by a key.
|
||||
* The hash bucket spinlock must not be held.
|
||||
*/
|
||||
static void drop_futex_key_refs(union futex_key *key)
|
||||
{
|
||||
if (!key->both.ptr) {
|
||||
/* If we're here then we tried to put a key we failed to get */
|
||||
WARN_ON_ONCE(1);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (key->both.offset & (FUT_OFF_INODE|FUT_OFF_MMSHARED)) {
|
||||
case FUT_OFF_INODE:
|
||||
iput(key->shared.inode);
|
||||
break;
|
||||
case FUT_OFF_MMSHARED:
|
||||
mmdrop(key->private.mm);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get_futex_key - Get parameters which are the keys for a futex.
|
||||
* @uaddr: virtual address of the futex
|
||||
|
|
@ -179,12 +204,10 @@ static inline int match_futex(union futex_key *key1, union futex_key *key2)
|
|||
* For other futexes, it points to ¤t->mm->mmap_sem and
|
||||
* caller must have taken the reader lock. but NOT any spinlocks.
|
||||
*/
|
||||
static int get_futex_key(u32 __user *uaddr, struct rw_semaphore *fshared,
|
||||
union futex_key *key)
|
||||
static int get_futex_key(u32 __user *uaddr, int fshared, union futex_key *key)
|
||||
{
|
||||
unsigned long address = (unsigned long)uaddr;
|
||||
struct mm_struct *mm = current->mm;
|
||||
struct vm_area_struct *vma;
|
||||
struct page *page;
|
||||
int err;
|
||||
|
||||
|
|
@ -208,100 +231,50 @@ static int get_futex_key(u32 __user *uaddr, struct rw_semaphore *fshared,
|
|||
return -EFAULT;
|
||||
key->private.mm = mm;
|
||||
key->private.address = address;
|
||||
get_futex_key_refs(key);
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* The futex is hashed differently depending on whether
|
||||
* it's in a shared or private mapping. So check vma first.
|
||||
*/
|
||||
vma = find_extend_vma(mm, address);
|
||||
if (unlikely(!vma))
|
||||
return -EFAULT;
|
||||
|
||||
/*
|
||||
* Permissions.
|
||||
*/
|
||||
if (unlikely((vma->vm_flags & (VM_IO|VM_READ)) != VM_READ))
|
||||
return (vma->vm_flags & VM_IO) ? -EPERM : -EACCES;
|
||||
again:
|
||||
err = get_user_pages_fast(address, 1, 0, &page);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
lock_page(page);
|
||||
if (!page->mapping) {
|
||||
unlock_page(page);
|
||||
put_page(page);
|
||||
goto again;
|
||||
}
|
||||
|
||||
/*
|
||||
* Private mappings are handled in a simple way.
|
||||
*
|
||||
* NOTE: When userspace waits on a MAP_SHARED mapping, even if
|
||||
* it's a read-only handle, it's expected that futexes attach to
|
||||
* the object not the particular process. Therefore we use
|
||||
* VM_MAYSHARE here, not VM_SHARED which is restricted to shared
|
||||
* mappings of _writable_ handles.
|
||||
* the object not the particular process.
|
||||
*/
|
||||
if (likely(!(vma->vm_flags & VM_MAYSHARE))) {
|
||||
key->both.offset |= FUT_OFF_MMSHARED; /* reference taken on mm */
|
||||
if (PageAnon(page)) {
|
||||
key->both.offset |= FUT_OFF_MMSHARED; /* ref taken on mm */
|
||||
key->private.mm = mm;
|
||||
key->private.address = address;
|
||||
return 0;
|
||||
} else {
|
||||
key->both.offset |= FUT_OFF_INODE; /* inode-based key */
|
||||
key->shared.inode = page->mapping->host;
|
||||
key->shared.pgoff = page->index;
|
||||
}
|
||||
|
||||
/*
|
||||
* Linear file mappings are also simple.
|
||||
*/
|
||||
key->shared.inode = vma->vm_file->f_path.dentry->d_inode;
|
||||
key->both.offset |= FUT_OFF_INODE; /* inode-based key. */
|
||||
if (likely(!(vma->vm_flags & VM_NONLINEAR))) {
|
||||
key->shared.pgoff = (((address - vma->vm_start) >> PAGE_SHIFT)
|
||||
+ vma->vm_pgoff);
|
||||
return 0;
|
||||
}
|
||||
get_futex_key_refs(key);
|
||||
|
||||
/*
|
||||
* We could walk the page table to read the non-linear
|
||||
* pte, and get the page index without fetching the page
|
||||
* from swap. But that's a lot of code to duplicate here
|
||||
* for a rare case, so we simply fetch the page.
|
||||
*/
|
||||
err = get_user_pages(current, mm, address, 1, 0, 0, &page, NULL);
|
||||
if (err >= 0) {
|
||||
key->shared.pgoff =
|
||||
page->index << (PAGE_CACHE_SHIFT - PAGE_SHIFT);
|
||||
put_page(page);
|
||||
return 0;
|
||||
}
|
||||
return err;
|
||||
unlock_page(page);
|
||||
put_page(page);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Take a reference to the resource addressed by a key.
|
||||
* Can be called while holding spinlocks.
|
||||
*
|
||||
*/
|
||||
static void get_futex_key_refs(union futex_key *key)
|
||||
static inline
|
||||
void put_futex_key(int fshared, union futex_key *key)
|
||||
{
|
||||
if (key->both.ptr == NULL)
|
||||
return;
|
||||
switch (key->both.offset & (FUT_OFF_INODE|FUT_OFF_MMSHARED)) {
|
||||
case FUT_OFF_INODE:
|
||||
atomic_inc(&key->shared.inode->i_count);
|
||||
break;
|
||||
case FUT_OFF_MMSHARED:
|
||||
atomic_inc(&key->private.mm->mm_count);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Drop a reference to the resource addressed by a key.
|
||||
* The hash bucket spinlock must not be held.
|
||||
*/
|
||||
static void drop_futex_key_refs(union futex_key *key)
|
||||
{
|
||||
if (!key->both.ptr)
|
||||
return;
|
||||
switch (key->both.offset & (FUT_OFF_INODE|FUT_OFF_MMSHARED)) {
|
||||
case FUT_OFF_INODE:
|
||||
iput(key->shared.inode);
|
||||
break;
|
||||
case FUT_OFF_MMSHARED:
|
||||
mmdrop(key->private.mm);
|
||||
break;
|
||||
}
|
||||
drop_futex_key_refs(key);
|
||||
}
|
||||
|
||||
static u32 cmpxchg_futex_value_locked(u32 __user *uaddr, u32 uval, u32 newval)
|
||||
|
|
@ -328,10 +301,8 @@ static int get_futex_value_locked(u32 *dest, u32 __user *from)
|
|||
|
||||
/*
|
||||
* Fault handling.
|
||||
* if fshared is non NULL, current->mm->mmap_sem is already held
|
||||
*/
|
||||
static int futex_handle_fault(unsigned long address,
|
||||
struct rw_semaphore *fshared, int attempt)
|
||||
static int futex_handle_fault(unsigned long address, int attempt)
|
||||
{
|
||||
struct vm_area_struct * vma;
|
||||
struct mm_struct *mm = current->mm;
|
||||
|
|
@ -340,8 +311,7 @@ static int futex_handle_fault(unsigned long address,
|
|||
if (attempt > 2)
|
||||
return ret;
|
||||
|
||||
if (!fshared)
|
||||
down_read(&mm->mmap_sem);
|
||||
down_read(&mm->mmap_sem);
|
||||
vma = find_vma(mm, address);
|
||||
if (vma && address >= vma->vm_start &&
|
||||
(vma->vm_flags & VM_WRITE)) {
|
||||
|
|
@ -361,8 +331,7 @@ static int futex_handle_fault(unsigned long address,
|
|||
current->min_flt++;
|
||||
}
|
||||
}
|
||||
if (!fshared)
|
||||
up_read(&mm->mmap_sem);
|
||||
up_read(&mm->mmap_sem);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -385,6 +354,7 @@ static int refill_pi_state_cache(void)
|
|||
/* pi_mutex gets initialized later */
|
||||
pi_state->owner = NULL;
|
||||
atomic_set(&pi_state->refcount, 1);
|
||||
pi_state->key = FUTEX_KEY_INIT;
|
||||
|
||||
current->pi_state_cache = pi_state;
|
||||
|
||||
|
|
@ -439,13 +409,20 @@ static void free_pi_state(struct futex_pi_state *pi_state)
|
|||
static struct task_struct * futex_find_get_task(pid_t pid)
|
||||
{
|
||||
struct task_struct *p;
|
||||
const struct cred *cred = current_cred(), *pcred;
|
||||
|
||||
rcu_read_lock();
|
||||
p = find_task_by_vpid(pid);
|
||||
if (!p || ((current->euid != p->euid) && (current->euid != p->uid)))
|
||||
if (!p) {
|
||||
p = ERR_PTR(-ESRCH);
|
||||
else
|
||||
get_task_struct(p);
|
||||
} else {
|
||||
pcred = __task_cred(p);
|
||||
if (cred->euid != pcred->euid &&
|
||||
cred->euid != pcred->uid)
|
||||
p = ERR_PTR(-ESRCH);
|
||||
else
|
||||
get_task_struct(p);
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
|
|
@ -462,7 +439,7 @@ void exit_pi_state_list(struct task_struct *curr)
|
|||
struct list_head *next, *head = &curr->pi_state_list;
|
||||
struct futex_pi_state *pi_state;
|
||||
struct futex_hash_bucket *hb;
|
||||
union futex_key key;
|
||||
union futex_key key = FUTEX_KEY_INIT;
|
||||
|
||||
if (!futex_cmpxchg_enabled)
|
||||
return;
|
||||
|
|
@ -607,7 +584,7 @@ static void wake_futex(struct futex_q *q)
|
|||
* The lock in wake_up_all() is a crucial memory barrier after the
|
||||
* plist_del() and also before assigning to q->lock_ptr.
|
||||
*/
|
||||
wake_up_all(&q->waiters);
|
||||
wake_up(&q->waiter);
|
||||
/*
|
||||
* The waiting task can free the futex_q as soon as this is written,
|
||||
* without taking any locks. This must come last.
|
||||
|
|
@ -719,20 +696,17 @@ double_lock_hb(struct futex_hash_bucket *hb1, struct futex_hash_bucket *hb2)
|
|||
* Wake up all waiters hashed on the physical page that is mapped
|
||||
* to this virtual address:
|
||||
*/
|
||||
static int futex_wake(u32 __user *uaddr, struct rw_semaphore *fshared,
|
||||
int nr_wake, u32 bitset)
|
||||
static int futex_wake(u32 __user *uaddr, int fshared, int nr_wake, u32 bitset)
|
||||
{
|
||||
struct futex_hash_bucket *hb;
|
||||
struct futex_q *this, *next;
|
||||
struct plist_head *head;
|
||||
union futex_key key;
|
||||
union futex_key key = FUTEX_KEY_INIT;
|
||||
int ret;
|
||||
|
||||
if (!bitset)
|
||||
return -EINVAL;
|
||||
|
||||
futex_lock_mm(fshared);
|
||||
|
||||
ret = get_futex_key(uaddr, fshared, &key);
|
||||
if (unlikely(ret != 0))
|
||||
goto out;
|
||||
|
|
@ -759,8 +733,8 @@ static int futex_wake(u32 __user *uaddr, struct rw_semaphore *fshared,
|
|||
}
|
||||
|
||||
spin_unlock(&hb->lock);
|
||||
put_futex_key(fshared, &key);
|
||||
out:
|
||||
futex_unlock_mm(fshared);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -769,25 +743,22 @@ out:
|
|||
* to this virtual address:
|
||||
*/
|
||||
static int
|
||||
futex_wake_op(u32 __user *uaddr1, struct rw_semaphore *fshared,
|
||||
u32 __user *uaddr2,
|
||||
futex_wake_op(u32 __user *uaddr1, int fshared, u32 __user *uaddr2,
|
||||
int nr_wake, int nr_wake2, int op)
|
||||
{
|
||||
union futex_key key1, key2;
|
||||
union futex_key key1 = FUTEX_KEY_INIT, key2 = FUTEX_KEY_INIT;
|
||||
struct futex_hash_bucket *hb1, *hb2;
|
||||
struct plist_head *head;
|
||||
struct futex_q *this, *next;
|
||||
int ret, op_ret, attempt = 0;
|
||||
|
||||
retryfull:
|
||||
futex_lock_mm(fshared);
|
||||
|
||||
ret = get_futex_key(uaddr1, fshared, &key1);
|
||||
if (unlikely(ret != 0))
|
||||
goto out;
|
||||
ret = get_futex_key(uaddr2, fshared, &key2);
|
||||
if (unlikely(ret != 0))
|
||||
goto out;
|
||||
goto out_put_key1;
|
||||
|
||||
hb1 = hash_futex(&key1);
|
||||
hb2 = hash_futex(&key2);
|
||||
|
|
@ -809,12 +780,12 @@ retry:
|
|||
* but we might get them from range checking
|
||||
*/
|
||||
ret = op_ret;
|
||||
goto out;
|
||||
goto out_put_keys;
|
||||
#endif
|
||||
|
||||
if (unlikely(op_ret != -EFAULT)) {
|
||||
ret = op_ret;
|
||||
goto out;
|
||||
goto out_put_keys;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -826,18 +797,12 @@ retry:
|
|||
*/
|
||||
if (attempt++) {
|
||||
ret = futex_handle_fault((unsigned long)uaddr2,
|
||||
fshared, attempt);
|
||||
attempt);
|
||||
if (ret)
|
||||
goto out;
|
||||
goto out_put_keys;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we would have faulted, release mmap_sem,
|
||||
* fault it in and start all over again.
|
||||
*/
|
||||
futex_unlock_mm(fshared);
|
||||
|
||||
ret = get_user(dummy, uaddr2);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
@ -872,9 +837,11 @@ retry:
|
|||
spin_unlock(&hb1->lock);
|
||||
if (hb1 != hb2)
|
||||
spin_unlock(&hb2->lock);
|
||||
out_put_keys:
|
||||
put_futex_key(fshared, &key2);
|
||||
out_put_key1:
|
||||
put_futex_key(fshared, &key1);
|
||||
out:
|
||||
futex_unlock_mm(fshared);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -882,25 +849,22 @@ out:
|
|||
* Requeue all waiters hashed on one physical page to another
|
||||
* physical page.
|
||||
*/
|
||||
static int futex_requeue(u32 __user *uaddr1, struct rw_semaphore *fshared,
|
||||
u32 __user *uaddr2,
|
||||
static int futex_requeue(u32 __user *uaddr1, int fshared, u32 __user *uaddr2,
|
||||
int nr_wake, int nr_requeue, u32 *cmpval)
|
||||
{
|
||||
union futex_key key1, key2;
|
||||
union futex_key key1 = FUTEX_KEY_INIT, key2 = FUTEX_KEY_INIT;
|
||||
struct futex_hash_bucket *hb1, *hb2;
|
||||
struct plist_head *head1;
|
||||
struct futex_q *this, *next;
|
||||
int ret, drop_count = 0;
|
||||
|
||||
retry:
|
||||
futex_lock_mm(fshared);
|
||||
|
||||
retry:
|
||||
ret = get_futex_key(uaddr1, fshared, &key1);
|
||||
if (unlikely(ret != 0))
|
||||
goto out;
|
||||
ret = get_futex_key(uaddr2, fshared, &key2);
|
||||
if (unlikely(ret != 0))
|
||||
goto out;
|
||||
goto out_put_key1;
|
||||
|
||||
hb1 = hash_futex(&key1);
|
||||
hb2 = hash_futex(&key2);
|
||||
|
|
@ -917,18 +881,12 @@ static int futex_requeue(u32 __user *uaddr1, struct rw_semaphore *fshared,
|
|||
if (hb1 != hb2)
|
||||
spin_unlock(&hb2->lock);
|
||||
|
||||
/*
|
||||
* If we would have faulted, release mmap_sem, fault
|
||||
* it in and start all over again.
|
||||
*/
|
||||
futex_unlock_mm(fshared);
|
||||
|
||||
ret = get_user(curval, uaddr1);
|
||||
|
||||
if (!ret)
|
||||
goto retry;
|
||||
|
||||
return ret;
|
||||
goto out_put_keys;
|
||||
}
|
||||
if (curval != *cmpval) {
|
||||
ret = -EAGAIN;
|
||||
|
|
@ -973,8 +931,11 @@ out_unlock:
|
|||
while (--drop_count >= 0)
|
||||
drop_futex_key_refs(&key1);
|
||||
|
||||
out_put_keys:
|
||||
put_futex_key(fshared, &key2);
|
||||
out_put_key1:
|
||||
put_futex_key(fshared, &key1);
|
||||
out:
|
||||
futex_unlock_mm(fshared);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -983,7 +944,7 @@ static inline struct futex_hash_bucket *queue_lock(struct futex_q *q)
|
|||
{
|
||||
struct futex_hash_bucket *hb;
|
||||
|
||||
init_waitqueue_head(&q->waiters);
|
||||
init_waitqueue_head(&q->waiter);
|
||||
|
||||
get_futex_key_refs(&q->key);
|
||||
hb = hash_futex(&q->key);
|
||||
|
|
@ -1035,7 +996,7 @@ static int unqueue_me(struct futex_q *q)
|
|||
int ret = 0;
|
||||
|
||||
/* In the common case we don't take the spinlock, which is nice. */
|
||||
retry:
|
||||
retry:
|
||||
lock_ptr = q->lock_ptr;
|
||||
barrier();
|
||||
if (lock_ptr != NULL) {
|
||||
|
|
@ -1096,8 +1057,7 @@ static void unqueue_me_pi(struct futex_q *q)
|
|||
* private futexes.
|
||||
*/
|
||||
static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q,
|
||||
struct task_struct *newowner,
|
||||
struct rw_semaphore *fshared)
|
||||
struct task_struct *newowner, int fshared)
|
||||
{
|
||||
u32 newtid = task_pid_vnr(newowner) | FUTEX_WAITERS;
|
||||
struct futex_pi_state *pi_state = q->pi_state;
|
||||
|
|
@ -1176,7 +1136,7 @@ retry:
|
|||
handle_fault:
|
||||
spin_unlock(q->lock_ptr);
|
||||
|
||||
ret = futex_handle_fault((unsigned long)uaddr, fshared, attempt++);
|
||||
ret = futex_handle_fault((unsigned long)uaddr, attempt++);
|
||||
|
||||
spin_lock(q->lock_ptr);
|
||||
|
||||
|
|
@ -1196,12 +1156,13 @@ handle_fault:
|
|||
* In case we must use restart_block to restart a futex_wait,
|
||||
* we encode in the 'flags' shared capability
|
||||
*/
|
||||
#define FLAGS_SHARED 1
|
||||
#define FLAGS_SHARED 0x01
|
||||
#define FLAGS_CLOCKRT 0x02
|
||||
|
||||
static long futex_wait_restart(struct restart_block *restart);
|
||||
|
||||
static int futex_wait(u32 __user *uaddr, struct rw_semaphore *fshared,
|
||||
u32 val, ktime_t *abs_time, u32 bitset)
|
||||
static int futex_wait(u32 __user *uaddr, int fshared,
|
||||
u32 val, ktime_t *abs_time, u32 bitset, int clockrt)
|
||||
{
|
||||
struct task_struct *curr = current;
|
||||
DECLARE_WAITQUEUE(wait, curr);
|
||||
|
|
@ -1217,12 +1178,11 @@ static int futex_wait(u32 __user *uaddr, struct rw_semaphore *fshared,
|
|||
|
||||
q.pi_state = NULL;
|
||||
q.bitset = bitset;
|
||||
retry:
|
||||
futex_lock_mm(fshared);
|
||||
|
||||
retry:
|
||||
q.key = FUTEX_KEY_INIT;
|
||||
ret = get_futex_key(uaddr, fshared, &q.key);
|
||||
if (unlikely(ret != 0))
|
||||
goto out_release_sem;
|
||||
goto out;
|
||||
|
||||
hb = queue_lock(&q);
|
||||
|
||||
|
|
@ -1250,12 +1210,7 @@ static int futex_wait(u32 __user *uaddr, struct rw_semaphore *fshared,
|
|||
|
||||
if (unlikely(ret)) {
|
||||
queue_unlock(&q, hb);
|
||||
|
||||
/*
|
||||
* If we would have faulted, release mmap_sem, fault it in and
|
||||
* start all over again.
|
||||
*/
|
||||
futex_unlock_mm(fshared);
|
||||
put_futex_key(fshared, &q.key);
|
||||
|
||||
ret = get_user(uval, uaddr);
|
||||
|
||||
|
|
@ -1265,17 +1220,11 @@ static int futex_wait(u32 __user *uaddr, struct rw_semaphore *fshared,
|
|||
}
|
||||
ret = -EWOULDBLOCK;
|
||||
if (uval != val)
|
||||
goto out_unlock_release_sem;
|
||||
goto out_unlock_put_key;
|
||||
|
||||
/* Only actually queue if *uaddr contained val. */
|
||||
queue_me(&q, hb);
|
||||
|
||||
/*
|
||||
* Now the futex is queued and we have checked the data, we
|
||||
* don't want to hold mmap_sem while we sleep.
|
||||
*/
|
||||
futex_unlock_mm(fshared);
|
||||
|
||||
/*
|
||||
* There might have been scheduling since the queue_me(), as we
|
||||
* cannot hold a spinlock across the get_user() in case it
|
||||
|
|
@ -1287,7 +1236,7 @@ static int futex_wait(u32 __user *uaddr, struct rw_semaphore *fshared,
|
|||
|
||||
/* add_wait_queue is the barrier after __set_current_state. */
|
||||
__set_current_state(TASK_INTERRUPTIBLE);
|
||||
add_wait_queue(&q.waiters, &wait);
|
||||
add_wait_queue(&q.waiter, &wait);
|
||||
/*
|
||||
* !plist_node_empty() is safe here without any lock.
|
||||
* q.lock_ptr != 0 is not safe, because of ordering against wakeup.
|
||||
|
|
@ -1300,8 +1249,10 @@ static int futex_wait(u32 __user *uaddr, struct rw_semaphore *fshared,
|
|||
slack = current->timer_slack_ns;
|
||||
if (rt_task(current))
|
||||
slack = 0;
|
||||
hrtimer_init_on_stack(&t.timer, CLOCK_MONOTONIC,
|
||||
HRTIMER_MODE_ABS);
|
||||
hrtimer_init_on_stack(&t.timer,
|
||||
clockrt ? CLOCK_REALTIME :
|
||||
CLOCK_MONOTONIC,
|
||||
HRTIMER_MODE_ABS);
|
||||
hrtimer_init_sleeper(&t, current);
|
||||
hrtimer_set_expires_range_ns(&t.timer, *abs_time, slack);
|
||||
|
||||
|
|
@ -1356,14 +1307,16 @@ static int futex_wait(u32 __user *uaddr, struct rw_semaphore *fshared,
|
|||
|
||||
if (fshared)
|
||||
restart->futex.flags |= FLAGS_SHARED;
|
||||
if (clockrt)
|
||||
restart->futex.flags |= FLAGS_CLOCKRT;
|
||||
return -ERESTART_RESTARTBLOCK;
|
||||
}
|
||||
|
||||
out_unlock_release_sem:
|
||||
out_unlock_put_key:
|
||||
queue_unlock(&q, hb);
|
||||
put_futex_key(fshared, &q.key);
|
||||
|
||||
out_release_sem:
|
||||
futex_unlock_mm(fshared);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -1371,15 +1324,16 @@ static int futex_wait(u32 __user *uaddr, struct rw_semaphore *fshared,
|
|||
static long futex_wait_restart(struct restart_block *restart)
|
||||
{
|
||||
u32 __user *uaddr = (u32 __user *)restart->futex.uaddr;
|
||||
struct rw_semaphore *fshared = NULL;
|
||||
int fshared = 0;
|
||||
ktime_t t;
|
||||
|
||||
t.tv64 = restart->futex.time;
|
||||
restart->fn = do_no_restart_syscall;
|
||||
if (restart->futex.flags & FLAGS_SHARED)
|
||||
fshared = ¤t->mm->mmap_sem;
|
||||
fshared = 1;
|
||||
return (long)futex_wait(uaddr, fshared, restart->futex.val, &t,
|
||||
restart->futex.bitset);
|
||||
restart->futex.bitset,
|
||||
restart->futex.flags & FLAGS_CLOCKRT);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1389,7 +1343,7 @@ static long futex_wait_restart(struct restart_block *restart)
|
|||
* if there are waiters then it will block, it does PI, etc. (Due to
|
||||
* races the kernel might see a 0 value of the futex too.)
|
||||
*/
|
||||
static int futex_lock_pi(u32 __user *uaddr, struct rw_semaphore *fshared,
|
||||
static int futex_lock_pi(u32 __user *uaddr, int fshared,
|
||||
int detect, ktime_t *time, int trylock)
|
||||
{
|
||||
struct hrtimer_sleeper timeout, *to = NULL;
|
||||
|
|
@ -1411,17 +1365,16 @@ static int futex_lock_pi(u32 __user *uaddr, struct rw_semaphore *fshared,
|
|||
}
|
||||
|
||||
q.pi_state = NULL;
|
||||
retry:
|
||||
futex_lock_mm(fshared);
|
||||
|
||||
retry:
|
||||
q.key = FUTEX_KEY_INIT;
|
||||
ret = get_futex_key(uaddr, fshared, &q.key);
|
||||
if (unlikely(ret != 0))
|
||||
goto out_release_sem;
|
||||
goto out;
|
||||
|
||||
retry_unlocked:
|
||||
retry_unlocked:
|
||||
hb = queue_lock(&q);
|
||||
|
||||
retry_locked:
|
||||
retry_locked:
|
||||
ret = lock_taken = 0;
|
||||
|
||||
/*
|
||||
|
|
@ -1442,14 +1395,14 @@ static int futex_lock_pi(u32 __user *uaddr, struct rw_semaphore *fshared,
|
|||
*/
|
||||
if (unlikely((curval & FUTEX_TID_MASK) == task_pid_vnr(current))) {
|
||||
ret = -EDEADLK;
|
||||
goto out_unlock_release_sem;
|
||||
goto out_unlock_put_key;
|
||||
}
|
||||
|
||||
/*
|
||||
* Surprise - we got the lock. Just return to userspace:
|
||||
*/
|
||||
if (unlikely(!curval))
|
||||
goto out_unlock_release_sem;
|
||||
goto out_unlock_put_key;
|
||||
|
||||
uval = curval;
|
||||
|
||||
|
|
@ -1485,7 +1438,7 @@ static int futex_lock_pi(u32 __user *uaddr, struct rw_semaphore *fshared,
|
|||
* We took the lock due to owner died take over.
|
||||
*/
|
||||
if (unlikely(lock_taken))
|
||||
goto out_unlock_release_sem;
|
||||
goto out_unlock_put_key;
|
||||
|
||||
/*
|
||||
* We dont have the lock. Look up the PI state (or create it if
|
||||
|
|
@ -1502,7 +1455,6 @@ static int futex_lock_pi(u32 __user *uaddr, struct rw_semaphore *fshared,
|
|||
* exit to complete.
|
||||
*/
|
||||
queue_unlock(&q, hb);
|
||||
futex_unlock_mm(fshared);
|
||||
cond_resched();
|
||||
goto retry;
|
||||
|
||||
|
|
@ -1525,7 +1477,7 @@ static int futex_lock_pi(u32 __user *uaddr, struct rw_semaphore *fshared,
|
|||
goto retry_locked;
|
||||
}
|
||||
default:
|
||||
goto out_unlock_release_sem;
|
||||
goto out_unlock_put_key;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1534,12 +1486,6 @@ static int futex_lock_pi(u32 __user *uaddr, struct rw_semaphore *fshared,
|
|||
*/
|
||||
queue_me(&q, hb);
|
||||
|
||||
/*
|
||||
* Now the futex is queued and we have checked the data, we
|
||||
* don't want to hold mmap_sem while we sleep.
|
||||
*/
|
||||
futex_unlock_mm(fshared);
|
||||
|
||||
WARN_ON(!q.pi_state);
|
||||
/*
|
||||
* Block on the PI mutex:
|
||||
|
|
@ -1552,7 +1498,6 @@ static int futex_lock_pi(u32 __user *uaddr, struct rw_semaphore *fshared,
|
|||
ret = ret ? 0 : -EWOULDBLOCK;
|
||||
}
|
||||
|
||||
futex_lock_mm(fshared);
|
||||
spin_lock(q.lock_ptr);
|
||||
|
||||
if (!ret) {
|
||||
|
|
@ -1618,44 +1563,40 @@ static int futex_lock_pi(u32 __user *uaddr, struct rw_semaphore *fshared,
|
|||
|
||||
/* Unqueue and drop the lock */
|
||||
unqueue_me_pi(&q);
|
||||
futex_unlock_mm(fshared);
|
||||
|
||||
if (to)
|
||||
destroy_hrtimer_on_stack(&to->timer);
|
||||
return ret != -EINTR ? ret : -ERESTARTNOINTR;
|
||||
|
||||
out_unlock_release_sem:
|
||||
out_unlock_put_key:
|
||||
queue_unlock(&q, hb);
|
||||
|
||||
out_release_sem:
|
||||
futex_unlock_mm(fshared);
|
||||
out_put_key:
|
||||
put_futex_key(fshared, &q.key);
|
||||
out:
|
||||
if (to)
|
||||
destroy_hrtimer_on_stack(&to->timer);
|
||||
return ret;
|
||||
|
||||
uaddr_faulted:
|
||||
uaddr_faulted:
|
||||
/*
|
||||
* We have to r/w *(int __user *)uaddr, but we can't modify it
|
||||
* non-atomically. Therefore, if get_user below is not
|
||||
* enough, we need to handle the fault ourselves, while
|
||||
* still holding the mmap_sem.
|
||||
*
|
||||
* ... and hb->lock. :-) --ANK
|
||||
* We have to r/w *(int __user *)uaddr, and we have to modify it
|
||||
* atomically. Therefore, if we continue to fault after get_user()
|
||||
* below, we need to handle the fault ourselves, while still holding
|
||||
* the mmap_sem. This can occur if the uaddr is under contention as
|
||||
* we have to drop the mmap_sem in order to call get_user().
|
||||
*/
|
||||
queue_unlock(&q, hb);
|
||||
|
||||
if (attempt++) {
|
||||
ret = futex_handle_fault((unsigned long)uaddr, fshared,
|
||||
attempt);
|
||||
ret = futex_handle_fault((unsigned long)uaddr, attempt);
|
||||
if (ret)
|
||||
goto out_release_sem;
|
||||
goto out_put_key;
|
||||
goto retry_unlocked;
|
||||
}
|
||||
|
||||
futex_unlock_mm(fshared);
|
||||
|
||||
ret = get_user(uval, uaddr);
|
||||
if (!ret && (uval != -EFAULT))
|
||||
if (!ret)
|
||||
goto retry;
|
||||
|
||||
if (to)
|
||||
|
|
@ -1668,13 +1609,13 @@ static int futex_lock_pi(u32 __user *uaddr, struct rw_semaphore *fshared,
|
|||
* This is the in-kernel slowpath: we look up the PI state (if any),
|
||||
* and do the rt-mutex unlock.
|
||||
*/
|
||||
static int futex_unlock_pi(u32 __user *uaddr, struct rw_semaphore *fshared)
|
||||
static int futex_unlock_pi(u32 __user *uaddr, int fshared)
|
||||
{
|
||||
struct futex_hash_bucket *hb;
|
||||
struct futex_q *this, *next;
|
||||
u32 uval;
|
||||
struct plist_head *head;
|
||||
union futex_key key;
|
||||
union futex_key key = FUTEX_KEY_INIT;
|
||||
int ret, attempt = 0;
|
||||
|
||||
retry:
|
||||
|
|
@ -1685,10 +1626,6 @@ retry:
|
|||
*/
|
||||
if ((uval & FUTEX_TID_MASK) != task_pid_vnr(current))
|
||||
return -EPERM;
|
||||
/*
|
||||
* First take all the futex related locks:
|
||||
*/
|
||||
futex_lock_mm(fshared);
|
||||
|
||||
ret = get_futex_key(uaddr, fshared, &key);
|
||||
if (unlikely(ret != 0))
|
||||
|
|
@ -1746,35 +1683,31 @@ retry_unlocked:
|
|||
|
||||
out_unlock:
|
||||
spin_unlock(&hb->lock);
|
||||
out:
|
||||
futex_unlock_mm(fshared);
|
||||
put_futex_key(fshared, &key);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
|
||||
pi_faulted:
|
||||
/*
|
||||
* We have to r/w *(int __user *)uaddr, but we can't modify it
|
||||
* non-atomically. Therefore, if get_user below is not
|
||||
* enough, we need to handle the fault ourselves, while
|
||||
* still holding the mmap_sem.
|
||||
*
|
||||
* ... and hb->lock. --ANK
|
||||
* We have to r/w *(int __user *)uaddr, and we have to modify it
|
||||
* atomically. Therefore, if we continue to fault after get_user()
|
||||
* below, we need to handle the fault ourselves, while still holding
|
||||
* the mmap_sem. This can occur if the uaddr is under contention as
|
||||
* we have to drop the mmap_sem in order to call get_user().
|
||||
*/
|
||||
spin_unlock(&hb->lock);
|
||||
|
||||
if (attempt++) {
|
||||
ret = futex_handle_fault((unsigned long)uaddr, fshared,
|
||||
attempt);
|
||||
ret = futex_handle_fault((unsigned long)uaddr, attempt);
|
||||
if (ret)
|
||||
goto out;
|
||||
uval = 0;
|
||||
goto retry_unlocked;
|
||||
}
|
||||
|
||||
futex_unlock_mm(fshared);
|
||||
|
||||
ret = get_user(uval, uaddr);
|
||||
if (!ret && (uval != -EFAULT))
|
||||
if (!ret)
|
||||
goto retry;
|
||||
|
||||
return ret;
|
||||
|
|
@ -1800,9 +1733,8 @@ pi_faulted:
|
|||
* @head: pointer to the list-head
|
||||
* @len: length of the list-head, as userspace expects
|
||||
*/
|
||||
asmlinkage long
|
||||
sys_set_robust_list(struct robust_list_head __user *head,
|
||||
size_t len)
|
||||
SYSCALL_DEFINE2(set_robust_list, struct robust_list_head __user *, head,
|
||||
size_t, len)
|
||||
{
|
||||
if (!futex_cmpxchg_enabled)
|
||||
return -ENOSYS;
|
||||
|
|
@ -1823,12 +1755,13 @@ sys_set_robust_list(struct robust_list_head __user *head,
|
|||
* @head_ptr: pointer to a list-head pointer, the kernel fills it in
|
||||
* @len_ptr: pointer to a length field, the kernel fills in the header size
|
||||
*/
|
||||
asmlinkage long
|
||||
sys_get_robust_list(int pid, struct robust_list_head __user * __user *head_ptr,
|
||||
size_t __user *len_ptr)
|
||||
SYSCALL_DEFINE3(get_robust_list, int, pid,
|
||||
struct robust_list_head __user * __user *, head_ptr,
|
||||
size_t __user *, len_ptr)
|
||||
{
|
||||
struct robust_list_head __user *head;
|
||||
unsigned long ret;
|
||||
const struct cred *cred = current_cred(), *pcred;
|
||||
|
||||
if (!futex_cmpxchg_enabled)
|
||||
return -ENOSYS;
|
||||
|
|
@ -1844,8 +1777,10 @@ sys_get_robust_list(int pid, struct robust_list_head __user * __user *head_ptr,
|
|||
if (!p)
|
||||
goto err_unlock;
|
||||
ret = -EPERM;
|
||||
if ((current->euid != p->euid) && (current->euid != p->uid) &&
|
||||
!capable(CAP_SYS_PTRACE))
|
||||
pcred = __task_cred(p);
|
||||
if (cred->euid != pcred->euid &&
|
||||
cred->euid != pcred->uid &&
|
||||
!capable(CAP_SYS_PTRACE))
|
||||
goto err_unlock;
|
||||
head = p->robust_list;
|
||||
rcu_read_unlock();
|
||||
|
|
@ -1898,8 +1833,7 @@ retry:
|
|||
* PI futexes happens in exit_pi_state():
|
||||
*/
|
||||
if (!pi && (uval & FUTEX_WAITERS))
|
||||
futex_wake(uaddr, &curr->mm->mmap_sem, 1,
|
||||
FUTEX_BITSET_MATCH_ANY);
|
||||
futex_wake(uaddr, 1, 1, FUTEX_BITSET_MATCH_ANY);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1993,18 +1927,22 @@ void exit_robust_list(struct task_struct *curr)
|
|||
long do_futex(u32 __user *uaddr, int op, u32 val, ktime_t *timeout,
|
||||
u32 __user *uaddr2, u32 val2, u32 val3)
|
||||
{
|
||||
int ret = -ENOSYS;
|
||||
int clockrt, ret = -ENOSYS;
|
||||
int cmd = op & FUTEX_CMD_MASK;
|
||||
struct rw_semaphore *fshared = NULL;
|
||||
int fshared = 0;
|
||||
|
||||
if (!(op & FUTEX_PRIVATE_FLAG))
|
||||
fshared = ¤t->mm->mmap_sem;
|
||||
fshared = 1;
|
||||
|
||||
clockrt = op & FUTEX_CLOCK_REALTIME;
|
||||
if (clockrt && cmd != FUTEX_WAIT_BITSET)
|
||||
return -ENOSYS;
|
||||
|
||||
switch (cmd) {
|
||||
case FUTEX_WAIT:
|
||||
val3 = FUTEX_BITSET_MATCH_ANY;
|
||||
case FUTEX_WAIT_BITSET:
|
||||
ret = futex_wait(uaddr, fshared, val, timeout, val3);
|
||||
ret = futex_wait(uaddr, fshared, val, timeout, val3, clockrt);
|
||||
break;
|
||||
case FUTEX_WAKE:
|
||||
val3 = FUTEX_BITSET_MATCH_ANY;
|
||||
|
|
@ -2039,9 +1977,9 @@ long do_futex(u32 __user *uaddr, int op, u32 val, ktime_t *timeout,
|
|||
}
|
||||
|
||||
|
||||
asmlinkage long sys_futex(u32 __user *uaddr, int op, u32 val,
|
||||
struct timespec __user *utime, u32 __user *uaddr2,
|
||||
u32 val3)
|
||||
SYSCALL_DEFINE6(futex, u32 __user *, uaddr, int, op, u32, val,
|
||||
struct timespec __user *, utime, u32 __user *, uaddr2,
|
||||
u32, val3)
|
||||
{
|
||||
struct timespec ts;
|
||||
ktime_t t, *tp = NULL;
|
||||
|
|
|
|||
|
|
@ -135,6 +135,7 @@ compat_sys_get_robust_list(int pid, compat_uptr_t __user *head_ptr,
|
|||
{
|
||||
struct compat_robust_list_head __user *head;
|
||||
unsigned long ret;
|
||||
const struct cred *cred = current_cred(), *pcred;
|
||||
|
||||
if (!futex_cmpxchg_enabled)
|
||||
return -ENOSYS;
|
||||
|
|
@ -150,8 +151,10 @@ compat_sys_get_robust_list(int pid, compat_uptr_t __user *head_ptr,
|
|||
if (!p)
|
||||
goto err_unlock;
|
||||
ret = -EPERM;
|
||||
if ((current->euid != p->euid) && (current->euid != p->uid) &&
|
||||
!capable(CAP_SYS_PTRACE))
|
||||
pcred = __task_cred(p);
|
||||
if (cred->euid != pcred->euid &&
|
||||
cred->euid != pcred->uid &&
|
||||
!capable(CAP_SYS_PTRACE))
|
||||
goto err_unlock;
|
||||
head = p->compat_robust_list;
|
||||
read_unlock(&tasklist_lock);
|
||||
|
|
|
|||
394
kernel/hrtimer.c
394
kernel/hrtimer.c
|
|
@ -32,7 +32,6 @@
|
|||
*/
|
||||
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/hrtimer.h>
|
||||
|
|
@ -442,22 +441,6 @@ static inline void debug_hrtimer_activate(struct hrtimer *timer) { }
|
|||
static inline void debug_hrtimer_deactivate(struct hrtimer *timer) { }
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Check, whether the timer is on the callback pending list
|
||||
*/
|
||||
static inline int hrtimer_cb_pending(const struct hrtimer *timer)
|
||||
{
|
||||
return timer->state & HRTIMER_STATE_PENDING;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove a timer from the callback pending list
|
||||
*/
|
||||
static inline void hrtimer_remove_cb_pending(struct hrtimer *timer)
|
||||
{
|
||||
list_del_init(&timer->cb_entry);
|
||||
}
|
||||
|
||||
/* High resolution timer related functions */
|
||||
#ifdef CONFIG_HIGH_RES_TIMERS
|
||||
|
||||
|
|
@ -651,6 +634,7 @@ static inline void hrtimer_init_timer_hres(struct hrtimer *timer)
|
|||
{
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* When High resolution timers are active, try to reprogram. Note, that in case
|
||||
* the state has HRTIMER_STATE_CALLBACK set, no reprogramming and no expiry
|
||||
|
|
@ -661,31 +645,10 @@ static inline int hrtimer_enqueue_reprogram(struct hrtimer *timer,
|
|||
struct hrtimer_clock_base *base)
|
||||
{
|
||||
if (base->cpu_base->hres_active && hrtimer_reprogram(timer, base)) {
|
||||
|
||||
/* Timer is expired, act upon the callback mode */
|
||||
switch(timer->cb_mode) {
|
||||
case HRTIMER_CB_IRQSAFE_PERCPU:
|
||||
case HRTIMER_CB_IRQSAFE_UNLOCKED:
|
||||
/*
|
||||
* This is solely for the sched tick emulation with
|
||||
* dynamic tick support to ensure that we do not
|
||||
* restart the tick right on the edge and end up with
|
||||
* the tick timer in the softirq ! The calling site
|
||||
* takes care of this. Also used for hrtimer sleeper !
|
||||
*/
|
||||
debug_hrtimer_deactivate(timer);
|
||||
return 1;
|
||||
case HRTIMER_CB_SOFTIRQ:
|
||||
/*
|
||||
* Move everything else into the softirq pending list !
|
||||
*/
|
||||
list_add_tail(&timer->cb_entry,
|
||||
&base->cpu_base->cb_pending);
|
||||
timer->state = HRTIMER_STATE_PENDING;
|
||||
return 1;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
spin_unlock(&base->cpu_base->lock);
|
||||
raise_softirq_irqoff(HRTIMER_SOFTIRQ);
|
||||
spin_lock(&base->cpu_base->lock);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -724,11 +687,6 @@ static int hrtimer_switch_to_hres(void)
|
|||
return 1;
|
||||
}
|
||||
|
||||
static inline void hrtimer_raise_softirq(void)
|
||||
{
|
||||
raise_softirq(HRTIMER_SOFTIRQ);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline int hrtimer_hres_active(void) { return 0; }
|
||||
|
|
@ -742,12 +700,6 @@ static inline int hrtimer_enqueue_reprogram(struct hrtimer *timer,
|
|||
}
|
||||
static inline void hrtimer_init_hres(struct hrtimer_cpu_base *base) { }
|
||||
static inline void hrtimer_init_timer_hres(struct hrtimer *timer) { }
|
||||
static inline int hrtimer_reprogram(struct hrtimer *timer,
|
||||
struct hrtimer_clock_base *base)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline void hrtimer_raise_softirq(void) { }
|
||||
|
||||
#endif /* CONFIG_HIGH_RES_TIMERS */
|
||||
|
||||
|
|
@ -818,9 +770,11 @@ EXPORT_SYMBOL_GPL(hrtimer_forward);
|
|||
*
|
||||
* The timer is inserted in expiry order. Insertion into the
|
||||
* red black tree is O(log(n)). Must hold the base lock.
|
||||
*
|
||||
* Returns 1 when the new timer is the leftmost timer in the tree.
|
||||
*/
|
||||
static void enqueue_hrtimer(struct hrtimer *timer,
|
||||
struct hrtimer_clock_base *base, int reprogram)
|
||||
static int enqueue_hrtimer(struct hrtimer *timer,
|
||||
struct hrtimer_clock_base *base)
|
||||
{
|
||||
struct rb_node **link = &base->active.rb_node;
|
||||
struct rb_node *parent = NULL;
|
||||
|
|
@ -852,20 +806,8 @@ static void enqueue_hrtimer(struct hrtimer *timer,
|
|||
* Insert the timer to the rbtree and check whether it
|
||||
* replaces the first pending timer
|
||||
*/
|
||||
if (leftmost) {
|
||||
/*
|
||||
* Reprogram the clock event device. When the timer is already
|
||||
* expired hrtimer_enqueue_reprogram has either called the
|
||||
* callback or added it to the pending list and raised the
|
||||
* softirq.
|
||||
*
|
||||
* This is a NOP for !HIGHRES
|
||||
*/
|
||||
if (reprogram && hrtimer_enqueue_reprogram(timer, base))
|
||||
return;
|
||||
|
||||
if (leftmost)
|
||||
base->first = &timer->node;
|
||||
}
|
||||
|
||||
rb_link_node(&timer->node, parent, link);
|
||||
rb_insert_color(&timer->node, &base->active);
|
||||
|
|
@ -874,6 +816,8 @@ static void enqueue_hrtimer(struct hrtimer *timer,
|
|||
* state of a possibly running callback.
|
||||
*/
|
||||
timer->state |= HRTIMER_STATE_ENQUEUED;
|
||||
|
||||
return leftmost;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -890,10 +834,7 @@ static void __remove_hrtimer(struct hrtimer *timer,
|
|||
struct hrtimer_clock_base *base,
|
||||
unsigned long newstate, int reprogram)
|
||||
{
|
||||
/* High res. callback list. NOP for !HIGHRES */
|
||||
if (hrtimer_cb_pending(timer))
|
||||
hrtimer_remove_cb_pending(timer);
|
||||
else {
|
||||
if (timer->state & HRTIMER_STATE_ENQUEUED) {
|
||||
/*
|
||||
* Remove the timer from the rbtree and replace the
|
||||
* first entry pointer if necessary.
|
||||
|
|
@ -953,7 +894,7 @@ hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim, unsigned long delta_n
|
|||
{
|
||||
struct hrtimer_clock_base *base, *new_base;
|
||||
unsigned long flags;
|
||||
int ret, raise;
|
||||
int ret, leftmost;
|
||||
|
||||
base = lock_hrtimer_base(timer, &flags);
|
||||
|
||||
|
|
@ -981,33 +922,19 @@ hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim, unsigned long delta_n
|
|||
|
||||
timer_stats_hrtimer_set_start_info(timer);
|
||||
|
||||
leftmost = enqueue_hrtimer(timer, new_base);
|
||||
|
||||
/*
|
||||
* Only allow reprogramming if the new base is on this CPU.
|
||||
* (it might still be on another CPU if the timer was pending)
|
||||
*
|
||||
* XXX send_remote_softirq() ?
|
||||
*/
|
||||
enqueue_hrtimer(timer, new_base,
|
||||
new_base->cpu_base == &__get_cpu_var(hrtimer_bases));
|
||||
|
||||
/*
|
||||
* The timer may be expired and moved to the cb_pending
|
||||
* list. We can not raise the softirq with base lock held due
|
||||
* to a possible deadlock with runqueue lock.
|
||||
*/
|
||||
raise = timer->state == HRTIMER_STATE_PENDING;
|
||||
|
||||
/*
|
||||
* We use preempt_disable to prevent this task from migrating after
|
||||
* setting up the softirq and raising it. Otherwise, if me migrate
|
||||
* we will raise the softirq on the wrong CPU.
|
||||
*/
|
||||
preempt_disable();
|
||||
if (leftmost && new_base->cpu_base == &__get_cpu_var(hrtimer_bases))
|
||||
hrtimer_enqueue_reprogram(timer, new_base);
|
||||
|
||||
unlock_hrtimer_base(timer, &flags);
|
||||
|
||||
if (raise)
|
||||
hrtimer_raise_softirq();
|
||||
preempt_enable();
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hrtimer_start_range_ns);
|
||||
|
|
@ -1192,75 +1119,6 @@ int hrtimer_get_res(const clockid_t which_clock, struct timespec *tp)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(hrtimer_get_res);
|
||||
|
||||
static void run_hrtimer_pending(struct hrtimer_cpu_base *cpu_base)
|
||||
{
|
||||
spin_lock_irq(&cpu_base->lock);
|
||||
|
||||
while (!list_empty(&cpu_base->cb_pending)) {
|
||||
enum hrtimer_restart (*fn)(struct hrtimer *);
|
||||
struct hrtimer *timer;
|
||||
int restart;
|
||||
int emulate_hardirq_ctx = 0;
|
||||
|
||||
timer = list_entry(cpu_base->cb_pending.next,
|
||||
struct hrtimer, cb_entry);
|
||||
|
||||
debug_hrtimer_deactivate(timer);
|
||||
timer_stats_account_hrtimer(timer);
|
||||
|
||||
fn = timer->function;
|
||||
/*
|
||||
* A timer might have been added to the cb_pending list
|
||||
* when it was migrated during a cpu-offline operation.
|
||||
* Emulate hardirq context for such timers.
|
||||
*/
|
||||
if (timer->cb_mode == HRTIMER_CB_IRQSAFE_PERCPU ||
|
||||
timer->cb_mode == HRTIMER_CB_IRQSAFE_UNLOCKED)
|
||||
emulate_hardirq_ctx = 1;
|
||||
|
||||
__remove_hrtimer(timer, timer->base, HRTIMER_STATE_CALLBACK, 0);
|
||||
spin_unlock_irq(&cpu_base->lock);
|
||||
|
||||
if (unlikely(emulate_hardirq_ctx)) {
|
||||
local_irq_disable();
|
||||
restart = fn(timer);
|
||||
local_irq_enable();
|
||||
} else
|
||||
restart = fn(timer);
|
||||
|
||||
spin_lock_irq(&cpu_base->lock);
|
||||
|
||||
timer->state &= ~HRTIMER_STATE_CALLBACK;
|
||||
if (restart == HRTIMER_RESTART) {
|
||||
BUG_ON(hrtimer_active(timer));
|
||||
/*
|
||||
* Enqueue the timer, allow reprogramming of the event
|
||||
* device
|
||||
*/
|
||||
enqueue_hrtimer(timer, timer->base, 1);
|
||||
} else if (hrtimer_active(timer)) {
|
||||
/*
|
||||
* If the timer was rearmed on another CPU, reprogram
|
||||
* the event device.
|
||||
*/
|
||||
struct hrtimer_clock_base *base = timer->base;
|
||||
|
||||
if (base->first == &timer->node &&
|
||||
hrtimer_reprogram(timer, base)) {
|
||||
/*
|
||||
* Timer is expired. Thus move it from tree to
|
||||
* pending list again.
|
||||
*/
|
||||
__remove_hrtimer(timer, base,
|
||||
HRTIMER_STATE_PENDING, 0);
|
||||
list_add_tail(&timer->cb_entry,
|
||||
&base->cpu_base->cb_pending);
|
||||
}
|
||||
}
|
||||
}
|
||||
spin_unlock_irq(&cpu_base->lock);
|
||||
}
|
||||
|
||||
static void __run_hrtimer(struct hrtimer *timer)
|
||||
{
|
||||
struct hrtimer_clock_base *base = timer->base;
|
||||
|
|
@ -1268,34 +1126,30 @@ static void __run_hrtimer(struct hrtimer *timer)
|
|||
enum hrtimer_restart (*fn)(struct hrtimer *);
|
||||
int restart;
|
||||
|
||||
WARN_ON(!irqs_disabled());
|
||||
|
||||
debug_hrtimer_deactivate(timer);
|
||||
__remove_hrtimer(timer, base, HRTIMER_STATE_CALLBACK, 0);
|
||||
timer_stats_account_hrtimer(timer);
|
||||
|
||||
fn = timer->function;
|
||||
if (timer->cb_mode == HRTIMER_CB_IRQSAFE_PERCPU ||
|
||||
timer->cb_mode == HRTIMER_CB_IRQSAFE_UNLOCKED) {
|
||||
/*
|
||||
* Used for scheduler timers, avoid lock inversion with
|
||||
* rq->lock and tasklist_lock.
|
||||
*
|
||||
* These timers are required to deal with enqueue expiry
|
||||
* themselves and are not allowed to migrate.
|
||||
*/
|
||||
spin_unlock(&cpu_base->lock);
|
||||
restart = fn(timer);
|
||||
spin_lock(&cpu_base->lock);
|
||||
} else
|
||||
restart = fn(timer);
|
||||
|
||||
/*
|
||||
* Note: We clear the CALLBACK bit after enqueue_hrtimer to avoid
|
||||
* reprogramming of the event hardware. This happens at the end of this
|
||||
* function anyway.
|
||||
* Because we run timers from hardirq context, there is no chance
|
||||
* they get migrated to another cpu, therefore its safe to unlock
|
||||
* the timer base.
|
||||
*/
|
||||
spin_unlock(&cpu_base->lock);
|
||||
restart = fn(timer);
|
||||
spin_lock(&cpu_base->lock);
|
||||
|
||||
/*
|
||||
* Note: We clear the CALLBACK bit after enqueue_hrtimer and
|
||||
* we do not reprogramm the event hardware. Happens either in
|
||||
* hrtimer_start_range_ns() or in hrtimer_interrupt()
|
||||
*/
|
||||
if (restart != HRTIMER_NORESTART) {
|
||||
BUG_ON(timer->state != HRTIMER_STATE_CALLBACK);
|
||||
enqueue_hrtimer(timer, base, 0);
|
||||
enqueue_hrtimer(timer, base);
|
||||
}
|
||||
timer->state &= ~HRTIMER_STATE_CALLBACK;
|
||||
}
|
||||
|
|
@ -1311,7 +1165,7 @@ void hrtimer_interrupt(struct clock_event_device *dev)
|
|||
struct hrtimer_cpu_base *cpu_base = &__get_cpu_var(hrtimer_bases);
|
||||
struct hrtimer_clock_base *base;
|
||||
ktime_t expires_next, now;
|
||||
int i, raise = 0;
|
||||
int i;
|
||||
|
||||
BUG_ON(!cpu_base->hres_active);
|
||||
cpu_base->nr_events++;
|
||||
|
|
@ -1360,16 +1214,6 @@ void hrtimer_interrupt(struct clock_event_device *dev)
|
|||
break;
|
||||
}
|
||||
|
||||
/* Move softirq callbacks to the pending list */
|
||||
if (timer->cb_mode == HRTIMER_CB_SOFTIRQ) {
|
||||
__remove_hrtimer(timer, base,
|
||||
HRTIMER_STATE_PENDING, 0);
|
||||
list_add_tail(&timer->cb_entry,
|
||||
&base->cpu_base->cb_pending);
|
||||
raise = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
__run_hrtimer(timer);
|
||||
}
|
||||
spin_unlock(&cpu_base->lock);
|
||||
|
|
@ -1383,10 +1227,22 @@ void hrtimer_interrupt(struct clock_event_device *dev)
|
|||
if (tick_program_event(expires_next, 0))
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
|
||||
/* Raise softirq ? */
|
||||
if (raise)
|
||||
raise_softirq(HRTIMER_SOFTIRQ);
|
||||
/*
|
||||
* local version of hrtimer_peek_ahead_timers() called with interrupts
|
||||
* disabled.
|
||||
*/
|
||||
static void __hrtimer_peek_ahead_timers(void)
|
||||
{
|
||||
struct tick_device *td;
|
||||
|
||||
if (!hrtimer_hres_active())
|
||||
return;
|
||||
|
||||
td = &__get_cpu_var(tick_cpu_device);
|
||||
if (td && td->evtdev)
|
||||
hrtimer_interrupt(td->evtdev);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1400,25 +1256,23 @@ void hrtimer_interrupt(struct clock_event_device *dev)
|
|||
*/
|
||||
void hrtimer_peek_ahead_timers(void)
|
||||
{
|
||||
struct tick_device *td;
|
||||
unsigned long flags;
|
||||
|
||||
if (!hrtimer_hres_active())
|
||||
return;
|
||||
|
||||
local_irq_save(flags);
|
||||
td = &__get_cpu_var(tick_cpu_device);
|
||||
if (td && td->evtdev)
|
||||
hrtimer_interrupt(td->evtdev);
|
||||
__hrtimer_peek_ahead_timers();
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
static void run_hrtimer_softirq(struct softirq_action *h)
|
||||
{
|
||||
run_hrtimer_pending(&__get_cpu_var(hrtimer_bases));
|
||||
hrtimer_peek_ahead_timers();
|
||||
}
|
||||
|
||||
#endif /* CONFIG_HIGH_RES_TIMERS */
|
||||
#else /* CONFIG_HIGH_RES_TIMERS */
|
||||
|
||||
static inline void __hrtimer_peek_ahead_timers(void) { }
|
||||
|
||||
#endif /* !CONFIG_HIGH_RES_TIMERS */
|
||||
|
||||
/*
|
||||
* Called from timer softirq every jiffy, expire hrtimers:
|
||||
|
|
@ -1429,8 +1283,6 @@ static void run_hrtimer_softirq(struct softirq_action *h)
|
|||
*/
|
||||
void hrtimer_run_pending(void)
|
||||
{
|
||||
struct hrtimer_cpu_base *cpu_base = &__get_cpu_var(hrtimer_bases);
|
||||
|
||||
if (hrtimer_hres_active())
|
||||
return;
|
||||
|
||||
|
|
@ -1444,8 +1296,6 @@ void hrtimer_run_pending(void)
|
|||
*/
|
||||
if (tick_check_oneshot_change(!hrtimer_is_hres_enabled()))
|
||||
hrtimer_switch_to_hres();
|
||||
|
||||
run_hrtimer_pending(cpu_base);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1482,14 +1332,6 @@ void hrtimer_run_queues(void)
|
|||
hrtimer_get_expires_tv64(timer))
|
||||
break;
|
||||
|
||||
if (timer->cb_mode == HRTIMER_CB_SOFTIRQ) {
|
||||
__remove_hrtimer(timer, base,
|
||||
HRTIMER_STATE_PENDING, 0);
|
||||
list_add_tail(&timer->cb_entry,
|
||||
&base->cpu_base->cb_pending);
|
||||
continue;
|
||||
}
|
||||
|
||||
__run_hrtimer(timer);
|
||||
}
|
||||
spin_unlock(&cpu_base->lock);
|
||||
|
|
@ -1516,9 +1358,6 @@ void hrtimer_init_sleeper(struct hrtimer_sleeper *sl, struct task_struct *task)
|
|||
{
|
||||
sl->timer.function = hrtimer_wakeup;
|
||||
sl->task = task;
|
||||
#ifdef CONFIG_HIGH_RES_TIMERS
|
||||
sl->timer.cb_mode = HRTIMER_CB_IRQSAFE_UNLOCKED;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int __sched do_nanosleep(struct hrtimer_sleeper *t, enum hrtimer_mode mode)
|
||||
|
|
@ -1628,8 +1467,8 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
asmlinkage long
|
||||
sys_nanosleep(struct timespec __user *rqtp, struct timespec __user *rmtp)
|
||||
SYSCALL_DEFINE2(nanosleep, struct timespec __user *, rqtp,
|
||||
struct timespec __user *, rmtp)
|
||||
{
|
||||
struct timespec tu;
|
||||
|
||||
|
|
@ -1655,36 +1494,22 @@ static void __cpuinit init_hrtimers_cpu(int cpu)
|
|||
for (i = 0; i < HRTIMER_MAX_CLOCK_BASES; i++)
|
||||
cpu_base->clock_base[i].cpu_base = cpu_base;
|
||||
|
||||
INIT_LIST_HEAD(&cpu_base->cb_pending);
|
||||
hrtimer_init_hres(cpu_base);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
|
||||
static int migrate_hrtimer_list(struct hrtimer_clock_base *old_base,
|
||||
struct hrtimer_clock_base *new_base, int dcpu)
|
||||
static void migrate_hrtimer_list(struct hrtimer_clock_base *old_base,
|
||||
struct hrtimer_clock_base *new_base)
|
||||
{
|
||||
struct hrtimer *timer;
|
||||
struct rb_node *node;
|
||||
int raise = 0;
|
||||
|
||||
while ((node = rb_first(&old_base->active))) {
|
||||
timer = rb_entry(node, struct hrtimer, node);
|
||||
BUG_ON(hrtimer_callback_running(timer));
|
||||
debug_hrtimer_deactivate(timer);
|
||||
|
||||
/*
|
||||
* Should not happen. Per CPU timers should be
|
||||
* canceled _before_ the migration code is called
|
||||
*/
|
||||
if (timer->cb_mode == HRTIMER_CB_IRQSAFE_PERCPU) {
|
||||
__remove_hrtimer(timer, old_base,
|
||||
HRTIMER_STATE_INACTIVE, 0);
|
||||
WARN(1, "hrtimer (%p %p)active but cpu %d dead\n",
|
||||
timer, timer->function, dcpu);
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Mark it as STATE_MIGRATE not INACTIVE otherwise the
|
||||
* timer could be seen as !active and just vanish away
|
||||
|
|
@ -1693,112 +1518,73 @@ static int migrate_hrtimer_list(struct hrtimer_clock_base *old_base,
|
|||
__remove_hrtimer(timer, old_base, HRTIMER_STATE_MIGRATE, 0);
|
||||
timer->base = new_base;
|
||||
/*
|
||||
* Enqueue the timer. Allow reprogramming of the event device
|
||||
* Enqueue the timers on the new cpu. This does not
|
||||
* reprogram the event device in case the timer
|
||||
* expires before the earliest on this CPU, but we run
|
||||
* hrtimer_interrupt after we migrated everything to
|
||||
* sort out already expired timers and reprogram the
|
||||
* event device.
|
||||
*/
|
||||
enqueue_hrtimer(timer, new_base, 1);
|
||||
enqueue_hrtimer(timer, new_base);
|
||||
|
||||
#ifdef CONFIG_HIGH_RES_TIMERS
|
||||
/*
|
||||
* Happens with high res enabled when the timer was
|
||||
* already expired and the callback mode is
|
||||
* HRTIMER_CB_IRQSAFE_UNLOCKED (hrtimer_sleeper). The
|
||||
* enqueue code does not move them to the soft irq
|
||||
* pending list for performance/latency reasons, but
|
||||
* in the migration state, we need to do that
|
||||
* otherwise we end up with a stale timer.
|
||||
*/
|
||||
if (timer->state == HRTIMER_STATE_MIGRATE) {
|
||||
timer->state = HRTIMER_STATE_PENDING;
|
||||
list_add_tail(&timer->cb_entry,
|
||||
&new_base->cpu_base->cb_pending);
|
||||
raise = 1;
|
||||
}
|
||||
#endif
|
||||
/* Clear the migration state bit */
|
||||
timer->state &= ~HRTIMER_STATE_MIGRATE;
|
||||
}
|
||||
return raise;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HIGH_RES_TIMERS
|
||||
static int migrate_hrtimer_pending(struct hrtimer_cpu_base *old_base,
|
||||
struct hrtimer_cpu_base *new_base)
|
||||
{
|
||||
struct hrtimer *timer;
|
||||
int raise = 0;
|
||||
|
||||
while (!list_empty(&old_base->cb_pending)) {
|
||||
timer = list_entry(old_base->cb_pending.next,
|
||||
struct hrtimer, cb_entry);
|
||||
|
||||
__remove_hrtimer(timer, timer->base, HRTIMER_STATE_PENDING, 0);
|
||||
timer->base = &new_base->clock_base[timer->base->index];
|
||||
list_add_tail(&timer->cb_entry, &new_base->cb_pending);
|
||||
raise = 1;
|
||||
}
|
||||
return raise;
|
||||
}
|
||||
#else
|
||||
static int migrate_hrtimer_pending(struct hrtimer_cpu_base *old_base,
|
||||
struct hrtimer_cpu_base *new_base)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void migrate_hrtimers(int cpu)
|
||||
static void migrate_hrtimers(int scpu)
|
||||
{
|
||||
struct hrtimer_cpu_base *old_base, *new_base;
|
||||
int i, raise = 0;
|
||||
int i;
|
||||
|
||||
BUG_ON(cpu_online(cpu));
|
||||
old_base = &per_cpu(hrtimer_bases, cpu);
|
||||
new_base = &get_cpu_var(hrtimer_bases);
|
||||
BUG_ON(cpu_online(scpu));
|
||||
tick_cancel_sched_timer(scpu);
|
||||
|
||||
tick_cancel_sched_timer(cpu);
|
||||
local_irq_disable();
|
||||
old_base = &per_cpu(hrtimer_bases, scpu);
|
||||
new_base = &__get_cpu_var(hrtimer_bases);
|
||||
/*
|
||||
* The caller is globally serialized and nobody else
|
||||
* takes two locks at once, deadlock is not possible.
|
||||
*/
|
||||
spin_lock_irq(&new_base->lock);
|
||||
spin_lock(&new_base->lock);
|
||||
spin_lock_nested(&old_base->lock, SINGLE_DEPTH_NESTING);
|
||||
|
||||
for (i = 0; i < HRTIMER_MAX_CLOCK_BASES; i++) {
|
||||
if (migrate_hrtimer_list(&old_base->clock_base[i],
|
||||
&new_base->clock_base[i], cpu))
|
||||
raise = 1;
|
||||
migrate_hrtimer_list(&old_base->clock_base[i],
|
||||
&new_base->clock_base[i]);
|
||||
}
|
||||
|
||||
if (migrate_hrtimer_pending(old_base, new_base))
|
||||
raise = 1;
|
||||
|
||||
spin_unlock(&old_base->lock);
|
||||
spin_unlock_irq(&new_base->lock);
|
||||
put_cpu_var(hrtimer_bases);
|
||||
spin_unlock(&new_base->lock);
|
||||
|
||||
if (raise)
|
||||
hrtimer_raise_softirq();
|
||||
/* Check, if we got expired work to do */
|
||||
__hrtimer_peek_ahead_timers();
|
||||
local_irq_enable();
|
||||
}
|
||||
|
||||
#endif /* CONFIG_HOTPLUG_CPU */
|
||||
|
||||
static int __cpuinit hrtimer_cpu_notify(struct notifier_block *self,
|
||||
unsigned long action, void *hcpu)
|
||||
{
|
||||
unsigned int cpu = (long)hcpu;
|
||||
int scpu = (long)hcpu;
|
||||
|
||||
switch (action) {
|
||||
|
||||
case CPU_UP_PREPARE:
|
||||
case CPU_UP_PREPARE_FROZEN:
|
||||
init_hrtimers_cpu(cpu);
|
||||
init_hrtimers_cpu(scpu);
|
||||
break;
|
||||
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
case CPU_DEAD:
|
||||
case CPU_DEAD_FROZEN:
|
||||
clockevents_notify(CLOCK_EVT_NOTIFY_CPU_DEAD, &cpu);
|
||||
migrate_hrtimers(cpu);
|
||||
{
|
||||
clockevents_notify(CLOCK_EVT_NOTIFY_CPU_DEAD, &scpu);
|
||||
migrate_hrtimers(scpu);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/async.h>
|
||||
|
||||
#include "internals.h"
|
||||
|
||||
|
|
@ -34,15 +35,16 @@ unsigned long probe_irq_on(void)
|
|||
unsigned int status;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* quiesce the kernel, or at least the asynchronous portion
|
||||
*/
|
||||
async_synchronize_full();
|
||||
mutex_lock(&probing_active);
|
||||
/*
|
||||
* something may have generated an irq long ago and we want to
|
||||
* flush such a longstanding irq before considering it as spurious.
|
||||
*/
|
||||
for_each_irq_desc_reverse(i, desc) {
|
||||
if (!desc)
|
||||
continue;
|
||||
|
||||
spin_lock_irq(&desc->lock);
|
||||
if (!desc->action && !(desc->status & IRQ_NOPROBE)) {
|
||||
/*
|
||||
|
|
@ -71,9 +73,6 @@ unsigned long probe_irq_on(void)
|
|||
* happened in the previous stage, it may have masked itself)
|
||||
*/
|
||||
for_each_irq_desc_reverse(i, desc) {
|
||||
if (!desc)
|
||||
continue;
|
||||
|
||||
spin_lock_irq(&desc->lock);
|
||||
if (!desc->action && !(desc->status & IRQ_NOPROBE)) {
|
||||
desc->status |= IRQ_AUTODETECT | IRQ_WAITING;
|
||||
|
|
@ -92,9 +91,6 @@ unsigned long probe_irq_on(void)
|
|||
* Now filter out any obviously spurious interrupts
|
||||
*/
|
||||
for_each_irq_desc(i, desc) {
|
||||
if (!desc)
|
||||
continue;
|
||||
|
||||
spin_lock_irq(&desc->lock);
|
||||
status = desc->status;
|
||||
|
||||
|
|
@ -133,9 +129,6 @@ unsigned int probe_irq_mask(unsigned long val)
|
|||
int i;
|
||||
|
||||
for_each_irq_desc(i, desc) {
|
||||
if (!desc)
|
||||
continue;
|
||||
|
||||
spin_lock_irq(&desc->lock);
|
||||
status = desc->status;
|
||||
|
||||
|
|
@ -178,9 +171,6 @@ int probe_irq_off(unsigned long val)
|
|||
unsigned int status;
|
||||
|
||||
for_each_irq_desc(i, desc) {
|
||||
if (!desc)
|
||||
continue;
|
||||
|
||||
spin_lock_irq(&desc->lock);
|
||||
status = desc->status;
|
||||
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ void dynamic_irq_init(unsigned int irq)
|
|||
desc->irq_count = 0;
|
||||
desc->irqs_unhandled = 0;
|
||||
#ifdef CONFIG_SMP
|
||||
cpus_setall(desc->affinity);
|
||||
cpumask_setall(&desc->affinity);
|
||||
#endif
|
||||
spin_unlock_irqrestore(&desc->lock, flags);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,10 +56,6 @@ void handle_bad_irq(unsigned int irq, struct irq_desc *desc)
|
|||
int nr_irqs = NR_IRQS;
|
||||
EXPORT_SYMBOL_GPL(nr_irqs);
|
||||
|
||||
void __init __attribute__((weak)) arch_early_irq_init(void)
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SPARSE_IRQ
|
||||
static struct irq_desc irq_desc_init = {
|
||||
.irq = -1,
|
||||
|
|
@ -90,13 +86,11 @@ void init_kstat_irqs(struct irq_desc *desc, int cpu, int nr)
|
|||
desc->kstat_irqs = (unsigned int *)ptr;
|
||||
}
|
||||
|
||||
void __attribute__((weak)) arch_init_chip_data(struct irq_desc *desc, int cpu)
|
||||
{
|
||||
}
|
||||
|
||||
static void init_one_irq_desc(int irq, struct irq_desc *desc, int cpu)
|
||||
{
|
||||
memcpy(desc, &irq_desc_init, sizeof(struct irq_desc));
|
||||
|
||||
spin_lock_init(&desc->lock);
|
||||
desc->irq = irq;
|
||||
#ifdef CONFIG_SMP
|
||||
desc->cpu = cpu;
|
||||
|
|
@ -134,7 +128,7 @@ static struct irq_desc irq_desc_legacy[NR_IRQS_LEGACY] __cacheline_aligned_in_sm
|
|||
/* FIXME: use bootmem alloc ...*/
|
||||
static unsigned int kstat_irqs_legacy[NR_IRQS_LEGACY][NR_CPUS];
|
||||
|
||||
void __init early_irq_init(void)
|
||||
int __init early_irq_init(void)
|
||||
{
|
||||
struct irq_desc *desc;
|
||||
int legacy_count;
|
||||
|
|
@ -146,6 +140,7 @@ void __init early_irq_init(void)
|
|||
for (i = 0; i < legacy_count; i++) {
|
||||
desc[i].irq = i;
|
||||
desc[i].kstat_irqs = kstat_irqs_legacy[i];
|
||||
lockdep_set_class(&desc[i].lock, &irq_desc_lock_class);
|
||||
|
||||
irq_desc_ptrs[i] = desc + i;
|
||||
}
|
||||
|
|
@ -153,7 +148,7 @@ void __init early_irq_init(void)
|
|||
for (i = legacy_count; i < NR_IRQS; i++)
|
||||
irq_desc_ptrs[i] = NULL;
|
||||
|
||||
arch_early_irq_init();
|
||||
return arch_early_irq_init();
|
||||
}
|
||||
|
||||
struct irq_desc *irq_to_desc(unsigned int irq)
|
||||
|
|
@ -203,7 +198,7 @@ out_unlock:
|
|||
return desc;
|
||||
}
|
||||
|
||||
#else
|
||||
#else /* !CONFIG_SPARSE_IRQ */
|
||||
|
||||
struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = {
|
||||
[0 ... NR_IRQS-1] = {
|
||||
|
|
@ -218,7 +213,31 @@ struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = {
|
|||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
int __init early_irq_init(void)
|
||||
{
|
||||
struct irq_desc *desc;
|
||||
int count;
|
||||
int i;
|
||||
|
||||
desc = irq_desc;
|
||||
count = ARRAY_SIZE(irq_desc);
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
desc[i].irq = i;
|
||||
|
||||
return arch_early_irq_init();
|
||||
}
|
||||
|
||||
struct irq_desc *irq_to_desc(unsigned int irq)
|
||||
{
|
||||
return (irq < NR_IRQS) ? irq_desc + irq : NULL;
|
||||
}
|
||||
|
||||
struct irq_desc *irq_to_desc_alloc_cpu(unsigned int irq, int cpu)
|
||||
{
|
||||
return irq_to_desc(irq);
|
||||
}
|
||||
#endif /* !CONFIG_SPARSE_IRQ */
|
||||
|
||||
/*
|
||||
* What should we do if we get a hw irq event on an illegal vector?
|
||||
|
|
@ -428,9 +447,6 @@ void early_init_irq_lock_class(void)
|
|||
int i;
|
||||
|
||||
for_each_irq_desc(i, desc) {
|
||||
if (!desc)
|
||||
continue;
|
||||
|
||||
lockdep_set_class(&desc->lock, &irq_desc_lock_class);
|
||||
}
|
||||
}
|
||||
|
|
@ -439,7 +455,7 @@ void early_init_irq_lock_class(void)
|
|||
unsigned int kstat_irqs_cpu(unsigned int irq, int cpu)
|
||||
{
|
||||
struct irq_desc *desc = irq_to_desc(irq);
|
||||
return desc->kstat_irqs[cpu];
|
||||
return desc ? desc->kstat_irqs[cpu] : 0;
|
||||
}
|
||||
#endif
|
||||
EXPORT_SYMBOL(kstat_irqs_cpu);
|
||||
|
|
|
|||
|
|
@ -16,8 +16,15 @@
|
|||
#include "internals.h"
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
cpumask_var_t irq_default_affinity;
|
||||
|
||||
cpumask_t irq_default_affinity = CPU_MASK_ALL;
|
||||
static int init_irq_default_affinity(void)
|
||||
{
|
||||
alloc_cpumask_var(&irq_default_affinity, GFP_KERNEL);
|
||||
cpumask_setall(irq_default_affinity);
|
||||
return 0;
|
||||
}
|
||||
core_initcall(init_irq_default_affinity);
|
||||
|
||||
/**
|
||||
* synchronize_irq - wait for pending IRQ handlers (on other CPUs)
|
||||
|
|
@ -79,7 +86,7 @@ int irq_can_set_affinity(unsigned int irq)
|
|||
* @cpumask: cpumask
|
||||
*
|
||||
*/
|
||||
int irq_set_affinity(unsigned int irq, cpumask_t cpumask)
|
||||
int irq_set_affinity(unsigned int irq, const struct cpumask *cpumask)
|
||||
{
|
||||
struct irq_desc *desc = irq_to_desc(irq);
|
||||
unsigned long flags;
|
||||
|
|
@ -91,14 +98,14 @@ int irq_set_affinity(unsigned int irq, cpumask_t cpumask)
|
|||
|
||||
#ifdef CONFIG_GENERIC_PENDING_IRQ
|
||||
if (desc->status & IRQ_MOVE_PCNTXT || desc->status & IRQ_DISABLED) {
|
||||
desc->affinity = cpumask;
|
||||
cpumask_copy(&desc->affinity, cpumask);
|
||||
desc->chip->set_affinity(irq, cpumask);
|
||||
} else {
|
||||
desc->status |= IRQ_MOVE_PENDING;
|
||||
desc->pending_mask = cpumask;
|
||||
cpumask_copy(&desc->pending_mask, cpumask);
|
||||
}
|
||||
#else
|
||||
desc->affinity = cpumask;
|
||||
cpumask_copy(&desc->affinity, cpumask);
|
||||
desc->chip->set_affinity(irq, cpumask);
|
||||
#endif
|
||||
desc->status |= IRQ_AFFINITY_SET;
|
||||
|
|
@ -112,26 +119,24 @@ int irq_set_affinity(unsigned int irq, cpumask_t cpumask)
|
|||
*/
|
||||
int do_irq_select_affinity(unsigned int irq, struct irq_desc *desc)
|
||||
{
|
||||
cpumask_t mask;
|
||||
|
||||
if (!irq_can_set_affinity(irq))
|
||||
return 0;
|
||||
|
||||
cpus_and(mask, cpu_online_map, irq_default_affinity);
|
||||
|
||||
/*
|
||||
* Preserve an userspace affinity setup, but make sure that
|
||||
* one of the targets is online.
|
||||
*/
|
||||
if (desc->status & (IRQ_AFFINITY_SET | IRQ_NO_BALANCING)) {
|
||||
if (cpus_intersects(desc->affinity, cpu_online_map))
|
||||
mask = desc->affinity;
|
||||
if (cpumask_any_and(&desc->affinity, cpu_online_mask)
|
||||
< nr_cpu_ids)
|
||||
goto set_affinity;
|
||||
else
|
||||
desc->status &= ~IRQ_AFFINITY_SET;
|
||||
}
|
||||
|
||||
desc->affinity = mask;
|
||||
desc->chip->set_affinity(irq, mask);
|
||||
cpumask_and(&desc->affinity, cpu_online_mask, irq_default_affinity);
|
||||
set_affinity:
|
||||
desc->chip->set_affinity(irq, &desc->affinity);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -676,6 +681,18 @@ int request_irq(unsigned int irq, irq_handler_t handler,
|
|||
struct irq_desc *desc;
|
||||
int retval;
|
||||
|
||||
/*
|
||||
* handle_IRQ_event() always ignores IRQF_DISABLED except for
|
||||
* the _first_ irqaction (sigh). That can cause oopsing, but
|
||||
* the behavior is classified as "will not fix" so we need to
|
||||
* start nudging drivers away from using that idiom.
|
||||
*/
|
||||
if ((irqflags & (IRQF_SHARED|IRQF_DISABLED))
|
||||
== (IRQF_SHARED|IRQF_DISABLED))
|
||||
pr_warning("IRQ %d/%s: IRQF_DISABLED is not "
|
||||
"guaranteed on shared IRQs\n",
|
||||
irq, devname);
|
||||
|
||||
#ifdef CONFIG_LOCKDEP
|
||||
/*
|
||||
* Lockdep wants atomic interrupt handlers:
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
void move_masked_irq(int irq)
|
||||
{
|
||||
struct irq_desc *desc = irq_to_desc(irq);
|
||||
cpumask_t tmp;
|
||||
|
||||
if (likely(!(desc->status & IRQ_MOVE_PENDING)))
|
||||
return;
|
||||
|
|
@ -19,7 +18,7 @@ void move_masked_irq(int irq)
|
|||
|
||||
desc->status &= ~IRQ_MOVE_PENDING;
|
||||
|
||||
if (unlikely(cpus_empty(desc->pending_mask)))
|
||||
if (unlikely(cpumask_empty(&desc->pending_mask)))
|
||||
return;
|
||||
|
||||
if (!desc->chip->set_affinity)
|
||||
|
|
@ -27,8 +26,6 @@ void move_masked_irq(int irq)
|
|||
|
||||
assert_spin_locked(&desc->lock);
|
||||
|
||||
cpus_and(tmp, desc->pending_mask, cpu_online_map);
|
||||
|
||||
/*
|
||||
* If there was a valid mask to work with, please
|
||||
* do the disable, re-program, enable sequence.
|
||||
|
|
@ -41,10 +38,13 @@ void move_masked_irq(int irq)
|
|||
* For correct operation this depends on the caller
|
||||
* masking the irqs.
|
||||
*/
|
||||
if (likely(!cpus_empty(tmp))) {
|
||||
desc->chip->set_affinity(irq,tmp);
|
||||
if (likely(cpumask_any_and(&desc->pending_mask, cpu_online_mask)
|
||||
< nr_cpu_ids)) {
|
||||
cpumask_and(&desc->affinity,
|
||||
&desc->pending_mask, cpu_online_mask);
|
||||
desc->chip->set_affinity(irq, &desc->affinity);
|
||||
}
|
||||
cpus_clear(desc->pending_mask);
|
||||
cpumask_clear(&desc->pending_mask);
|
||||
}
|
||||
|
||||
void move_native_irq(int irq)
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ static void init_copy_one_irq_desc(int irq, struct irq_desc *old_desc,
|
|||
struct irq_desc *desc, int cpu)
|
||||
{
|
||||
memcpy(desc, old_desc, sizeof(struct irq_desc));
|
||||
spin_lock_init(&desc->lock);
|
||||
desc->cpu = cpu;
|
||||
lockdep_set_class(&desc->lock, &irq_desc_lock_class);
|
||||
init_copy_kstat_irqs(old_desc, desc, cpu, nr_cpu_ids);
|
||||
|
|
@ -74,10 +75,8 @@ static struct irq_desc *__real_move_irq_desc(struct irq_desc *old_desc,
|
|||
|
||||
node = cpu_to_node(cpu);
|
||||
desc = kzalloc_node(sizeof(*desc), GFP_ATOMIC, node);
|
||||
printk(KERN_DEBUG " move irq_desc for %d to cpu %d node %d\n",
|
||||
irq, cpu, node);
|
||||
if (!desc) {
|
||||
printk(KERN_ERR "can not get new irq_desc for moving\n");
|
||||
printk(KERN_ERR "irq %d: can not get new irq_desc for migration.\n", irq);
|
||||
/* still use old one */
|
||||
desc = old_desc;
|
||||
goto out_unlock;
|
||||
|
|
@ -106,8 +105,6 @@ struct irq_desc *move_irq_desc(struct irq_desc *desc, int cpu)
|
|||
return desc;
|
||||
|
||||
old_cpu = desc->cpu;
|
||||
printk(KERN_DEBUG
|
||||
"try to move irq_desc from cpu %d to %d\n", old_cpu, cpu);
|
||||
if (old_cpu != cpu) {
|
||||
node = cpu_to_node(cpu);
|
||||
old_node = cpu_to_node(old_cpu);
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ static struct proc_dir_entry *root_irq_dir;
|
|||
static int irq_affinity_proc_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct irq_desc *desc = irq_to_desc((long)m->private);
|
||||
cpumask_t *mask = &desc->affinity;
|
||||
const struct cpumask *mask = &desc->affinity;
|
||||
|
||||
#ifdef CONFIG_GENERIC_PENDING_IRQ
|
||||
if (desc->status & IRQ_MOVE_PENDING)
|
||||
|
|
@ -40,33 +40,42 @@ static ssize_t irq_affinity_proc_write(struct file *file,
|
|||
const char __user *buffer, size_t count, loff_t *pos)
|
||||
{
|
||||
unsigned int irq = (int)(long)PDE(file->f_path.dentry->d_inode)->data;
|
||||
cpumask_t new_value;
|
||||
cpumask_var_t new_value;
|
||||
int err;
|
||||
|
||||
if (!irq_to_desc(irq)->chip->set_affinity || no_irq_affinity ||
|
||||
irq_balancing_disabled(irq))
|
||||
return -EIO;
|
||||
|
||||
if (!alloc_cpumask_var(&new_value, GFP_KERNEL))
|
||||
return -ENOMEM;
|
||||
|
||||
err = cpumask_parse_user(buffer, count, new_value);
|
||||
if (err)
|
||||
return err;
|
||||
goto free_cpumask;
|
||||
|
||||
if (!is_affinity_mask_valid(new_value))
|
||||
return -EINVAL;
|
||||
if (!is_affinity_mask_valid(new_value)) {
|
||||
err = -EINVAL;
|
||||
goto free_cpumask;
|
||||
}
|
||||
|
||||
/*
|
||||
* Do not allow disabling IRQs completely - it's a too easy
|
||||
* way to make the system unusable accidentally :-) At least
|
||||
* one online CPU still has to be targeted.
|
||||
*/
|
||||
if (!cpus_intersects(new_value, cpu_online_map))
|
||||
if (!cpumask_intersects(new_value, cpu_online_mask)) {
|
||||
/* Special case for empty set - allow the architecture
|
||||
code to set default SMP affinity. */
|
||||
return irq_select_affinity_usr(irq) ? -EINVAL : count;
|
||||
err = irq_select_affinity_usr(irq) ? -EINVAL : count;
|
||||
} else {
|
||||
irq_set_affinity(irq, new_value);
|
||||
err = count;
|
||||
}
|
||||
|
||||
irq_set_affinity(irq, new_value);
|
||||
|
||||
return count;
|
||||
free_cpumask:
|
||||
free_cpumask_var(new_value);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int irq_affinity_proc_open(struct inode *inode, struct file *file)
|
||||
|
|
@ -84,7 +93,7 @@ static const struct file_operations irq_affinity_proc_fops = {
|
|||
|
||||
static int default_affinity_show(struct seq_file *m, void *v)
|
||||
{
|
||||
seq_cpumask(m, &irq_default_affinity);
|
||||
seq_cpumask(m, irq_default_affinity);
|
||||
seq_putc(m, '\n');
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -92,27 +101,37 @@ static int default_affinity_show(struct seq_file *m, void *v)
|
|||
static ssize_t default_affinity_write(struct file *file,
|
||||
const char __user *buffer, size_t count, loff_t *ppos)
|
||||
{
|
||||
cpumask_t new_value;
|
||||
cpumask_var_t new_value;
|
||||
int err;
|
||||
|
||||
if (!alloc_cpumask_var(&new_value, GFP_KERNEL))
|
||||
return -ENOMEM;
|
||||
|
||||
err = cpumask_parse_user(buffer, count, new_value);
|
||||
if (err)
|
||||
return err;
|
||||
goto out;
|
||||
|
||||
if (!is_affinity_mask_valid(new_value))
|
||||
return -EINVAL;
|
||||
if (!is_affinity_mask_valid(new_value)) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Do not allow disabling IRQs completely - it's a too easy
|
||||
* way to make the system unusable accidentally :-) At least
|
||||
* one online CPU still has to be targeted.
|
||||
*/
|
||||
if (!cpus_intersects(new_value, cpu_online_map))
|
||||
return -EINVAL;
|
||||
if (!cpumask_intersects(new_value, cpu_online_mask)) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
irq_default_affinity = new_value;
|
||||
cpumask_copy(irq_default_affinity, new_value);
|
||||
err = count;
|
||||
|
||||
return count;
|
||||
out:
|
||||
free_cpumask_var(new_value);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int default_affinity_open(struct inode *inode, struct file *file)
|
||||
|
|
|
|||
|
|
@ -91,9 +91,6 @@ static int misrouted_irq(int irq)
|
|||
int i, ok = 0;
|
||||
|
||||
for_each_irq_desc(i, desc) {
|
||||
if (!desc)
|
||||
continue;
|
||||
|
||||
if (!i)
|
||||
continue;
|
||||
|
||||
|
|
@ -115,8 +112,6 @@ static void poll_spurious_irqs(unsigned long dummy)
|
|||
for_each_irq_desc(i, desc) {
|
||||
unsigned int status;
|
||||
|
||||
if (!desc)
|
||||
continue;
|
||||
if (!i)
|
||||
continue;
|
||||
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ int do_getitimer(int which, struct itimerval *value)
|
|||
return 0;
|
||||
}
|
||||
|
||||
asmlinkage long sys_getitimer(int which, struct itimerval __user *value)
|
||||
SYSCALL_DEFINE2(getitimer, int, which, struct itimerval __user *, value)
|
||||
{
|
||||
int error = -EFAULT;
|
||||
struct itimerval get_buffer;
|
||||
|
|
@ -260,9 +260,8 @@ unsigned int alarm_setitimer(unsigned int seconds)
|
|||
return it_old.it_value.tv_sec;
|
||||
}
|
||||
|
||||
asmlinkage long sys_setitimer(int which,
|
||||
struct itimerval __user *value,
|
||||
struct itimerval __user *ovalue)
|
||||
SYSCALL_DEFINE3(setitimer, int, which, struct itimerval __user *, value,
|
||||
struct itimerval __user *, ovalue)
|
||||
{
|
||||
struct itimerval set_buffer, get_buffer;
|
||||
int error;
|
||||
|
|
|
|||
|
|
@ -30,20 +30,19 @@
|
|||
#define all_var 0
|
||||
#endif
|
||||
|
||||
/* These will be re-linked against their real values during the second link stage */
|
||||
extern const unsigned long kallsyms_addresses[] __attribute__((weak));
|
||||
extern const u8 kallsyms_names[] __attribute__((weak));
|
||||
extern const unsigned long kallsyms_addresses[];
|
||||
extern const u8 kallsyms_names[];
|
||||
|
||||
/* tell the compiler that the count isn't in the small data section if the arch
|
||||
* has one (eg: FRV)
|
||||
*/
|
||||
extern const unsigned long kallsyms_num_syms
|
||||
__attribute__((weak, section(".rodata")));
|
||||
__attribute__((__section__(".rodata")));
|
||||
|
||||
extern const u8 kallsyms_token_table[] __attribute__((weak));
|
||||
extern const u16 kallsyms_token_index[] __attribute__((weak));
|
||||
extern const u8 kallsyms_token_table[];
|
||||
extern const u16 kallsyms_token_index[];
|
||||
|
||||
extern const unsigned long kallsyms_markers[] __attribute__((weak));
|
||||
extern const unsigned long kallsyms_markers[];
|
||||
|
||||
static inline int is_kernel_inittext(unsigned long addr)
|
||||
{
|
||||
|
|
@ -168,9 +167,6 @@ static unsigned long get_symbol_pos(unsigned long addr,
|
|||
unsigned long symbol_start = 0, symbol_end = 0;
|
||||
unsigned long i, low, high, mid;
|
||||
|
||||
/* This kernel should never had been booted. */
|
||||
BUG_ON(!kallsyms_addresses);
|
||||
|
||||
/* do a binary search on the sorted kallsyms_addresses array */
|
||||
low = 0;
|
||||
high = kallsyms_num_syms;
|
||||
|
|
|
|||
|
|
@ -934,9 +934,8 @@ struct kimage *kexec_crash_image;
|
|||
|
||||
static DEFINE_MUTEX(kexec_mutex);
|
||||
|
||||
asmlinkage long sys_kexec_load(unsigned long entry, unsigned long nr_segments,
|
||||
struct kexec_segment __user *segments,
|
||||
unsigned long flags)
|
||||
SYSCALL_DEFINE4(kexec_load, unsigned long, entry, unsigned long, nr_segments,
|
||||
struct kexec_segment __user *, segments, unsigned long, flags)
|
||||
{
|
||||
struct kimage **dest_image, *image;
|
||||
int result;
|
||||
|
|
@ -1116,7 +1115,7 @@ void crash_save_cpu(struct pt_regs *regs, int cpu)
|
|||
struct elf_prstatus prstatus;
|
||||
u32 *buf;
|
||||
|
||||
if ((cpu < 0) || (cpu >= NR_CPUS))
|
||||
if ((cpu < 0) || (cpu >= nr_cpu_ids))
|
||||
return;
|
||||
|
||||
/* Using ELF notes here is opportunistic.
|
||||
|
|
|
|||
|
|
@ -51,8 +51,8 @@ char modprobe_path[KMOD_PATH_LEN] = "/sbin/modprobe";
|
|||
|
||||
/**
|
||||
* request_module - try to load a kernel module
|
||||
* @fmt: printf style format string for the name of the module
|
||||
* @varargs: arguements as specified in the format string
|
||||
* @fmt: printf style format string for the name of the module
|
||||
* @...: arguments as specified in the format string
|
||||
*
|
||||
* Load a module using the user mode module loader. The function returns
|
||||
* zero on success or a negative errno code on failure. Note that a
|
||||
|
|
@ -118,10 +118,10 @@ EXPORT_SYMBOL(request_module);
|
|||
struct subprocess_info {
|
||||
struct work_struct work;
|
||||
struct completion *complete;
|
||||
struct cred *cred;
|
||||
char *path;
|
||||
char **argv;
|
||||
char **envp;
|
||||
struct key *ring;
|
||||
enum umh_wait wait;
|
||||
int retval;
|
||||
struct file *stdin;
|
||||
|
|
@ -134,19 +134,20 @@ struct subprocess_info {
|
|||
static int ____call_usermodehelper(void *data)
|
||||
{
|
||||
struct subprocess_info *sub_info = data;
|
||||
struct key *new_session, *old_session;
|
||||
int retval;
|
||||
|
||||
/* Unblock all signals and set the session keyring. */
|
||||
new_session = key_get(sub_info->ring);
|
||||
BUG_ON(atomic_read(&sub_info->cred->usage) != 1);
|
||||
|
||||
/* Unblock all signals */
|
||||
spin_lock_irq(¤t->sighand->siglock);
|
||||
old_session = __install_session_keyring(current, new_session);
|
||||
flush_signal_handlers(current, 1);
|
||||
sigemptyset(¤t->blocked);
|
||||
recalc_sigpending();
|
||||
spin_unlock_irq(¤t->sighand->siglock);
|
||||
|
||||
key_put(old_session);
|
||||
/* Install the credentials */
|
||||
commit_creds(sub_info->cred);
|
||||
sub_info->cred = NULL;
|
||||
|
||||
/* Install input pipe when needed */
|
||||
if (sub_info->stdin) {
|
||||
|
|
@ -185,6 +186,8 @@ void call_usermodehelper_freeinfo(struct subprocess_info *info)
|
|||
{
|
||||
if (info->cleanup)
|
||||
(*info->cleanup)(info->argv, info->envp);
|
||||
if (info->cred)
|
||||
put_cred(info->cred);
|
||||
kfree(info);
|
||||
}
|
||||
EXPORT_SYMBOL(call_usermodehelper_freeinfo);
|
||||
|
|
@ -240,6 +243,8 @@ static void __call_usermodehelper(struct work_struct *work)
|
|||
pid_t pid;
|
||||
enum umh_wait wait = sub_info->wait;
|
||||
|
||||
BUG_ON(atomic_read(&sub_info->cred->usage) != 1);
|
||||
|
||||
/* CLONE_VFORK: wait until the usermode helper has execve'd
|
||||
* successfully We need the data structures to stay around
|
||||
* until that is done. */
|
||||
|
|
@ -362,6 +367,9 @@ struct subprocess_info *call_usermodehelper_setup(char *path, char **argv,
|
|||
sub_info->path = path;
|
||||
sub_info->argv = argv;
|
||||
sub_info->envp = envp;
|
||||
sub_info->cred = prepare_usermodehelper_creds();
|
||||
if (!sub_info->cred)
|
||||
return NULL;
|
||||
|
||||
out:
|
||||
return sub_info;
|
||||
|
|
@ -376,7 +384,13 @@ EXPORT_SYMBOL(call_usermodehelper_setup);
|
|||
void call_usermodehelper_setkeys(struct subprocess_info *info,
|
||||
struct key *session_keyring)
|
||||
{
|
||||
info->ring = session_keyring;
|
||||
#ifdef CONFIG_KEYS
|
||||
struct thread_group_cred *tgcred = info->cred->tgcred;
|
||||
key_put(tgcred->session_keyring);
|
||||
tgcred->session_keyring = key_get(session_keyring);
|
||||
#else
|
||||
BUG();
|
||||
#endif
|
||||
}
|
||||
EXPORT_SYMBOL(call_usermodehelper_setkeys);
|
||||
|
||||
|
|
@ -444,6 +458,8 @@ int call_usermodehelper_exec(struct subprocess_info *sub_info,
|
|||
DECLARE_COMPLETION_ONSTACK(done);
|
||||
int retval = 0;
|
||||
|
||||
BUG_ON(atomic_read(&sub_info->cred->usage) != 1);
|
||||
|
||||
helper_lock();
|
||||
if (sub_info->path[0] == '\0')
|
||||
goto out;
|
||||
|
|
|
|||
281
kernel/kprobes.c
281
kernel/kprobes.c
|
|
@ -69,7 +69,7 @@ static struct hlist_head kretprobe_inst_table[KPROBE_TABLE_SIZE];
|
|||
/* NOTE: change this value only with kprobe_mutex held */
|
||||
static bool kprobe_enabled;
|
||||
|
||||
DEFINE_MUTEX(kprobe_mutex); /* Protects kprobe_table */
|
||||
static DEFINE_MUTEX(kprobe_mutex); /* Protects kprobe_table */
|
||||
static DEFINE_PER_CPU(struct kprobe *, kprobe_instance) = NULL;
|
||||
static struct {
|
||||
spinlock_t lock ____cacheline_aligned_in_smp;
|
||||
|
|
@ -115,6 +115,7 @@ enum kprobe_slot_state {
|
|||
SLOT_USED = 2,
|
||||
};
|
||||
|
||||
static DEFINE_MUTEX(kprobe_insn_mutex); /* Protects kprobe_insn_pages */
|
||||
static struct hlist_head kprobe_insn_pages;
|
||||
static int kprobe_garbage_slots;
|
||||
static int collect_garbage_slots(void);
|
||||
|
|
@ -144,10 +145,10 @@ loop_end:
|
|||
}
|
||||
|
||||
/**
|
||||
* get_insn_slot() - Find a slot on an executable page for an instruction.
|
||||
* __get_insn_slot() - Find a slot on an executable page for an instruction.
|
||||
* We allocate an executable page if there's no room on existing ones.
|
||||
*/
|
||||
kprobe_opcode_t __kprobes *get_insn_slot(void)
|
||||
static kprobe_opcode_t __kprobes *__get_insn_slot(void)
|
||||
{
|
||||
struct kprobe_insn_page *kip;
|
||||
struct hlist_node *pos;
|
||||
|
|
@ -196,6 +197,15 @@ kprobe_opcode_t __kprobes *get_insn_slot(void)
|
|||
return kip->insns;
|
||||
}
|
||||
|
||||
kprobe_opcode_t __kprobes *get_insn_slot(void)
|
||||
{
|
||||
kprobe_opcode_t *ret;
|
||||
mutex_lock(&kprobe_insn_mutex);
|
||||
ret = __get_insn_slot();
|
||||
mutex_unlock(&kprobe_insn_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Return 1 if all garbages are collected, otherwise 0. */
|
||||
static int __kprobes collect_one_slot(struct kprobe_insn_page *kip, int idx)
|
||||
{
|
||||
|
|
@ -226,9 +236,13 @@ static int __kprobes collect_garbage_slots(void)
|
|||
{
|
||||
struct kprobe_insn_page *kip;
|
||||
struct hlist_node *pos, *next;
|
||||
int safety;
|
||||
|
||||
/* Ensure no-one is preepmted on the garbages */
|
||||
if (check_safety() != 0)
|
||||
mutex_unlock(&kprobe_insn_mutex);
|
||||
safety = check_safety();
|
||||
mutex_lock(&kprobe_insn_mutex);
|
||||
if (safety != 0)
|
||||
return -EAGAIN;
|
||||
|
||||
hlist_for_each_entry_safe(kip, pos, next, &kprobe_insn_pages, hlist) {
|
||||
|
|
@ -251,6 +265,7 @@ void __kprobes free_insn_slot(kprobe_opcode_t * slot, int dirty)
|
|||
struct kprobe_insn_page *kip;
|
||||
struct hlist_node *pos;
|
||||
|
||||
mutex_lock(&kprobe_insn_mutex);
|
||||
hlist_for_each_entry(kip, pos, &kprobe_insn_pages, hlist) {
|
||||
if (kip->insns <= slot &&
|
||||
slot < kip->insns + (INSNS_PER_PAGE * MAX_INSN_SIZE)) {
|
||||
|
|
@ -267,6 +282,8 @@ void __kprobes free_insn_slot(kprobe_opcode_t * slot, int dirty)
|
|||
|
||||
if (dirty && ++kprobe_garbage_slots > INSNS_PER_PAGE)
|
||||
collect_garbage_slots();
|
||||
|
||||
mutex_unlock(&kprobe_insn_mutex);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
@ -310,7 +327,7 @@ static int __kprobes aggr_pre_handler(struct kprobe *p, struct pt_regs *regs)
|
|||
struct kprobe *kp;
|
||||
|
||||
list_for_each_entry_rcu(kp, &p->list, list) {
|
||||
if (kp->pre_handler) {
|
||||
if (kp->pre_handler && !kprobe_gone(kp)) {
|
||||
set_kprobe_instance(kp);
|
||||
if (kp->pre_handler(kp, regs))
|
||||
return 1;
|
||||
|
|
@ -326,7 +343,7 @@ static void __kprobes aggr_post_handler(struct kprobe *p, struct pt_regs *regs,
|
|||
struct kprobe *kp;
|
||||
|
||||
list_for_each_entry_rcu(kp, &p->list, list) {
|
||||
if (kp->post_handler) {
|
||||
if (kp->post_handler && !kprobe_gone(kp)) {
|
||||
set_kprobe_instance(kp);
|
||||
kp->post_handler(kp, regs, flags);
|
||||
reset_kprobe_instance();
|
||||
|
|
@ -393,7 +410,7 @@ void __kprobes recycle_rp_inst(struct kretprobe_instance *ri,
|
|||
hlist_add_head(&ri->hlist, head);
|
||||
}
|
||||
|
||||
void kretprobe_hash_lock(struct task_struct *tsk,
|
||||
void __kprobes kretprobe_hash_lock(struct task_struct *tsk,
|
||||
struct hlist_head **head, unsigned long *flags)
|
||||
{
|
||||
unsigned long hash = hash_ptr(tsk, KPROBE_HASH_BITS);
|
||||
|
|
@ -404,13 +421,15 @@ void kretprobe_hash_lock(struct task_struct *tsk,
|
|||
spin_lock_irqsave(hlist_lock, *flags);
|
||||
}
|
||||
|
||||
static void kretprobe_table_lock(unsigned long hash, unsigned long *flags)
|
||||
static void __kprobes kretprobe_table_lock(unsigned long hash,
|
||||
unsigned long *flags)
|
||||
{
|
||||
spinlock_t *hlist_lock = kretprobe_table_lock_ptr(hash);
|
||||
spin_lock_irqsave(hlist_lock, *flags);
|
||||
}
|
||||
|
||||
void kretprobe_hash_unlock(struct task_struct *tsk, unsigned long *flags)
|
||||
void __kprobes kretprobe_hash_unlock(struct task_struct *tsk,
|
||||
unsigned long *flags)
|
||||
{
|
||||
unsigned long hash = hash_ptr(tsk, KPROBE_HASH_BITS);
|
||||
spinlock_t *hlist_lock;
|
||||
|
|
@ -419,7 +438,7 @@ void kretprobe_hash_unlock(struct task_struct *tsk, unsigned long *flags)
|
|||
spin_unlock_irqrestore(hlist_lock, *flags);
|
||||
}
|
||||
|
||||
void kretprobe_table_unlock(unsigned long hash, unsigned long *flags)
|
||||
void __kprobes kretprobe_table_unlock(unsigned long hash, unsigned long *flags)
|
||||
{
|
||||
spinlock_t *hlist_lock = kretprobe_table_lock_ptr(hash);
|
||||
spin_unlock_irqrestore(hlist_lock, *flags);
|
||||
|
|
@ -526,9 +545,10 @@ static inline void add_aggr_kprobe(struct kprobe *ap, struct kprobe *p)
|
|||
ap->addr = p->addr;
|
||||
ap->pre_handler = aggr_pre_handler;
|
||||
ap->fault_handler = aggr_fault_handler;
|
||||
if (p->post_handler)
|
||||
/* We don't care the kprobe which has gone. */
|
||||
if (p->post_handler && !kprobe_gone(p))
|
||||
ap->post_handler = aggr_post_handler;
|
||||
if (p->break_handler)
|
||||
if (p->break_handler && !kprobe_gone(p))
|
||||
ap->break_handler = aggr_break_handler;
|
||||
|
||||
INIT_LIST_HEAD(&ap->list);
|
||||
|
|
@ -547,17 +567,41 @@ static int __kprobes register_aggr_kprobe(struct kprobe *old_p,
|
|||
int ret = 0;
|
||||
struct kprobe *ap;
|
||||
|
||||
if (kprobe_gone(old_p)) {
|
||||
/*
|
||||
* Attempting to insert new probe at the same location that
|
||||
* had a probe in the module vaddr area which already
|
||||
* freed. So, the instruction slot has already been
|
||||
* released. We need a new slot for the new probe.
|
||||
*/
|
||||
ret = arch_prepare_kprobe(old_p);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
if (old_p->pre_handler == aggr_pre_handler) {
|
||||
copy_kprobe(old_p, p);
|
||||
ret = add_new_kprobe(old_p, p);
|
||||
ap = old_p;
|
||||
} else {
|
||||
ap = kzalloc(sizeof(struct kprobe), GFP_KERNEL);
|
||||
if (!ap)
|
||||
if (!ap) {
|
||||
if (kprobe_gone(old_p))
|
||||
arch_remove_kprobe(old_p);
|
||||
return -ENOMEM;
|
||||
}
|
||||
add_aggr_kprobe(ap, old_p);
|
||||
copy_kprobe(ap, p);
|
||||
ret = add_new_kprobe(ap, p);
|
||||
}
|
||||
if (kprobe_gone(old_p)) {
|
||||
/*
|
||||
* If the old_p has gone, its breakpoint has been disarmed.
|
||||
* We have to arm it again after preparing real kprobes.
|
||||
*/
|
||||
ap->flags &= ~KPROBE_FLAG_GONE;
|
||||
if (kprobe_enabled)
|
||||
arch_arm_kprobe(ap);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -600,8 +644,7 @@ static kprobe_opcode_t __kprobes *kprobe_addr(struct kprobe *p)
|
|||
return (kprobe_opcode_t *)(((char *)addr) + p->offset);
|
||||
}
|
||||
|
||||
static int __kprobes __register_kprobe(struct kprobe *p,
|
||||
unsigned long called_from)
|
||||
int __kprobes register_kprobe(struct kprobe *p)
|
||||
{
|
||||
int ret = 0;
|
||||
struct kprobe *old_p;
|
||||
|
|
@ -620,28 +663,30 @@ static int __kprobes __register_kprobe(struct kprobe *p,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
p->mod_refcounted = 0;
|
||||
|
||||
p->flags = 0;
|
||||
/*
|
||||
* Check if are we probing a module.
|
||||
*/
|
||||
probed_mod = __module_text_address((unsigned long) p->addr);
|
||||
if (probed_mod) {
|
||||
struct module *calling_mod;
|
||||
calling_mod = __module_text_address(called_from);
|
||||
/*
|
||||
* We must allow modules to probe themself and in this case
|
||||
* avoid incrementing the module refcount, so as to allow
|
||||
* unloading of self probing modules.
|
||||
* We must hold a refcount of the probed module while updating
|
||||
* its code to prohibit unexpected unloading.
|
||||
*/
|
||||
if (calling_mod && calling_mod != probed_mod) {
|
||||
if (unlikely(!try_module_get(probed_mod))) {
|
||||
preempt_enable();
|
||||
return -EINVAL;
|
||||
}
|
||||
p->mod_refcounted = 1;
|
||||
} else
|
||||
probed_mod = NULL;
|
||||
if (unlikely(!try_module_get(probed_mod))) {
|
||||
preempt_enable();
|
||||
return -EINVAL;
|
||||
}
|
||||
/*
|
||||
* If the module freed .init.text, we couldn't insert
|
||||
* kprobes in there.
|
||||
*/
|
||||
if (within_module_init((unsigned long)p->addr, probed_mod) &&
|
||||
probed_mod->state != MODULE_STATE_COMING) {
|
||||
module_put(probed_mod);
|
||||
preempt_enable();
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
preempt_enable();
|
||||
|
||||
|
|
@ -668,8 +713,9 @@ static int __kprobes __register_kprobe(struct kprobe *p,
|
|||
out:
|
||||
mutex_unlock(&kprobe_mutex);
|
||||
|
||||
if (ret && probed_mod)
|
||||
if (probed_mod)
|
||||
module_put(probed_mod);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -697,16 +743,16 @@ valid_p:
|
|||
list_is_singular(&old_p->list))) {
|
||||
/*
|
||||
* Only probe on the hash list. Disarm only if kprobes are
|
||||
* enabled - otherwise, the breakpoint would already have
|
||||
* been removed. We save on flushing icache.
|
||||
* enabled and not gone - otherwise, the breakpoint would
|
||||
* already have been removed. We save on flushing icache.
|
||||
*/
|
||||
if (kprobe_enabled)
|
||||
if (kprobe_enabled && !kprobe_gone(old_p))
|
||||
arch_disarm_kprobe(p);
|
||||
hlist_del_rcu(&old_p->hlist);
|
||||
} else {
|
||||
if (p->break_handler)
|
||||
if (p->break_handler && !kprobe_gone(p))
|
||||
old_p->break_handler = NULL;
|
||||
if (p->post_handler) {
|
||||
if (p->post_handler && !kprobe_gone(p)) {
|
||||
list_for_each_entry_rcu(list_p, &old_p->list, list) {
|
||||
if ((list_p != p) && (list_p->post_handler))
|
||||
goto noclean;
|
||||
|
|
@ -721,39 +767,27 @@ noclean:
|
|||
|
||||
static void __kprobes __unregister_kprobe_bottom(struct kprobe *p)
|
||||
{
|
||||
struct module *mod;
|
||||
struct kprobe *old_p;
|
||||
|
||||
if (p->mod_refcounted) {
|
||||
/*
|
||||
* Since we've already incremented refcount,
|
||||
* we don't need to disable preemption.
|
||||
*/
|
||||
mod = module_text_address((unsigned long)p->addr);
|
||||
if (mod)
|
||||
module_put(mod);
|
||||
}
|
||||
|
||||
if (list_empty(&p->list) || list_is_singular(&p->list)) {
|
||||
if (!list_empty(&p->list)) {
|
||||
/* "p" is the last child of an aggr_kprobe */
|
||||
old_p = list_entry(p->list.next, struct kprobe, list);
|
||||
list_del(&p->list);
|
||||
kfree(old_p);
|
||||
}
|
||||
if (list_empty(&p->list))
|
||||
arch_remove_kprobe(p);
|
||||
else if (list_is_singular(&p->list)) {
|
||||
/* "p" is the last child of an aggr_kprobe */
|
||||
old_p = list_entry(p->list.next, struct kprobe, list);
|
||||
list_del(&p->list);
|
||||
arch_remove_kprobe(old_p);
|
||||
kfree(old_p);
|
||||
}
|
||||
}
|
||||
|
||||
static int __register_kprobes(struct kprobe **kps, int num,
|
||||
unsigned long called_from)
|
||||
int __kprobes register_kprobes(struct kprobe **kps, int num)
|
||||
{
|
||||
int i, ret = 0;
|
||||
|
||||
if (num <= 0)
|
||||
return -EINVAL;
|
||||
for (i = 0; i < num; i++) {
|
||||
ret = __register_kprobe(kps[i], called_from);
|
||||
ret = register_kprobe(kps[i]);
|
||||
if (ret < 0) {
|
||||
if (i > 0)
|
||||
unregister_kprobes(kps, i);
|
||||
|
|
@ -763,26 +797,11 @@ static int __register_kprobes(struct kprobe **kps, int num,
|
|||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Registration and unregistration functions for kprobe.
|
||||
*/
|
||||
int __kprobes register_kprobe(struct kprobe *p)
|
||||
{
|
||||
return __register_kprobes(&p, 1,
|
||||
(unsigned long)__builtin_return_address(0));
|
||||
}
|
||||
|
||||
void __kprobes unregister_kprobe(struct kprobe *p)
|
||||
{
|
||||
unregister_kprobes(&p, 1);
|
||||
}
|
||||
|
||||
int __kprobes register_kprobes(struct kprobe **kps, int num)
|
||||
{
|
||||
return __register_kprobes(kps, num,
|
||||
(unsigned long)__builtin_return_address(0));
|
||||
}
|
||||
|
||||
void __kprobes unregister_kprobes(struct kprobe **kps, int num)
|
||||
{
|
||||
int i;
|
||||
|
|
@ -811,8 +830,7 @@ unsigned long __weak arch_deref_entry_point(void *entry)
|
|||
return (unsigned long)entry;
|
||||
}
|
||||
|
||||
static int __register_jprobes(struct jprobe **jps, int num,
|
||||
unsigned long called_from)
|
||||
int __kprobes register_jprobes(struct jprobe **jps, int num)
|
||||
{
|
||||
struct jprobe *jp;
|
||||
int ret = 0, i;
|
||||
|
|
@ -830,7 +848,7 @@ static int __register_jprobes(struct jprobe **jps, int num,
|
|||
/* Todo: Verify probepoint is a function entry point */
|
||||
jp->kp.pre_handler = setjmp_pre_handler;
|
||||
jp->kp.break_handler = longjmp_break_handler;
|
||||
ret = __register_kprobe(&jp->kp, called_from);
|
||||
ret = register_kprobe(&jp->kp);
|
||||
}
|
||||
if (ret < 0) {
|
||||
if (i > 0)
|
||||
|
|
@ -843,8 +861,7 @@ static int __register_jprobes(struct jprobe **jps, int num,
|
|||
|
||||
int __kprobes register_jprobe(struct jprobe *jp)
|
||||
{
|
||||
return __register_jprobes(&jp, 1,
|
||||
(unsigned long)__builtin_return_address(0));
|
||||
return register_jprobes(&jp, 1);
|
||||
}
|
||||
|
||||
void __kprobes unregister_jprobe(struct jprobe *jp)
|
||||
|
|
@ -852,12 +869,6 @@ void __kprobes unregister_jprobe(struct jprobe *jp)
|
|||
unregister_jprobes(&jp, 1);
|
||||
}
|
||||
|
||||
int __kprobes register_jprobes(struct jprobe **jps, int num)
|
||||
{
|
||||
return __register_jprobes(jps, num,
|
||||
(unsigned long)__builtin_return_address(0));
|
||||
}
|
||||
|
||||
void __kprobes unregister_jprobes(struct jprobe **jps, int num)
|
||||
{
|
||||
int i;
|
||||
|
|
@ -920,8 +931,7 @@ static int __kprobes pre_handler_kretprobe(struct kprobe *p,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int __kprobes __register_kretprobe(struct kretprobe *rp,
|
||||
unsigned long called_from)
|
||||
int __kprobes register_kretprobe(struct kretprobe *rp)
|
||||
{
|
||||
int ret = 0;
|
||||
struct kretprobe_instance *inst;
|
||||
|
|
@ -967,21 +977,20 @@ static int __kprobes __register_kretprobe(struct kretprobe *rp,
|
|||
|
||||
rp->nmissed = 0;
|
||||
/* Establish function entry probe point */
|
||||
ret = __register_kprobe(&rp->kp, called_from);
|
||||
ret = register_kprobe(&rp->kp);
|
||||
if (ret != 0)
|
||||
free_rp_inst(rp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __register_kretprobes(struct kretprobe **rps, int num,
|
||||
unsigned long called_from)
|
||||
int __kprobes register_kretprobes(struct kretprobe **rps, int num)
|
||||
{
|
||||
int ret = 0, i;
|
||||
|
||||
if (num <= 0)
|
||||
return -EINVAL;
|
||||
for (i = 0; i < num; i++) {
|
||||
ret = __register_kretprobe(rps[i], called_from);
|
||||
ret = register_kretprobe(rps[i]);
|
||||
if (ret < 0) {
|
||||
if (i > 0)
|
||||
unregister_kretprobes(rps, i);
|
||||
|
|
@ -991,23 +1000,11 @@ static int __register_kretprobes(struct kretprobe **rps, int num,
|
|||
return ret;
|
||||
}
|
||||
|
||||
int __kprobes register_kretprobe(struct kretprobe *rp)
|
||||
{
|
||||
return __register_kretprobes(&rp, 1,
|
||||
(unsigned long)__builtin_return_address(0));
|
||||
}
|
||||
|
||||
void __kprobes unregister_kretprobe(struct kretprobe *rp)
|
||||
{
|
||||
unregister_kretprobes(&rp, 1);
|
||||
}
|
||||
|
||||
int __kprobes register_kretprobes(struct kretprobe **rps, int num)
|
||||
{
|
||||
return __register_kretprobes(rps, num,
|
||||
(unsigned long)__builtin_return_address(0));
|
||||
}
|
||||
|
||||
void __kprobes unregister_kretprobes(struct kretprobe **rps, int num)
|
||||
{
|
||||
int i;
|
||||
|
|
@ -1055,6 +1052,72 @@ static int __kprobes pre_handler_kretprobe(struct kprobe *p,
|
|||
|
||||
#endif /* CONFIG_KRETPROBES */
|
||||
|
||||
/* Set the kprobe gone and remove its instruction buffer. */
|
||||
static void __kprobes kill_kprobe(struct kprobe *p)
|
||||
{
|
||||
struct kprobe *kp;
|
||||
p->flags |= KPROBE_FLAG_GONE;
|
||||
if (p->pre_handler == aggr_pre_handler) {
|
||||
/*
|
||||
* If this is an aggr_kprobe, we have to list all the
|
||||
* chained probes and mark them GONE.
|
||||
*/
|
||||
list_for_each_entry_rcu(kp, &p->list, list)
|
||||
kp->flags |= KPROBE_FLAG_GONE;
|
||||
p->post_handler = NULL;
|
||||
p->break_handler = NULL;
|
||||
}
|
||||
/*
|
||||
* Here, we can remove insn_slot safely, because no thread calls
|
||||
* the original probed function (which will be freed soon) any more.
|
||||
*/
|
||||
arch_remove_kprobe(p);
|
||||
}
|
||||
|
||||
/* Module notifier call back, checking kprobes on the module */
|
||||
static int __kprobes kprobes_module_callback(struct notifier_block *nb,
|
||||
unsigned long val, void *data)
|
||||
{
|
||||
struct module *mod = data;
|
||||
struct hlist_head *head;
|
||||
struct hlist_node *node;
|
||||
struct kprobe *p;
|
||||
unsigned int i;
|
||||
int checkcore = (val == MODULE_STATE_GOING);
|
||||
|
||||
if (val != MODULE_STATE_GOING && val != MODULE_STATE_LIVE)
|
||||
return NOTIFY_DONE;
|
||||
|
||||
/*
|
||||
* When MODULE_STATE_GOING was notified, both of module .text and
|
||||
* .init.text sections would be freed. When MODULE_STATE_LIVE was
|
||||
* notified, only .init.text section would be freed. We need to
|
||||
* disable kprobes which have been inserted in the sections.
|
||||
*/
|
||||
mutex_lock(&kprobe_mutex);
|
||||
for (i = 0; i < KPROBE_TABLE_SIZE; i++) {
|
||||
head = &kprobe_table[i];
|
||||
hlist_for_each_entry_rcu(p, node, head, hlist)
|
||||
if (within_module_init((unsigned long)p->addr, mod) ||
|
||||
(checkcore &&
|
||||
within_module_core((unsigned long)p->addr, mod))) {
|
||||
/*
|
||||
* The vaddr this probe is installed will soon
|
||||
* be vfreed buy not synced to disk. Hence,
|
||||
* disarming the breakpoint isn't needed.
|
||||
*/
|
||||
kill_kprobe(p);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&kprobe_mutex);
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static struct notifier_block kprobe_module_nb = {
|
||||
.notifier_call = kprobes_module_callback,
|
||||
.priority = 0
|
||||
};
|
||||
|
||||
static int __init init_kprobes(void)
|
||||
{
|
||||
int i, err = 0;
|
||||
|
|
@ -1111,6 +1174,9 @@ static int __init init_kprobes(void)
|
|||
err = arch_init_kprobes();
|
||||
if (!err)
|
||||
err = register_die_notifier(&kprobe_exceptions_nb);
|
||||
if (!err)
|
||||
err = register_module_notifier(&kprobe_module_nb);
|
||||
|
||||
kprobes_initialized = (err == 0);
|
||||
|
||||
if (!err)
|
||||
|
|
@ -1131,10 +1197,12 @@ static void __kprobes report_probe(struct seq_file *pi, struct kprobe *p,
|
|||
else
|
||||
kprobe_type = "k";
|
||||
if (sym)
|
||||
seq_printf(pi, "%p %s %s+0x%x %s\n", p->addr, kprobe_type,
|
||||
sym, offset, (modname ? modname : " "));
|
||||
seq_printf(pi, "%p %s %s+0x%x %s %s\n", p->addr, kprobe_type,
|
||||
sym, offset, (modname ? modname : " "),
|
||||
(kprobe_gone(p) ? "[GONE]" : ""));
|
||||
else
|
||||
seq_printf(pi, "%p %s %p\n", p->addr, kprobe_type, p->addr);
|
||||
seq_printf(pi, "%p %s %p %s\n", p->addr, kprobe_type, p->addr,
|
||||
(kprobe_gone(p) ? "[GONE]" : ""));
|
||||
}
|
||||
|
||||
static void __kprobes *kprobe_seq_start(struct seq_file *f, loff_t *pos)
|
||||
|
|
@ -1215,7 +1283,8 @@ static void __kprobes enable_all_kprobes(void)
|
|||
for (i = 0; i < KPROBE_TABLE_SIZE; i++) {
|
||||
head = &kprobe_table[i];
|
||||
hlist_for_each_entry_rcu(p, node, head, hlist)
|
||||
arch_arm_kprobe(p);
|
||||
if (!kprobe_gone(p))
|
||||
arch_arm_kprobe(p);
|
||||
}
|
||||
|
||||
kprobe_enabled = true;
|
||||
|
|
@ -1244,7 +1313,7 @@ static void __kprobes disable_all_kprobes(void)
|
|||
for (i = 0; i < KPROBE_TABLE_SIZE; i++) {
|
||||
head = &kprobe_table[i];
|
||||
hlist_for_each_entry_rcu(p, node, head, hlist) {
|
||||
if (!arch_trampoline_kprobe(p))
|
||||
if (!arch_trampoline_kprobe(p) && !kprobe_gone(p))
|
||||
arch_disarm_kprobe(p);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ static struct kobj_attribute _name##_attr = __ATTR_RO(_name)
|
|||
static struct kobj_attribute _name##_attr = \
|
||||
__ATTR(_name, 0644, _name##_show, _name##_store)
|
||||
|
||||
#if defined(CONFIG_HOTPLUG) && defined(CONFIG_NET)
|
||||
#if defined(CONFIG_HOTPLUG)
|
||||
/* current uevent sequence number */
|
||||
static ssize_t uevent_seqnum_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf)
|
||||
|
|
@ -137,7 +137,7 @@ struct kobject *kernel_kobj;
|
|||
EXPORT_SYMBOL_GPL(kernel_kobj);
|
||||
|
||||
static struct attribute * kernel_attrs[] = {
|
||||
#if defined(CONFIG_HOTPLUG) && defined(CONFIG_NET)
|
||||
#if defined(CONFIG_HOTPLUG)
|
||||
&uevent_seqnum_attr.attr,
|
||||
&uevent_helper_attr.attr,
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -21,6 +21,9 @@ static DEFINE_SPINLOCK(kthread_create_lock);
|
|||
static LIST_HEAD(kthread_create_list);
|
||||
struct task_struct *kthreadd_task;
|
||||
|
||||
DEFINE_TRACE(sched_kthread_stop);
|
||||
DEFINE_TRACE(sched_kthread_stop_ret);
|
||||
|
||||
struct kthread_create_info
|
||||
{
|
||||
/* Information passed to kthread() from kthreadd. */
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
* Thanks to Arjan van de Ven for coming up with the initial idea of
|
||||
* mapping lock dependencies runtime.
|
||||
*/
|
||||
#define DISABLE_BRANCH_PROFILING
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/delay.h>
|
||||
|
|
@ -136,16 +137,16 @@ static inline struct lock_class *hlock_class(struct held_lock *hlock)
|
|||
#ifdef CONFIG_LOCK_STAT
|
||||
static DEFINE_PER_CPU(struct lock_class_stats[MAX_LOCKDEP_KEYS], lock_stats);
|
||||
|
||||
static int lock_contention_point(struct lock_class *class, unsigned long ip)
|
||||
static int lock_point(unsigned long points[], unsigned long ip)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(class->contention_point); i++) {
|
||||
if (class->contention_point[i] == 0) {
|
||||
class->contention_point[i] = ip;
|
||||
for (i = 0; i < LOCKSTAT_POINTS; i++) {
|
||||
if (points[i] == 0) {
|
||||
points[i] = ip;
|
||||
break;
|
||||
}
|
||||
if (class->contention_point[i] == ip)
|
||||
if (points[i] == ip)
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -185,6 +186,9 @@ struct lock_class_stats lock_stats(struct lock_class *class)
|
|||
for (i = 0; i < ARRAY_SIZE(stats.contention_point); i++)
|
||||
stats.contention_point[i] += pcs->contention_point[i];
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(stats.contending_point); i++)
|
||||
stats.contending_point[i] += pcs->contending_point[i];
|
||||
|
||||
lock_time_add(&pcs->read_waittime, &stats.read_waittime);
|
||||
lock_time_add(&pcs->write_waittime, &stats.write_waittime);
|
||||
|
||||
|
|
@ -209,6 +213,7 @@ void clear_lock_stats(struct lock_class *class)
|
|||
memset(cpu_stats, 0, sizeof(struct lock_class_stats));
|
||||
}
|
||||
memset(class->contention_point, 0, sizeof(class->contention_point));
|
||||
memset(class->contending_point, 0, sizeof(class->contending_point));
|
||||
}
|
||||
|
||||
static struct lock_class_stats *get_lock_stats(struct lock_class *class)
|
||||
|
|
@ -287,14 +292,12 @@ void lockdep_off(void)
|
|||
{
|
||||
current->lockdep_recursion++;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(lockdep_off);
|
||||
|
||||
void lockdep_on(void)
|
||||
{
|
||||
current->lockdep_recursion--;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(lockdep_on);
|
||||
|
||||
/*
|
||||
|
|
@ -576,7 +579,8 @@ static void print_lock_class_header(struct lock_class *class, int depth)
|
|||
/*
|
||||
* printk all lock dependencies starting at <entry>:
|
||||
*/
|
||||
static void print_lock_dependencies(struct lock_class *class, int depth)
|
||||
static void __used
|
||||
print_lock_dependencies(struct lock_class *class, int depth)
|
||||
{
|
||||
struct lock_list *entry;
|
||||
|
||||
|
|
@ -2508,7 +2512,6 @@ void lockdep_init_map(struct lockdep_map *lock, const char *name,
|
|||
if (subclass)
|
||||
register_lock_class(lock, subclass, 1);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(lockdep_init_map);
|
||||
|
||||
/*
|
||||
|
|
@ -2689,8 +2692,9 @@ static int check_unlock(struct task_struct *curr, struct lockdep_map *lock,
|
|||
}
|
||||
|
||||
static int
|
||||
__lock_set_subclass(struct lockdep_map *lock,
|
||||
unsigned int subclass, unsigned long ip)
|
||||
__lock_set_class(struct lockdep_map *lock, const char *name,
|
||||
struct lock_class_key *key, unsigned int subclass,
|
||||
unsigned long ip)
|
||||
{
|
||||
struct task_struct *curr = current;
|
||||
struct held_lock *hlock, *prev_hlock;
|
||||
|
|
@ -2717,6 +2721,7 @@ __lock_set_subclass(struct lockdep_map *lock,
|
|||
return print_unlock_inbalance_bug(curr, lock, ip);
|
||||
|
||||
found_it:
|
||||
lockdep_init_map(lock, name, key, 0);
|
||||
class = register_lock_class(lock, subclass, 0);
|
||||
hlock->class_idx = class - lock_classes + 1;
|
||||
|
||||
|
|
@ -2901,9 +2906,9 @@ static void check_flags(unsigned long flags)
|
|||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
lock_set_subclass(struct lockdep_map *lock,
|
||||
unsigned int subclass, unsigned long ip)
|
||||
void lock_set_class(struct lockdep_map *lock, const char *name,
|
||||
struct lock_class_key *key, unsigned int subclass,
|
||||
unsigned long ip)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
|
|
@ -2913,13 +2918,12 @@ lock_set_subclass(struct lockdep_map *lock,
|
|||
raw_local_irq_save(flags);
|
||||
current->lockdep_recursion = 1;
|
||||
check_flags(flags);
|
||||
if (__lock_set_subclass(lock, subclass, ip))
|
||||
if (__lock_set_class(lock, name, key, subclass, ip))
|
||||
check_chain_key(current);
|
||||
current->lockdep_recursion = 0;
|
||||
raw_local_irq_restore(flags);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(lock_set_subclass);
|
||||
EXPORT_SYMBOL_GPL(lock_set_class);
|
||||
|
||||
/*
|
||||
* We are not always called with irqs disabled - do that here,
|
||||
|
|
@ -2943,7 +2947,6 @@ void lock_acquire(struct lockdep_map *lock, unsigned int subclass,
|
|||
current->lockdep_recursion = 0;
|
||||
raw_local_irq_restore(flags);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(lock_acquire);
|
||||
|
||||
void lock_release(struct lockdep_map *lock, int nested,
|
||||
|
|
@ -2961,7 +2964,6 @@ void lock_release(struct lockdep_map *lock, int nested,
|
|||
current->lockdep_recursion = 0;
|
||||
raw_local_irq_restore(flags);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(lock_release);
|
||||
|
||||
#ifdef CONFIG_LOCK_STAT
|
||||
|
|
@ -2999,7 +3001,7 @@ __lock_contended(struct lockdep_map *lock, unsigned long ip)
|
|||
struct held_lock *hlock, *prev_hlock;
|
||||
struct lock_class_stats *stats;
|
||||
unsigned int depth;
|
||||
int i, point;
|
||||
int i, contention_point, contending_point;
|
||||
|
||||
depth = curr->lockdep_depth;
|
||||
if (DEBUG_LOCKS_WARN_ON(!depth))
|
||||
|
|
@ -3023,18 +3025,22 @@ __lock_contended(struct lockdep_map *lock, unsigned long ip)
|
|||
found_it:
|
||||
hlock->waittime_stamp = sched_clock();
|
||||
|
||||
point = lock_contention_point(hlock_class(hlock), ip);
|
||||
contention_point = lock_point(hlock_class(hlock)->contention_point, ip);
|
||||
contending_point = lock_point(hlock_class(hlock)->contending_point,
|
||||
lock->ip);
|
||||
|
||||
stats = get_lock_stats(hlock_class(hlock));
|
||||
if (point < ARRAY_SIZE(stats->contention_point))
|
||||
stats->contention_point[point]++;
|
||||
if (contention_point < LOCKSTAT_POINTS)
|
||||
stats->contention_point[contention_point]++;
|
||||
if (contending_point < LOCKSTAT_POINTS)
|
||||
stats->contending_point[contending_point]++;
|
||||
if (lock->cpu != smp_processor_id())
|
||||
stats->bounces[bounce_contended + !!hlock->read]++;
|
||||
put_lock_stats(stats);
|
||||
}
|
||||
|
||||
static void
|
||||
__lock_acquired(struct lockdep_map *lock)
|
||||
__lock_acquired(struct lockdep_map *lock, unsigned long ip)
|
||||
{
|
||||
struct task_struct *curr = current;
|
||||
struct held_lock *hlock, *prev_hlock;
|
||||
|
|
@ -3083,6 +3089,7 @@ found_it:
|
|||
put_lock_stats(stats);
|
||||
|
||||
lock->cpu = cpu;
|
||||
lock->ip = ip;
|
||||
}
|
||||
|
||||
void lock_contended(struct lockdep_map *lock, unsigned long ip)
|
||||
|
|
@ -3104,7 +3111,7 @@ void lock_contended(struct lockdep_map *lock, unsigned long ip)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(lock_contended);
|
||||
|
||||
void lock_acquired(struct lockdep_map *lock)
|
||||
void lock_acquired(struct lockdep_map *lock, unsigned long ip)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
|
|
@ -3117,7 +3124,7 @@ void lock_acquired(struct lockdep_map *lock)
|
|||
raw_local_irq_save(flags);
|
||||
check_flags(flags);
|
||||
current->lockdep_recursion = 1;
|
||||
__lock_acquired(lock);
|
||||
__lock_acquired(lock, ip);
|
||||
current->lockdep_recursion = 0;
|
||||
raw_local_irq_restore(flags);
|
||||
}
|
||||
|
|
@ -3441,7 +3448,6 @@ retry:
|
|||
if (unlock)
|
||||
read_unlock(&tasklist_lock);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(debug_show_all_locks);
|
||||
|
||||
/*
|
||||
|
|
@ -3462,7 +3468,6 @@ void debug_show_held_locks(struct task_struct *task)
|
|||
{
|
||||
__debug_show_held_locks(task);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(debug_show_held_locks);
|
||||
|
||||
void lockdep_sys_exit(void)
|
||||
|
|
|
|||
|
|
@ -470,11 +470,12 @@ static void seq_line(struct seq_file *m, char c, int offset, int length)
|
|||
|
||||
static void snprint_time(char *buf, size_t bufsiz, s64 nr)
|
||||
{
|
||||
unsigned long rem;
|
||||
s64 div;
|
||||
s32 rem;
|
||||
|
||||
nr += 5; /* for display rounding */
|
||||
rem = do_div(nr, 1000); /* XXX: do_div_signed */
|
||||
snprintf(buf, bufsiz, "%lld.%02d", (long long)nr, (int)rem/10);
|
||||
div = div_s64_rem(nr, 1000, &rem);
|
||||
snprintf(buf, bufsiz, "%lld.%02d", (long long)div, (int)rem/10);
|
||||
}
|
||||
|
||||
static void seq_time(struct seq_file *m, s64 time)
|
||||
|
|
@ -556,7 +557,7 @@ static void seq_stats(struct seq_file *m, struct lock_stat_data *data)
|
|||
if (stats->read_holdtime.nr)
|
||||
namelen += 2;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(class->contention_point); i++) {
|
||||
for (i = 0; i < LOCKSTAT_POINTS; i++) {
|
||||
char sym[KSYM_SYMBOL_LEN];
|
||||
char ip[32];
|
||||
|
||||
|
|
@ -573,6 +574,23 @@ static void seq_stats(struct seq_file *m, struct lock_stat_data *data)
|
|||
stats->contention_point[i],
|
||||
ip, sym);
|
||||
}
|
||||
for (i = 0; i < LOCKSTAT_POINTS; i++) {
|
||||
char sym[KSYM_SYMBOL_LEN];
|
||||
char ip[32];
|
||||
|
||||
if (class->contending_point[i] == 0)
|
||||
break;
|
||||
|
||||
if (!i)
|
||||
seq_line(m, '-', 40-namelen, namelen);
|
||||
|
||||
sprint_symbol(sym, class->contending_point[i]);
|
||||
snprintf(ip, sizeof(ip), "[<%p>]",
|
||||
(void *)class->contending_point[i]);
|
||||
seq_printf(m, "%40s %14lu %29s %s\n", name,
|
||||
stats->contending_point[i],
|
||||
ip, sym);
|
||||
}
|
||||
if (i) {
|
||||
seq_puts(m, "\n");
|
||||
seq_line(m, '.', 0, 40 + 1 + 10 * (14 + 1));
|
||||
|
|
@ -582,7 +600,7 @@ static void seq_stats(struct seq_file *m, struct lock_stat_data *data)
|
|||
|
||||
static void seq_header(struct seq_file *m)
|
||||
{
|
||||
seq_printf(m, "lock_stat version 0.2\n");
|
||||
seq_printf(m, "lock_stat version 0.3\n");
|
||||
seq_line(m, '-', 0, 40 + 1 + 10 * (14 + 1));
|
||||
seq_printf(m, "%40s %14s %14s %14s %14s %14s %14s %14s %14s "
|
||||
"%14s %14s\n",
|
||||
|
|
|
|||
192
kernel/marker.c
192
kernel/marker.c
|
|
@ -43,6 +43,7 @@ static DEFINE_MUTEX(markers_mutex);
|
|||
*/
|
||||
#define MARKER_HASH_BITS 6
|
||||
#define MARKER_TABLE_SIZE (1 << MARKER_HASH_BITS)
|
||||
static struct hlist_head marker_table[MARKER_TABLE_SIZE];
|
||||
|
||||
/*
|
||||
* Note about RCU :
|
||||
|
|
@ -64,11 +65,10 @@ struct marker_entry {
|
|||
void *oldptr;
|
||||
int rcu_pending;
|
||||
unsigned char ptype:1;
|
||||
unsigned char format_allocated:1;
|
||||
char name[0]; /* Contains name'\0'format'\0' */
|
||||
};
|
||||
|
||||
static struct hlist_head marker_table[MARKER_TABLE_SIZE];
|
||||
|
||||
/**
|
||||
* __mark_empty_function - Empty probe callback
|
||||
* @probe_private: probe private data
|
||||
|
|
@ -81,7 +81,7 @@ static struct hlist_head marker_table[MARKER_TABLE_SIZE];
|
|||
* though the function pointer change and the marker enabling are two distinct
|
||||
* operations that modifies the execution flow of preemptible code.
|
||||
*/
|
||||
void __mark_empty_function(void *probe_private, void *call_private,
|
||||
notrace void __mark_empty_function(void *probe_private, void *call_private,
|
||||
const char *fmt, va_list *args)
|
||||
{
|
||||
}
|
||||
|
|
@ -97,7 +97,8 @@ EXPORT_SYMBOL_GPL(__mark_empty_function);
|
|||
* need to put a full smp_rmb() in this branch. This is why we do not use
|
||||
* rcu_dereference() for the pointer read.
|
||||
*/
|
||||
void marker_probe_cb(const struct marker *mdata, void *call_private, ...)
|
||||
notrace void marker_probe_cb(const struct marker *mdata,
|
||||
void *call_private, ...)
|
||||
{
|
||||
va_list args;
|
||||
char ptype;
|
||||
|
|
@ -107,7 +108,7 @@ void marker_probe_cb(const struct marker *mdata, void *call_private, ...)
|
|||
* sure the teardown of the callbacks can be done correctly when they
|
||||
* are in modules and they insure RCU read coherency.
|
||||
*/
|
||||
rcu_read_lock_sched();
|
||||
rcu_read_lock_sched_notrace();
|
||||
ptype = mdata->ptype;
|
||||
if (likely(!ptype)) {
|
||||
marker_probe_func *func;
|
||||
|
|
@ -145,7 +146,7 @@ void marker_probe_cb(const struct marker *mdata, void *call_private, ...)
|
|||
va_end(args);
|
||||
}
|
||||
}
|
||||
rcu_read_unlock_sched();
|
||||
rcu_read_unlock_sched_notrace();
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(marker_probe_cb);
|
||||
|
||||
|
|
@ -157,12 +158,13 @@ EXPORT_SYMBOL_GPL(marker_probe_cb);
|
|||
*
|
||||
* Should be connected to markers "MARK_NOARGS".
|
||||
*/
|
||||
void marker_probe_cb_noarg(const struct marker *mdata, void *call_private, ...)
|
||||
static notrace void marker_probe_cb_noarg(const struct marker *mdata,
|
||||
void *call_private, ...)
|
||||
{
|
||||
va_list args; /* not initialized */
|
||||
char ptype;
|
||||
|
||||
rcu_read_lock_sched();
|
||||
rcu_read_lock_sched_notrace();
|
||||
ptype = mdata->ptype;
|
||||
if (likely(!ptype)) {
|
||||
marker_probe_func *func;
|
||||
|
|
@ -195,9 +197,8 @@ void marker_probe_cb_noarg(const struct marker *mdata, void *call_private, ...)
|
|||
multi[i].func(multi[i].probe_private, call_private,
|
||||
mdata->format, &args);
|
||||
}
|
||||
rcu_read_unlock_sched();
|
||||
rcu_read_unlock_sched_notrace();
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(marker_probe_cb_noarg);
|
||||
|
||||
static void free_old_closure(struct rcu_head *head)
|
||||
{
|
||||
|
|
@ -416,6 +417,7 @@ static struct marker_entry *add_marker(const char *name, const char *format)
|
|||
e->single.probe_private = NULL;
|
||||
e->multi = NULL;
|
||||
e->ptype = 0;
|
||||
e->format_allocated = 0;
|
||||
e->refcount = 0;
|
||||
e->rcu_pending = 0;
|
||||
hlist_add_head(&e->hlist, head);
|
||||
|
|
@ -447,6 +449,8 @@ static int remove_marker(const char *name)
|
|||
if (e->single.func != __mark_empty_function)
|
||||
return -EBUSY;
|
||||
hlist_del(&e->hlist);
|
||||
if (e->format_allocated)
|
||||
kfree(e->format);
|
||||
/* Make sure the call_rcu has been executed */
|
||||
if (e->rcu_pending)
|
||||
rcu_barrier_sched();
|
||||
|
|
@ -457,57 +461,34 @@ static int remove_marker(const char *name)
|
|||
/*
|
||||
* Set the mark_entry format to the format found in the element.
|
||||
*/
|
||||
static int marker_set_format(struct marker_entry **entry, const char *format)
|
||||
static int marker_set_format(struct marker_entry *entry, const char *format)
|
||||
{
|
||||
struct marker_entry *e;
|
||||
size_t name_len = strlen((*entry)->name) + 1;
|
||||
size_t format_len = strlen(format) + 1;
|
||||
|
||||
|
||||
e = kmalloc(sizeof(struct marker_entry) + name_len + format_len,
|
||||
GFP_KERNEL);
|
||||
if (!e)
|
||||
entry->format = kstrdup(format, GFP_KERNEL);
|
||||
if (!entry->format)
|
||||
return -ENOMEM;
|
||||
memcpy(&e->name[0], (*entry)->name, name_len);
|
||||
e->format = &e->name[name_len];
|
||||
memcpy(e->format, format, format_len);
|
||||
if (strcmp(e->format, MARK_NOARGS) == 0)
|
||||
e->call = marker_probe_cb_noarg;
|
||||
else
|
||||
e->call = marker_probe_cb;
|
||||
e->single = (*entry)->single;
|
||||
e->multi = (*entry)->multi;
|
||||
e->ptype = (*entry)->ptype;
|
||||
e->refcount = (*entry)->refcount;
|
||||
e->rcu_pending = 0;
|
||||
hlist_add_before(&e->hlist, &(*entry)->hlist);
|
||||
hlist_del(&(*entry)->hlist);
|
||||
/* Make sure the call_rcu has been executed */
|
||||
if ((*entry)->rcu_pending)
|
||||
rcu_barrier_sched();
|
||||
kfree(*entry);
|
||||
*entry = e;
|
||||
entry->format_allocated = 1;
|
||||
|
||||
trace_mark(core_marker_format, "name %s format %s",
|
||||
e->name, e->format);
|
||||
entry->name, entry->format);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets the probe callback corresponding to one marker.
|
||||
*/
|
||||
static int set_marker(struct marker_entry **entry, struct marker *elem,
|
||||
static int set_marker(struct marker_entry *entry, struct marker *elem,
|
||||
int active)
|
||||
{
|
||||
int ret;
|
||||
WARN_ON(strcmp((*entry)->name, elem->name) != 0);
|
||||
int ret = 0;
|
||||
WARN_ON(strcmp(entry->name, elem->name) != 0);
|
||||
|
||||
if ((*entry)->format) {
|
||||
if (strcmp((*entry)->format, elem->format) != 0) {
|
||||
if (entry->format) {
|
||||
if (strcmp(entry->format, elem->format) != 0) {
|
||||
printk(KERN_NOTICE
|
||||
"Format mismatch for probe %s "
|
||||
"(%s), marker (%s)\n",
|
||||
(*entry)->name,
|
||||
(*entry)->format,
|
||||
entry->name,
|
||||
entry->format,
|
||||
elem->format);
|
||||
return -EPERM;
|
||||
}
|
||||
|
|
@ -523,37 +504,67 @@ static int set_marker(struct marker_entry **entry, struct marker *elem,
|
|||
* pass from a "safe" callback (with argument) to an "unsafe"
|
||||
* callback (does not set arguments).
|
||||
*/
|
||||
elem->call = (*entry)->call;
|
||||
elem->call = entry->call;
|
||||
/*
|
||||
* Sanity check :
|
||||
* We only update the single probe private data when the ptr is
|
||||
* set to a _non_ single probe! (0 -> 1 and N -> 1, N != 1)
|
||||
*/
|
||||
WARN_ON(elem->single.func != __mark_empty_function
|
||||
&& elem->single.probe_private
|
||||
!= (*entry)->single.probe_private &&
|
||||
!elem->ptype);
|
||||
elem->single.probe_private = (*entry)->single.probe_private;
|
||||
&& elem->single.probe_private != entry->single.probe_private
|
||||
&& !elem->ptype);
|
||||
elem->single.probe_private = entry->single.probe_private;
|
||||
/*
|
||||
* Make sure the private data is valid when we update the
|
||||
* single probe ptr.
|
||||
*/
|
||||
smp_wmb();
|
||||
elem->single.func = (*entry)->single.func;
|
||||
elem->single.func = entry->single.func;
|
||||
/*
|
||||
* We also make sure that the new probe callbacks array is consistent
|
||||
* before setting a pointer to it.
|
||||
*/
|
||||
rcu_assign_pointer(elem->multi, (*entry)->multi);
|
||||
rcu_assign_pointer(elem->multi, entry->multi);
|
||||
/*
|
||||
* Update the function or multi probe array pointer before setting the
|
||||
* ptype.
|
||||
*/
|
||||
smp_wmb();
|
||||
elem->ptype = (*entry)->ptype;
|
||||
elem->ptype = entry->ptype;
|
||||
|
||||
if (elem->tp_name && (active ^ elem->state)) {
|
||||
WARN_ON(!elem->tp_cb);
|
||||
/*
|
||||
* It is ok to directly call the probe registration because type
|
||||
* checking has been done in the __trace_mark_tp() macro.
|
||||
*/
|
||||
|
||||
if (active) {
|
||||
/*
|
||||
* try_module_get should always succeed because we hold
|
||||
* lock_module() to get the tp_cb address.
|
||||
*/
|
||||
ret = try_module_get(__module_text_address(
|
||||
(unsigned long)elem->tp_cb));
|
||||
BUG_ON(!ret);
|
||||
ret = tracepoint_probe_register_noupdate(
|
||||
elem->tp_name,
|
||||
elem->tp_cb);
|
||||
} else {
|
||||
ret = tracepoint_probe_unregister_noupdate(
|
||||
elem->tp_name,
|
||||
elem->tp_cb);
|
||||
/*
|
||||
* tracepoint_probe_update_all() must be called
|
||||
* before the module containing tp_cb is unloaded.
|
||||
*/
|
||||
module_put(__module_text_address(
|
||||
(unsigned long)elem->tp_cb));
|
||||
}
|
||||
}
|
||||
elem->state = active;
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -564,7 +575,24 @@ static int set_marker(struct marker_entry **entry, struct marker *elem,
|
|||
*/
|
||||
static void disable_marker(struct marker *elem)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* leave "call" as is. It is known statically. */
|
||||
if (elem->tp_name && elem->state) {
|
||||
WARN_ON(!elem->tp_cb);
|
||||
/*
|
||||
* It is ok to directly call the probe registration because type
|
||||
* checking has been done in the __trace_mark_tp() macro.
|
||||
*/
|
||||
ret = tracepoint_probe_unregister_noupdate(elem->tp_name,
|
||||
elem->tp_cb);
|
||||
WARN_ON(ret);
|
||||
/*
|
||||
* tracepoint_probe_update_all() must be called
|
||||
* before the module containing tp_cb is unloaded.
|
||||
*/
|
||||
module_put(__module_text_address((unsigned long)elem->tp_cb));
|
||||
}
|
||||
elem->state = 0;
|
||||
elem->single.func = __mark_empty_function;
|
||||
/* Update the function before setting the ptype */
|
||||
|
|
@ -594,8 +622,7 @@ void marker_update_probe_range(struct marker *begin,
|
|||
for (iter = begin; iter < end; iter++) {
|
||||
mark_entry = get_marker(iter->name);
|
||||
if (mark_entry) {
|
||||
set_marker(&mark_entry, iter,
|
||||
!!mark_entry->refcount);
|
||||
set_marker(mark_entry, iter, !!mark_entry->refcount);
|
||||
/*
|
||||
* ignore error, continue
|
||||
*/
|
||||
|
|
@ -629,6 +656,7 @@ static void marker_update_probes(void)
|
|||
marker_update_probe_range(__start___markers, __stop___markers);
|
||||
/* Markers in modules. */
|
||||
module_update_markers();
|
||||
tracepoint_probe_update_all();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -657,7 +685,7 @@ int marker_probe_register(const char *name, const char *format,
|
|||
ret = PTR_ERR(entry);
|
||||
} else if (format) {
|
||||
if (!entry->format)
|
||||
ret = marker_set_format(&entry, format);
|
||||
ret = marker_set_format(entry, format);
|
||||
else if (strcmp(entry->format, format))
|
||||
ret = -EPERM;
|
||||
}
|
||||
|
|
@ -676,10 +704,11 @@ int marker_probe_register(const char *name, const char *format,
|
|||
goto end;
|
||||
}
|
||||
mutex_unlock(&markers_mutex);
|
||||
marker_update_probes(); /* may update entry */
|
||||
marker_update_probes();
|
||||
mutex_lock(&markers_mutex);
|
||||
entry = get_marker(name);
|
||||
WARN_ON(!entry);
|
||||
if (!entry)
|
||||
goto end;
|
||||
if (entry->rcu_pending)
|
||||
rcu_barrier_sched();
|
||||
entry->oldptr = old;
|
||||
|
|
@ -720,7 +749,7 @@ int marker_probe_unregister(const char *name,
|
|||
rcu_barrier_sched();
|
||||
old = marker_entry_remove_probe(entry, probe, probe_private);
|
||||
mutex_unlock(&markers_mutex);
|
||||
marker_update_probes(); /* may update entry */
|
||||
marker_update_probes();
|
||||
mutex_lock(&markers_mutex);
|
||||
entry = get_marker(name);
|
||||
if (!entry)
|
||||
|
|
@ -801,10 +830,11 @@ int marker_probe_unregister_private_data(marker_probe_func *probe,
|
|||
rcu_barrier_sched();
|
||||
old = marker_entry_remove_probe(entry, NULL, probe_private);
|
||||
mutex_unlock(&markers_mutex);
|
||||
marker_update_probes(); /* may update entry */
|
||||
marker_update_probes();
|
||||
mutex_lock(&markers_mutex);
|
||||
entry = get_marker_from_private_data(probe, probe_private);
|
||||
WARN_ON(!entry);
|
||||
if (!entry)
|
||||
goto end;
|
||||
if (entry->rcu_pending)
|
||||
rcu_barrier_sched();
|
||||
entry->oldptr = old;
|
||||
|
|
@ -848,8 +878,6 @@ void *marker_get_private_data(const char *name, marker_probe_func *probe,
|
|||
if (!e->ptype) {
|
||||
if (num == 0 && e->single.func == probe)
|
||||
return e->single.probe_private;
|
||||
else
|
||||
break;
|
||||
} else {
|
||||
struct marker_probe_closure *closure;
|
||||
int match = 0;
|
||||
|
|
@ -861,8 +889,42 @@ void *marker_get_private_data(const char *name, marker_probe_func *probe,
|
|||
return closure[i].probe_private;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ERR_PTR(-ENOENT);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(marker_get_private_data);
|
||||
|
||||
#ifdef CONFIG_MODULES
|
||||
|
||||
int marker_module_notify(struct notifier_block *self,
|
||||
unsigned long val, void *data)
|
||||
{
|
||||
struct module *mod = data;
|
||||
|
||||
switch (val) {
|
||||
case MODULE_STATE_COMING:
|
||||
marker_update_probe_range(mod->markers,
|
||||
mod->markers + mod->num_markers);
|
||||
break;
|
||||
case MODULE_STATE_GOING:
|
||||
marker_update_probe_range(mod->markers,
|
||||
mod->markers + mod->num_markers);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct notifier_block marker_module_nb = {
|
||||
.notifier_call = marker_module_notify,
|
||||
.priority = 0,
|
||||
};
|
||||
|
||||
static int init_markers(void)
|
||||
{
|
||||
return register_module_notifier(&marker_module_nb);
|
||||
}
|
||||
__initcall(init_markers);
|
||||
|
||||
#endif /* CONFIG_MODULES */
|
||||
|
|
|
|||
117
kernel/module.c
117
kernel/module.c
|
|
@ -43,7 +43,6 @@
|
|||
#include <linux/device.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/unwind.h>
|
||||
#include <linux/rculist.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/cacheflush.h>
|
||||
|
|
@ -51,6 +50,7 @@
|
|||
#include <asm/sections.h>
|
||||
#include <linux/tracepoint.h>
|
||||
#include <linux/ftrace.h>
|
||||
#include <linux/async.h>
|
||||
|
||||
#if 0
|
||||
#define DEBUGP printk
|
||||
|
|
@ -743,8 +743,8 @@ static void wait_for_zero_refcount(struct module *mod)
|
|||
mutex_lock(&module_mutex);
|
||||
}
|
||||
|
||||
asmlinkage long
|
||||
sys_delete_module(const char __user *name_user, unsigned int flags)
|
||||
SYSCALL_DEFINE2(delete_module, const char __user *, name_user,
|
||||
unsigned int, flags)
|
||||
{
|
||||
struct module *mod;
|
||||
char name[MODULE_NAME_LEN];
|
||||
|
|
@ -757,8 +757,16 @@ sys_delete_module(const char __user *name_user, unsigned int flags)
|
|||
return -EFAULT;
|
||||
name[MODULE_NAME_LEN-1] = '\0';
|
||||
|
||||
if (mutex_lock_interruptible(&module_mutex) != 0)
|
||||
return -EINTR;
|
||||
/* Create stop_machine threads since free_module relies on
|
||||
* a non-failing stop_machine call. */
|
||||
ret = stop_machine_create();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (mutex_lock_interruptible(&module_mutex) != 0) {
|
||||
ret = -EINTR;
|
||||
goto out_stop;
|
||||
}
|
||||
|
||||
mod = find_module(name);
|
||||
if (!mod) {
|
||||
|
|
@ -809,6 +817,7 @@ sys_delete_module(const char __user *name_user, unsigned int flags)
|
|||
mod->exit();
|
||||
blocking_notifier_call_chain(&module_notify_list,
|
||||
MODULE_STATE_GOING, mod);
|
||||
async_synchronize_full();
|
||||
mutex_lock(&module_mutex);
|
||||
/* Store the name of the last unloaded module for diagnostic purposes */
|
||||
strlcpy(last_unloaded_module, mod->name, sizeof(last_unloaded_module));
|
||||
|
|
@ -817,10 +826,12 @@ sys_delete_module(const char __user *name_user, unsigned int flags)
|
|||
|
||||
out:
|
||||
mutex_unlock(&module_mutex);
|
||||
out_stop:
|
||||
stop_machine_destroy();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void print_unload_info(struct seq_file *m, struct module *mod)
|
||||
static inline void print_unload_info(struct seq_file *m, struct module *mod)
|
||||
{
|
||||
struct module_use *use;
|
||||
int printed_something = 0;
|
||||
|
|
@ -893,7 +904,7 @@ void module_put(struct module *module)
|
|||
EXPORT_SYMBOL(module_put);
|
||||
|
||||
#else /* !CONFIG_MODULE_UNLOAD */
|
||||
static void print_unload_info(struct seq_file *m, struct module *mod)
|
||||
static inline void print_unload_info(struct seq_file *m, struct module *mod)
|
||||
{
|
||||
/* We don't know the usage count, or what modules are using. */
|
||||
seq_printf(m, " - -");
|
||||
|
|
@ -1439,8 +1450,6 @@ static void free_module(struct module *mod)
|
|||
remove_sect_attrs(mod);
|
||||
mod_kobject_remove(mod);
|
||||
|
||||
unwind_remove_table(mod->unwind_info, 0);
|
||||
|
||||
/* Arch-specific cleanup. */
|
||||
module_arch_cleanup(mod);
|
||||
|
||||
|
|
@ -1578,11 +1587,21 @@ static int simplify_symbols(Elf_Shdr *sechdrs,
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* Additional bytes needed by arch in front of individual sections */
|
||||
unsigned int __weak arch_mod_section_prepend(struct module *mod,
|
||||
unsigned int section)
|
||||
{
|
||||
/* default implementation just returns zero */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Update size with this section: return offset. */
|
||||
static long get_offset(unsigned int *size, Elf_Shdr *sechdr)
|
||||
static long get_offset(struct module *mod, unsigned int *size,
|
||||
Elf_Shdr *sechdr, unsigned int section)
|
||||
{
|
||||
long ret;
|
||||
|
||||
*size += arch_mod_section_prepend(mod, section);
|
||||
ret = ALIGN(*size, sechdr->sh_addralign ?: 1);
|
||||
*size = ret + sechdr->sh_size;
|
||||
return ret;
|
||||
|
|
@ -1622,7 +1641,7 @@ static void layout_sections(struct module *mod,
|
|||
|| strncmp(secstrings + s->sh_name,
|
||||
".init", 5) == 0)
|
||||
continue;
|
||||
s->sh_entsize = get_offset(&mod->core_size, s);
|
||||
s->sh_entsize = get_offset(mod, &mod->core_size, s, i);
|
||||
DEBUGP("\t%s\n", secstrings + s->sh_name);
|
||||
}
|
||||
if (m == 0)
|
||||
|
|
@ -1640,7 +1659,7 @@ static void layout_sections(struct module *mod,
|
|||
|| strncmp(secstrings + s->sh_name,
|
||||
".init", 5) != 0)
|
||||
continue;
|
||||
s->sh_entsize = (get_offset(&mod->init_size, s)
|
||||
s->sh_entsize = (get_offset(mod, &mod->init_size, s, i)
|
||||
| INIT_OFFSET_MASK);
|
||||
DEBUGP("\t%s\n", secstrings + s->sh_name);
|
||||
}
|
||||
|
|
@ -1725,15 +1744,15 @@ static const struct kernel_symbol *lookup_symbol(const char *name,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static int is_exported(const char *name, const struct module *mod)
|
||||
static int is_exported(const char *name, unsigned long value,
|
||||
const struct module *mod)
|
||||
{
|
||||
if (!mod && lookup_symbol(name, __start___ksymtab, __stop___ksymtab))
|
||||
return 1;
|
||||
const struct kernel_symbol *ks;
|
||||
if (!mod)
|
||||
ks = lookup_symbol(name, __start___ksymtab, __stop___ksymtab);
|
||||
else
|
||||
if (mod && lookup_symbol(name, mod->syms, mod->syms + mod->num_syms))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
ks = lookup_symbol(name, mod->syms, mod->syms + mod->num_syms);
|
||||
return ks != NULL && ks->value == value;
|
||||
}
|
||||
|
||||
/* As per nm */
|
||||
|
|
@ -1847,7 +1866,6 @@ static noinline struct module *load_module(void __user *umod,
|
|||
unsigned int symindex = 0;
|
||||
unsigned int strindex = 0;
|
||||
unsigned int modindex, versindex, infoindex, pcpuindex;
|
||||
unsigned int unwindex = 0;
|
||||
unsigned int num_kp, num_mcount;
|
||||
struct kernel_param *kp;
|
||||
struct module *mod;
|
||||
|
|
@ -1865,6 +1883,13 @@ static noinline struct module *load_module(void __user *umod,
|
|||
/* vmalloc barfs on "unusual" numbers. Check here */
|
||||
if (len > 64 * 1024 * 1024 || (hdr = vmalloc(len)) == NULL)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
/* Create stop_machine threads since the error path relies on
|
||||
* a non-failing stop_machine call. */
|
||||
err = stop_machine_create();
|
||||
if (err)
|
||||
goto free_hdr;
|
||||
|
||||
if (copy_from_user(hdr, umod, len) != 0) {
|
||||
err = -EFAULT;
|
||||
goto free_hdr;
|
||||
|
|
@ -1930,9 +1955,6 @@ static noinline struct module *load_module(void __user *umod,
|
|||
versindex = find_sec(hdr, sechdrs, secstrings, "__versions");
|
||||
infoindex = find_sec(hdr, sechdrs, secstrings, ".modinfo");
|
||||
pcpuindex = find_pcpusec(hdr, sechdrs, secstrings);
|
||||
#ifdef ARCH_UNWIND_SECTION_NAME
|
||||
unwindex = find_sec(hdr, sechdrs, secstrings, ARCH_UNWIND_SECTION_NAME);
|
||||
#endif
|
||||
|
||||
/* Don't keep modinfo and version sections. */
|
||||
sechdrs[infoindex].sh_flags &= ~(unsigned long)SHF_ALLOC;
|
||||
|
|
@ -1942,8 +1964,6 @@ static noinline struct module *load_module(void __user *umod,
|
|||
sechdrs[symindex].sh_flags |= SHF_ALLOC;
|
||||
sechdrs[strindex].sh_flags |= SHF_ALLOC;
|
||||
#endif
|
||||
if (unwindex)
|
||||
sechdrs[unwindex].sh_flags |= SHF_ALLOC;
|
||||
|
||||
/* Check module struct version now, before we try to use module. */
|
||||
if (!check_modstruct_version(sechdrs, versindex, mod)) {
|
||||
|
|
@ -2184,24 +2204,15 @@ static noinline struct module *load_module(void __user *umod,
|
|||
struct mod_debug *debug;
|
||||
unsigned int num_debug;
|
||||
|
||||
#ifdef CONFIG_MARKERS
|
||||
marker_update_probe_range(mod->markers,
|
||||
mod->markers + mod->num_markers);
|
||||
#endif
|
||||
debug = section_objs(hdr, sechdrs, secstrings, "__verbose",
|
||||
sizeof(*debug), &num_debug);
|
||||
dynamic_printk_setup(debug, num_debug);
|
||||
|
||||
#ifdef CONFIG_TRACEPOINTS
|
||||
tracepoint_update_probe_range(mod->tracepoints,
|
||||
mod->tracepoints + mod->num_tracepoints);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* sechdrs[0].sh_size is always zero */
|
||||
mseg = section_objs(hdr, sechdrs, secstrings, "__mcount_loc",
|
||||
sizeof(*mseg), &num_mcount);
|
||||
ftrace_init_module(mseg, mseg + num_mcount);
|
||||
ftrace_init_module(mod, mseg, mseg + num_mcount);
|
||||
|
||||
err = module_finalize(hdr, sechdrs, mod);
|
||||
if (err < 0)
|
||||
|
|
@ -2249,14 +2260,10 @@ static noinline struct module *load_module(void __user *umod,
|
|||
add_sect_attrs(mod, hdr->e_shnum, secstrings, sechdrs);
|
||||
add_notes_attrs(mod, hdr->e_shnum, secstrings, sechdrs);
|
||||
|
||||
/* Size of section 0 is 0, so this works well if no unwind info. */
|
||||
mod->unwind_info = unwind_add_table(mod,
|
||||
(void *)sechdrs[unwindex].sh_addr,
|
||||
sechdrs[unwindex].sh_size);
|
||||
|
||||
/* Get rid of temporary copy */
|
||||
vfree(hdr);
|
||||
|
||||
stop_machine_destroy();
|
||||
/* Done! */
|
||||
return mod;
|
||||
|
||||
|
|
@ -2279,6 +2286,7 @@ static noinline struct module *load_module(void __user *umod,
|
|||
kfree(args);
|
||||
free_hdr:
|
||||
vfree(hdr);
|
||||
stop_machine_destroy();
|
||||
return ERR_PTR(err);
|
||||
|
||||
truncated:
|
||||
|
|
@ -2288,10 +2296,8 @@ static noinline struct module *load_module(void __user *umod,
|
|||
}
|
||||
|
||||
/* This is where the real work happens */
|
||||
asmlinkage long
|
||||
sys_init_module(void __user *umod,
|
||||
unsigned long len,
|
||||
const char __user *uargs)
|
||||
SYSCALL_DEFINE3(init_module, void __user *, umod,
|
||||
unsigned long, len, const char __user *, uargs)
|
||||
{
|
||||
struct module *mod;
|
||||
int ret = 0;
|
||||
|
|
@ -2346,11 +2352,12 @@ sys_init_module(void __user *umod,
|
|||
/* Now it's a first class citizen! Wake up anyone waiting for it. */
|
||||
mod->state = MODULE_STATE_LIVE;
|
||||
wake_up(&module_wq);
|
||||
blocking_notifier_call_chain(&module_notify_list,
|
||||
MODULE_STATE_LIVE, mod);
|
||||
|
||||
mutex_lock(&module_mutex);
|
||||
/* Drop initial reference. */
|
||||
module_put(mod);
|
||||
unwind_remove_table(mod->unwind_info, 1);
|
||||
module_free(mod, mod->module_init);
|
||||
mod->module_init = NULL;
|
||||
mod->init_size = 0;
|
||||
|
|
@ -2385,7 +2392,7 @@ static const char *get_ksymbol(struct module *mod,
|
|||
unsigned long nextval;
|
||||
|
||||
/* At worse, next value is at end of module */
|
||||
if (within(addr, mod->module_init, mod->init_size))
|
||||
if (within_module_init(addr, mod))
|
||||
nextval = (unsigned long)mod->module_init+mod->init_text_size;
|
||||
else
|
||||
nextval = (unsigned long)mod->module_core+mod->core_text_size;
|
||||
|
|
@ -2433,8 +2440,8 @@ const char *module_address_lookup(unsigned long addr,
|
|||
|
||||
preempt_disable();
|
||||
list_for_each_entry_rcu(mod, &modules, list) {
|
||||
if (within(addr, mod->module_init, mod->init_size)
|
||||
|| within(addr, mod->module_core, mod->core_size)) {
|
||||
if (within_module_init(addr, mod) ||
|
||||
within_module_core(addr, mod)) {
|
||||
if (modname)
|
||||
*modname = mod->name;
|
||||
ret = get_ksymbol(mod, addr, size, offset);
|
||||
|
|
@ -2456,8 +2463,8 @@ int lookup_module_symbol_name(unsigned long addr, char *symname)
|
|||
|
||||
preempt_disable();
|
||||
list_for_each_entry_rcu(mod, &modules, list) {
|
||||
if (within(addr, mod->module_init, mod->init_size) ||
|
||||
within(addr, mod->module_core, mod->core_size)) {
|
||||
if (within_module_init(addr, mod) ||
|
||||
within_module_core(addr, mod)) {
|
||||
const char *sym;
|
||||
|
||||
sym = get_ksymbol(mod, addr, NULL, NULL);
|
||||
|
|
@ -2480,8 +2487,8 @@ int lookup_module_symbol_attrs(unsigned long addr, unsigned long *size,
|
|||
|
||||
preempt_disable();
|
||||
list_for_each_entry_rcu(mod, &modules, list) {
|
||||
if (within(addr, mod->module_init, mod->init_size) ||
|
||||
within(addr, mod->module_core, mod->core_size)) {
|
||||
if (within_module_init(addr, mod) ||
|
||||
within_module_core(addr, mod)) {
|
||||
const char *sym;
|
||||
|
||||
sym = get_ksymbol(mod, addr, size, offset);
|
||||
|
|
@ -2513,7 +2520,7 @@ int module_get_kallsym(unsigned int symnum, unsigned long *value, char *type,
|
|||
strlcpy(name, mod->strtab + mod->symtab[symnum].st_name,
|
||||
KSYM_NAME_LEN);
|
||||
strlcpy(module_name, mod->name, MODULE_NAME_LEN);
|
||||
*exported = is_exported(name, mod);
|
||||
*exported = is_exported(name, *value, mod);
|
||||
preempt_enable();
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -2700,7 +2707,7 @@ int is_module_address(unsigned long addr)
|
|||
preempt_disable();
|
||||
|
||||
list_for_each_entry_rcu(mod, &modules, list) {
|
||||
if (within(addr, mod->module_core, mod->core_size)) {
|
||||
if (within_module_core(addr, mod)) {
|
||||
preempt_enable();
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -2713,7 +2720,7 @@ int is_module_address(unsigned long addr)
|
|||
|
||||
|
||||
/* Is this a valid kernel address? */
|
||||
struct module *__module_text_address(unsigned long addr)
|
||||
__notrace_funcgraph struct module *__module_text_address(unsigned long addr)
|
||||
{
|
||||
struct module *mod;
|
||||
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ EXPORT_SYMBOL(__mutex_init);
|
|||
* We also put the fastpath first in the kernel image, to make sure the
|
||||
* branch is predicted by the CPU as default-untaken.
|
||||
*/
|
||||
static void noinline __sched
|
||||
static __used noinline void __sched
|
||||
__mutex_lock_slowpath(atomic_t *lock_count);
|
||||
|
||||
/***
|
||||
|
|
@ -96,7 +96,7 @@ void inline __sched mutex_lock(struct mutex *lock)
|
|||
EXPORT_SYMBOL(mutex_lock);
|
||||
#endif
|
||||
|
||||
static noinline void __sched __mutex_unlock_slowpath(atomic_t *lock_count);
|
||||
static __used noinline void __sched __mutex_unlock_slowpath(atomic_t *lock_count);
|
||||
|
||||
/***
|
||||
* mutex_unlock - release the mutex
|
||||
|
|
@ -184,7 +184,7 @@ __mutex_lock_common(struct mutex *lock, long state, unsigned int subclass,
|
|||
}
|
||||
|
||||
done:
|
||||
lock_acquired(&lock->dep_map);
|
||||
lock_acquired(&lock->dep_map, ip);
|
||||
/* got the lock - rejoice! */
|
||||
mutex_remove_waiter(lock, &waiter, task_thread_info(task));
|
||||
debug_mutex_set_owner(lock, task_thread_info(task));
|
||||
|
|
@ -268,7 +268,7 @@ __mutex_unlock_common_slowpath(atomic_t *lock_count, int nested)
|
|||
/*
|
||||
* Release the lock, slowpath:
|
||||
*/
|
||||
static noinline void
|
||||
static __used noinline void
|
||||
__mutex_unlock_slowpath(atomic_t *lock_count)
|
||||
{
|
||||
__mutex_unlock_common_slowpath(lock_count, 1);
|
||||
|
|
@ -313,7 +313,7 @@ int __sched mutex_lock_killable(struct mutex *lock)
|
|||
}
|
||||
EXPORT_SYMBOL(mutex_lock_killable);
|
||||
|
||||
static noinline void __sched
|
||||
static __used noinline void __sched
|
||||
__mutex_lock_slowpath(atomic_t *lock_count)
|
||||
{
|
||||
struct mutex *lock = container_of(lock_count, struct mutex, count);
|
||||
|
|
|
|||
|
|
@ -82,6 +82,14 @@ static int __kprobes notifier_call_chain(struct notifier_block **nl,
|
|||
|
||||
while (nb && nr_to_call) {
|
||||
next_nb = rcu_dereference(nb->next);
|
||||
|
||||
#ifdef CONFIG_DEBUG_NOTIFIERS
|
||||
if (unlikely(!func_ptr_is_kernel_text(nb->notifier_call))) {
|
||||
WARN(1, "Invalid notifier called!");
|
||||
nb = next_nb;
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
ret = nb->notifier_call(nb, val, v);
|
||||
|
||||
if (nr_calls)
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@
|
|||
|
||||
struct ns_cgroup {
|
||||
struct cgroup_subsys_state css;
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
struct cgroup_subsys ns_subsys;
|
||||
|
|
@ -84,7 +83,6 @@ static struct cgroup_subsys_state *ns_create(struct cgroup_subsys *ss,
|
|||
ns_cgroup = kzalloc(sizeof(*ns_cgroup), GFP_KERNEL);
|
||||
if (!ns_cgroup)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
spin_lock_init(&ns_cgroup->lock);
|
||||
return &ns_cgroup->css;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -80,12 +80,6 @@ static struct nsproxy *create_new_namespaces(unsigned long flags,
|
|||
goto out_pid;
|
||||
}
|
||||
|
||||
new_nsp->user_ns = copy_user_ns(flags, tsk->nsproxy->user_ns);
|
||||
if (IS_ERR(new_nsp->user_ns)) {
|
||||
err = PTR_ERR(new_nsp->user_ns);
|
||||
goto out_user;
|
||||
}
|
||||
|
||||
new_nsp->net_ns = copy_net_ns(flags, tsk->nsproxy->net_ns);
|
||||
if (IS_ERR(new_nsp->net_ns)) {
|
||||
err = PTR_ERR(new_nsp->net_ns);
|
||||
|
|
@ -95,9 +89,6 @@ static struct nsproxy *create_new_namespaces(unsigned long flags,
|
|||
return new_nsp;
|
||||
|
||||
out_net:
|
||||
if (new_nsp->user_ns)
|
||||
put_user_ns(new_nsp->user_ns);
|
||||
out_user:
|
||||
if (new_nsp->pid_ns)
|
||||
put_pid_ns(new_nsp->pid_ns);
|
||||
out_pid:
|
||||
|
|
@ -130,7 +121,7 @@ int copy_namespaces(unsigned long flags, struct task_struct *tsk)
|
|||
get_nsproxy(old_ns);
|
||||
|
||||
if (!(flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC |
|
||||
CLONE_NEWUSER | CLONE_NEWPID | CLONE_NEWNET)))
|
||||
CLONE_NEWPID | CLONE_NEWNET)))
|
||||
return 0;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN)) {
|
||||
|
|
@ -173,8 +164,6 @@ void free_nsproxy(struct nsproxy *ns)
|
|||
put_ipc_ns(ns->ipc_ns);
|
||||
if (ns->pid_ns)
|
||||
put_pid_ns(ns->pid_ns);
|
||||
if (ns->user_ns)
|
||||
put_user_ns(ns->user_ns);
|
||||
put_net(ns->net_ns);
|
||||
kmem_cache_free(nsproxy_cachep, ns);
|
||||
}
|
||||
|
|
@ -189,7 +178,7 @@ int unshare_nsproxy_namespaces(unsigned long unshare_flags,
|
|||
int err = 0;
|
||||
|
||||
if (!(unshare_flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC |
|
||||
CLONE_NEWUSER | CLONE_NEWNET)))
|
||||
CLONE_NEWNET)))
|
||||
return 0;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
#include <linux/debug_locks.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/kallsyms.h>
|
||||
#include <linux/dmi.h>
|
||||
|
||||
int panic_on_oops;
|
||||
static unsigned long tainted_mask;
|
||||
|
|
@ -298,6 +299,8 @@ static int init_oops_id(void)
|
|||
{
|
||||
if (!oops_id)
|
||||
get_random_bytes(&oops_id, sizeof(oops_id));
|
||||
else
|
||||
oops_id++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -321,36 +324,27 @@ void oops_exit(void)
|
|||
}
|
||||
|
||||
#ifdef WANT_WARN_ON_SLOWPATH
|
||||
void warn_on_slowpath(const char *file, int line)
|
||||
{
|
||||
char function[KSYM_SYMBOL_LEN];
|
||||
unsigned long caller = (unsigned long) __builtin_return_address(0);
|
||||
sprint_symbol(function, caller);
|
||||
|
||||
printk(KERN_WARNING "------------[ cut here ]------------\n");
|
||||
printk(KERN_WARNING "WARNING: at %s:%d %s()\n", file,
|
||||
line, function);
|
||||
print_modules();
|
||||
dump_stack();
|
||||
print_oops_end_marker();
|
||||
add_taint(TAINT_WARN);
|
||||
}
|
||||
EXPORT_SYMBOL(warn_on_slowpath);
|
||||
|
||||
|
||||
void warn_slowpath(const char *file, int line, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
char function[KSYM_SYMBOL_LEN];
|
||||
unsigned long caller = (unsigned long)__builtin_return_address(0);
|
||||
const char *board;
|
||||
|
||||
sprint_symbol(function, caller);
|
||||
|
||||
printk(KERN_WARNING "------------[ cut here ]------------\n");
|
||||
printk(KERN_WARNING "WARNING: at %s:%d %s()\n", file,
|
||||
line, function);
|
||||
va_start(args, fmt);
|
||||
vprintk(fmt, args);
|
||||
va_end(args);
|
||||
board = dmi_get_system_info(DMI_PRODUCT_NAME);
|
||||
if (board)
|
||||
printk(KERN_WARNING "Hardware name: %s\n", board);
|
||||
|
||||
if (fmt) {
|
||||
va_start(args, fmt);
|
||||
vprintk(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
print_modules();
|
||||
dump_stack();
|
||||
|
|
|
|||
|
|
@ -474,8 +474,14 @@ pid_t task_session_nr_ns(struct task_struct *tsk, struct pid_namespace *ns)
|
|||
}
|
||||
EXPORT_SYMBOL(task_session_nr_ns);
|
||||
|
||||
struct pid_namespace *task_active_pid_ns(struct task_struct *tsk)
|
||||
{
|
||||
return ns_of_pid(task_pid(tsk));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(task_active_pid_ns);
|
||||
|
||||
/*
|
||||
* Used by proc to find the first pid that is greater then or equal to nr.
|
||||
* Used by proc to find the first pid that is greater than or equal to nr.
|
||||
*
|
||||
* If there is a pid at nr this function is exactly the same as find_pid_ns.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -58,21 +58,21 @@ void thread_group_cputime(
|
|||
struct task_struct *tsk,
|
||||
struct task_cputime *times)
|
||||
{
|
||||
struct signal_struct *sig;
|
||||
struct task_cputime *totals, *tot;
|
||||
int i;
|
||||
struct task_cputime *tot;
|
||||
|
||||
sig = tsk->signal;
|
||||
if (unlikely(!sig) || !sig->cputime.totals) {
|
||||
totals = tsk->signal->cputime.totals;
|
||||
if (!totals) {
|
||||
times->utime = tsk->utime;
|
||||
times->stime = tsk->stime;
|
||||
times->sum_exec_runtime = tsk->se.sum_exec_runtime;
|
||||
return;
|
||||
}
|
||||
|
||||
times->stime = times->utime = cputime_zero;
|
||||
times->sum_exec_runtime = 0;
|
||||
for_each_possible_cpu(i) {
|
||||
tot = per_cpu_ptr(tsk->signal->cputime.totals, i);
|
||||
tot = per_cpu_ptr(totals, i);
|
||||
times->utime = cputime_add(times->utime, tot->utime);
|
||||
times->stime = cputime_add(times->stime, tot->stime);
|
||||
times->sum_exec_runtime += tot->sum_exec_runtime;
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ static DEFINE_SPINLOCK(idr_lock);
|
|||
* must supply functions here, even if the function just returns
|
||||
* ENOSYS. The standard POSIX timer management code assumes the
|
||||
* following: 1.) The k_itimer struct (sched.h) is used for the
|
||||
* timer. 2.) The list, it_lock, it_clock, it_id and it_process
|
||||
* timer. 2.) The list, it_lock, it_clock, it_id and it_pid
|
||||
* fields are not modified by timer code.
|
||||
*
|
||||
* At this time all functions EXCEPT clock_nanosleep can be
|
||||
|
|
@ -319,7 +319,8 @@ void do_schedule_next_timer(struct siginfo *info)
|
|||
|
||||
int posix_timer_event(struct k_itimer *timr, int si_private)
|
||||
{
|
||||
int shared, ret;
|
||||
struct task_struct *task;
|
||||
int shared, ret = -1;
|
||||
/*
|
||||
* FIXME: if ->sigq is queued we can race with
|
||||
* dequeue_signal()->do_schedule_next_timer().
|
||||
|
|
@ -333,8 +334,13 @@ int posix_timer_event(struct k_itimer *timr, int si_private)
|
|||
*/
|
||||
timr->sigq->info.si_sys_private = si_private;
|
||||
|
||||
shared = !(timr->it_sigev_notify & SIGEV_THREAD_ID);
|
||||
ret = send_sigqueue(timr->sigq, timr->it_process, shared);
|
||||
rcu_read_lock();
|
||||
task = pid_task(timr->it_pid, PIDTYPE_PID);
|
||||
if (task) {
|
||||
shared = !(timr->it_sigev_notify & SIGEV_THREAD_ID);
|
||||
ret = send_sigqueue(timr->sigq, task, shared);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
/* If we failed to send the signal the timer stops. */
|
||||
return ret > 0;
|
||||
}
|
||||
|
|
@ -411,7 +417,7 @@ static enum hrtimer_restart posix_timer_fn(struct hrtimer *timer)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static struct task_struct * good_sigevent(sigevent_t * event)
|
||||
static struct pid *good_sigevent(sigevent_t * event)
|
||||
{
|
||||
struct task_struct *rtn = current->group_leader;
|
||||
|
||||
|
|
@ -425,7 +431,7 @@ static struct task_struct * good_sigevent(sigevent_t * event)
|
|||
((event->sigev_signo <= 0) || (event->sigev_signo > SIGRTMAX)))
|
||||
return NULL;
|
||||
|
||||
return rtn;
|
||||
return task_pid(rtn);
|
||||
}
|
||||
|
||||
void register_posix_clock(const clockid_t clock_id, struct k_clock *new_clock)
|
||||
|
|
@ -464,20 +470,19 @@ static void release_posix_timer(struct k_itimer *tmr, int it_id_set)
|
|||
idr_remove(&posix_timers_id, tmr->it_id);
|
||||
spin_unlock_irqrestore(&idr_lock, flags);
|
||||
}
|
||||
put_pid(tmr->it_pid);
|
||||
sigqueue_free(tmr->sigq);
|
||||
kmem_cache_free(posix_timers_cache, tmr);
|
||||
}
|
||||
|
||||
/* Create a POSIX.1b interval timer. */
|
||||
|
||||
asmlinkage long
|
||||
sys_timer_create(const clockid_t which_clock,
|
||||
struct sigevent __user *timer_event_spec,
|
||||
timer_t __user * created_timer_id)
|
||||
SYSCALL_DEFINE3(timer_create, const clockid_t, which_clock,
|
||||
struct sigevent __user *, timer_event_spec,
|
||||
timer_t __user *, created_timer_id)
|
||||
{
|
||||
struct k_itimer *new_timer;
|
||||
int error, new_timer_id;
|
||||
struct task_struct *process;
|
||||
sigevent_t event;
|
||||
int it_id_set = IT_ID_NOT_SET;
|
||||
|
||||
|
|
@ -531,11 +536,9 @@ sys_timer_create(const clockid_t which_clock,
|
|||
goto out;
|
||||
}
|
||||
rcu_read_lock();
|
||||
process = good_sigevent(&event);
|
||||
if (process)
|
||||
get_task_struct(process);
|
||||
new_timer->it_pid = get_pid(good_sigevent(&event));
|
||||
rcu_read_unlock();
|
||||
if (!process) {
|
||||
if (!new_timer->it_pid) {
|
||||
error = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
|
@ -543,8 +546,7 @@ sys_timer_create(const clockid_t which_clock,
|
|||
event.sigev_notify = SIGEV_SIGNAL;
|
||||
event.sigev_signo = SIGALRM;
|
||||
event.sigev_value.sival_int = new_timer->it_id;
|
||||
process = current->group_leader;
|
||||
get_task_struct(process);
|
||||
new_timer->it_pid = get_pid(task_tgid(current));
|
||||
}
|
||||
|
||||
new_timer->it_sigev_notify = event.sigev_notify;
|
||||
|
|
@ -554,7 +556,7 @@ sys_timer_create(const clockid_t which_clock,
|
|||
new_timer->sigq->info.si_code = SI_TIMER;
|
||||
|
||||
spin_lock_irq(¤t->sighand->siglock);
|
||||
new_timer->it_process = process;
|
||||
new_timer->it_signal = current->signal;
|
||||
list_add(&new_timer->list, ¤t->signal->posix_timers);
|
||||
spin_unlock_irq(¤t->sighand->siglock);
|
||||
|
||||
|
|
@ -589,8 +591,7 @@ static struct k_itimer *lock_timer(timer_t timer_id, unsigned long *flags)
|
|||
timr = idr_find(&posix_timers_id, (int)timer_id);
|
||||
if (timr) {
|
||||
spin_lock(&timr->it_lock);
|
||||
if (timr->it_process &&
|
||||
same_thread_group(timr->it_process, current)) {
|
||||
if (timr->it_signal == current->signal) {
|
||||
spin_unlock(&idr_lock);
|
||||
return timr;
|
||||
}
|
||||
|
|
@ -659,8 +660,8 @@ common_timer_get(struct k_itimer *timr, struct itimerspec *cur_setting)
|
|||
}
|
||||
|
||||
/* Get the time remaining on a POSIX.1b interval timer. */
|
||||
asmlinkage long
|
||||
sys_timer_gettime(timer_t timer_id, struct itimerspec __user *setting)
|
||||
SYSCALL_DEFINE2(timer_gettime, timer_t, timer_id,
|
||||
struct itimerspec __user *, setting)
|
||||
{
|
||||
struct k_itimer *timr;
|
||||
struct itimerspec cur_setting;
|
||||
|
|
@ -689,8 +690,7 @@ sys_timer_gettime(timer_t timer_id, struct itimerspec __user *setting)
|
|||
* the call back to do_schedule_next_timer(). So all we need to do is
|
||||
* to pick up the frozen overrun.
|
||||
*/
|
||||
asmlinkage long
|
||||
sys_timer_getoverrun(timer_t timer_id)
|
||||
SYSCALL_DEFINE1(timer_getoverrun, timer_t, timer_id)
|
||||
{
|
||||
struct k_itimer *timr;
|
||||
int overrun;
|
||||
|
|
@ -758,10 +758,9 @@ common_timer_set(struct k_itimer *timr, int flags,
|
|||
}
|
||||
|
||||
/* Set a POSIX.1b interval timer */
|
||||
asmlinkage long
|
||||
sys_timer_settime(timer_t timer_id, int flags,
|
||||
const struct itimerspec __user *new_setting,
|
||||
struct itimerspec __user *old_setting)
|
||||
SYSCALL_DEFINE4(timer_settime, timer_t, timer_id, int, flags,
|
||||
const struct itimerspec __user *, new_setting,
|
||||
struct itimerspec __user *, old_setting)
|
||||
{
|
||||
struct k_itimer *timr;
|
||||
struct itimerspec new_spec, old_spec;
|
||||
|
|
@ -814,8 +813,7 @@ static inline int timer_delete_hook(struct k_itimer *timer)
|
|||
}
|
||||
|
||||
/* Delete a POSIX.1b interval timer. */
|
||||
asmlinkage long
|
||||
sys_timer_delete(timer_t timer_id)
|
||||
SYSCALL_DEFINE1(timer_delete, timer_t, timer_id)
|
||||
{
|
||||
struct k_itimer *timer;
|
||||
unsigned long flags;
|
||||
|
|
@ -837,8 +835,7 @@ retry_delete:
|
|||
* This keeps any tasks waiting on the spin lock from thinking
|
||||
* they got something (see the lock code above).
|
||||
*/
|
||||
put_task_struct(timer->it_process);
|
||||
timer->it_process = NULL;
|
||||
timer->it_signal = NULL;
|
||||
|
||||
unlock_timer(timer, flags);
|
||||
release_posix_timer(timer, IT_ID_SET);
|
||||
|
|
@ -864,8 +861,7 @@ retry_delete:
|
|||
* This keeps any tasks waiting on the spin lock from thinking
|
||||
* they got something (see the lock code above).
|
||||
*/
|
||||
put_task_struct(timer->it_process);
|
||||
timer->it_process = NULL;
|
||||
timer->it_signal = NULL;
|
||||
|
||||
unlock_timer(timer, flags);
|
||||
release_posix_timer(timer, IT_ID_SET);
|
||||
|
|
@ -903,8 +899,8 @@ int do_posix_clock_nonanosleep(const clockid_t clock, int flags,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(do_posix_clock_nonanosleep);
|
||||
|
||||
asmlinkage long sys_clock_settime(const clockid_t which_clock,
|
||||
const struct timespec __user *tp)
|
||||
SYSCALL_DEFINE2(clock_settime, const clockid_t, which_clock,
|
||||
const struct timespec __user *, tp)
|
||||
{
|
||||
struct timespec new_tp;
|
||||
|
||||
|
|
@ -916,8 +912,8 @@ asmlinkage long sys_clock_settime(const clockid_t which_clock,
|
|||
return CLOCK_DISPATCH(which_clock, clock_set, (which_clock, &new_tp));
|
||||
}
|
||||
|
||||
asmlinkage long
|
||||
sys_clock_gettime(const clockid_t which_clock, struct timespec __user *tp)
|
||||
SYSCALL_DEFINE2(clock_gettime, const clockid_t, which_clock,
|
||||
struct timespec __user *,tp)
|
||||
{
|
||||
struct timespec kernel_tp;
|
||||
int error;
|
||||
|
|
@ -933,8 +929,8 @@ sys_clock_gettime(const clockid_t which_clock, struct timespec __user *tp)
|
|||
|
||||
}
|
||||
|
||||
asmlinkage long
|
||||
sys_clock_getres(const clockid_t which_clock, struct timespec __user *tp)
|
||||
SYSCALL_DEFINE2(clock_getres, const clockid_t, which_clock,
|
||||
struct timespec __user *, tp)
|
||||
{
|
||||
struct timespec rtn_tp;
|
||||
int error;
|
||||
|
|
@ -963,10 +959,9 @@ static int common_nsleep(const clockid_t which_clock, int flags,
|
|||
which_clock);
|
||||
}
|
||||
|
||||
asmlinkage long
|
||||
sys_clock_nanosleep(const clockid_t which_clock, int flags,
|
||||
const struct timespec __user *rqtp,
|
||||
struct timespec __user *rmtp)
|
||||
SYSCALL_DEFINE4(clock_nanosleep, const clockid_t, which_clock, int, flags,
|
||||
const struct timespec __user *, rqtp,
|
||||
struct timespec __user *, rmtp)
|
||||
{
|
||||
struct timespec t;
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@
|
|||
#include <linux/console.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/freezer.h>
|
||||
#include <linux/ftrace.h>
|
||||
|
||||
#include "power.h"
|
||||
|
||||
|
|
@ -257,19 +256,18 @@ static int create_image(int platform_mode)
|
|||
|
||||
int hibernation_snapshot(int platform_mode)
|
||||
{
|
||||
int error, ftrace_save;
|
||||
int error;
|
||||
|
||||
error = platform_begin(platform_mode);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/* Free memory before shutting down devices. */
|
||||
error = swsusp_shrink_memory();
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = platform_begin(platform_mode);
|
||||
if (error)
|
||||
goto Close;
|
||||
|
||||
suspend_console();
|
||||
ftrace_save = __ftrace_enabled_save();
|
||||
error = device_suspend(PMSG_FREEZE);
|
||||
if (error)
|
||||
goto Recover_platform;
|
||||
|
|
@ -299,7 +297,6 @@ int hibernation_snapshot(int platform_mode)
|
|||
Resume_devices:
|
||||
device_resume(in_suspend ?
|
||||
(error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE);
|
||||
__ftrace_enabled_restore(ftrace_save);
|
||||
resume_console();
|
||||
Close:
|
||||
platform_end(platform_mode);
|
||||
|
|
@ -370,11 +367,10 @@ static int resume_target_kernel(void)
|
|||
|
||||
int hibernation_restore(int platform_mode)
|
||||
{
|
||||
int error, ftrace_save;
|
||||
int error;
|
||||
|
||||
pm_prepare_console();
|
||||
suspend_console();
|
||||
ftrace_save = __ftrace_enabled_save();
|
||||
error = device_suspend(PMSG_QUIESCE);
|
||||
if (error)
|
||||
goto Finish;
|
||||
|
|
@ -389,7 +385,6 @@ int hibernation_restore(int platform_mode)
|
|||
platform_restore_cleanup(platform_mode);
|
||||
device_resume(PMSG_RECOVER);
|
||||
Finish:
|
||||
__ftrace_enabled_restore(ftrace_save);
|
||||
resume_console();
|
||||
pm_restore_console();
|
||||
return error;
|
||||
|
|
@ -402,7 +397,7 @@ int hibernation_restore(int platform_mode)
|
|||
|
||||
int hibernation_platform_enter(void)
|
||||
{
|
||||
int error, ftrace_save;
|
||||
int error;
|
||||
|
||||
if (!hibernation_ops)
|
||||
return -ENOSYS;
|
||||
|
|
@ -417,7 +412,6 @@ int hibernation_platform_enter(void)
|
|||
goto Close;
|
||||
|
||||
suspend_console();
|
||||
ftrace_save = __ftrace_enabled_save();
|
||||
error = device_suspend(PMSG_HIBERNATE);
|
||||
if (error) {
|
||||
if (hibernation_ops->recover)
|
||||
|
|
@ -452,7 +446,6 @@ int hibernation_platform_enter(void)
|
|||
hibernation_ops->finish();
|
||||
Resume_devices:
|
||||
device_resume(PMSG_RESTORE);
|
||||
__ftrace_enabled_restore(ftrace_save);
|
||||
resume_console();
|
||||
Close:
|
||||
hibernation_ops->end();
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@
|
|||
#include <linux/freezer.h>
|
||||
#include <linux/vmstat.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/ftrace.h>
|
||||
|
||||
#include "power.h"
|
||||
|
||||
|
|
@ -317,7 +316,7 @@ static int suspend_enter(suspend_state_t state)
|
|||
*/
|
||||
int suspend_devices_and_enter(suspend_state_t state)
|
||||
{
|
||||
int error, ftrace_save;
|
||||
int error;
|
||||
|
||||
if (!suspend_ops)
|
||||
return -ENOSYS;
|
||||
|
|
@ -328,7 +327,6 @@ int suspend_devices_and_enter(suspend_state_t state)
|
|||
goto Close;
|
||||
}
|
||||
suspend_console();
|
||||
ftrace_save = __ftrace_enabled_save();
|
||||
suspend_test_start();
|
||||
error = device_suspend(PMSG_SUSPEND);
|
||||
if (error) {
|
||||
|
|
@ -360,7 +358,6 @@ int suspend_devices_and_enter(suspend_state_t state)
|
|||
suspend_test_start();
|
||||
device_resume(PMSG_RESUME);
|
||||
suspend_test_finish("resume devices");
|
||||
__ftrace_enabled_restore(ftrace_save);
|
||||
resume_console();
|
||||
Close:
|
||||
if (suspend_ops->end)
|
||||
|
|
@ -618,7 +615,7 @@ static void __init test_wakealarm(struct rtc_device *rtc, suspend_state_t state)
|
|||
/* this may fail if the RTC hasn't been initialized */
|
||||
status = rtc_read_time(rtc, &alm.time);
|
||||
if (status < 0) {
|
||||
printk(err_readtime, rtc->dev.bus_id, status);
|
||||
printk(err_readtime, dev_name(&rtc->dev), status);
|
||||
return;
|
||||
}
|
||||
rtc_tm_to_time(&alm.time, &now);
|
||||
|
|
@ -629,7 +626,7 @@ static void __init test_wakealarm(struct rtc_device *rtc, suspend_state_t state)
|
|||
|
||||
status = rtc_set_alarm(rtc, &alm);
|
||||
if (status < 0) {
|
||||
printk(err_wakealarm, rtc->dev.bus_id, status);
|
||||
printk(err_wakealarm, dev_name(&rtc->dev), status);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -663,7 +660,7 @@ static int __init has_wakealarm(struct device *dev, void *name_ptr)
|
|||
if (!device_may_wakeup(candidate->dev.parent))
|
||||
return 0;
|
||||
|
||||
*(char **)name_ptr = dev->bus_id;
|
||||
*(const char **)name_ptr = dev_name(dev);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ static DECLARE_WORK(poweroff_work, do_poweroff);
|
|||
static void handle_poweroff(int key, struct tty_struct *tty)
|
||||
{
|
||||
/* run sysrq poweroff on boot cpu */
|
||||
schedule_work_on(first_cpu(cpu_online_map), &poweroff_work);
|
||||
schedule_work_on(cpumask_first(cpu_online_mask), &poweroff_work);
|
||||
}
|
||||
|
||||
static struct sysrq_key_op sysrq_poweroff_op = {
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
#include <linux/syscalls.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/highmem.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/mmu_context.h>
|
||||
|
|
@ -192,12 +193,6 @@ static void *chain_alloc(struct chain_allocator *ca, unsigned int size)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void chain_free(struct chain_allocator *ca, int clear_page_nosave)
|
||||
{
|
||||
free_list_of_pages(ca->chain, clear_page_nosave);
|
||||
memset(ca, 0, sizeof(struct chain_allocator));
|
||||
}
|
||||
|
||||
/**
|
||||
* Data types related to memory bitmaps.
|
||||
*
|
||||
|
|
@ -233,7 +228,7 @@ static void chain_free(struct chain_allocator *ca, int clear_page_nosave)
|
|||
#define BM_BITS_PER_BLOCK (PAGE_SIZE << 3)
|
||||
|
||||
struct bm_block {
|
||||
struct bm_block *next; /* next element of the list */
|
||||
struct list_head hook; /* hook into a list of bitmap blocks */
|
||||
unsigned long start_pfn; /* pfn represented by the first bit */
|
||||
unsigned long end_pfn; /* pfn represented by the last bit plus 1 */
|
||||
unsigned long *data; /* bitmap representing pages */
|
||||
|
|
@ -244,24 +239,15 @@ static inline unsigned long bm_block_bits(struct bm_block *bb)
|
|||
return bb->end_pfn - bb->start_pfn;
|
||||
}
|
||||
|
||||
struct zone_bitmap {
|
||||
struct zone_bitmap *next; /* next element of the list */
|
||||
unsigned long start_pfn; /* minimal pfn in this zone */
|
||||
unsigned long end_pfn; /* maximal pfn in this zone plus 1 */
|
||||
struct bm_block *bm_blocks; /* list of bitmap blocks */
|
||||
struct bm_block *cur_block; /* recently used bitmap block */
|
||||
};
|
||||
|
||||
/* strcut bm_position is used for browsing memory bitmaps */
|
||||
|
||||
struct bm_position {
|
||||
struct zone_bitmap *zone_bm;
|
||||
struct bm_block *block;
|
||||
int bit;
|
||||
};
|
||||
|
||||
struct memory_bitmap {
|
||||
struct zone_bitmap *zone_bm_list; /* list of zone bitmaps */
|
||||
struct list_head blocks; /* list of bitmap blocks */
|
||||
struct linked_page *p_list; /* list of pages used to store zone
|
||||
* bitmap objects and bitmap block
|
||||
* objects
|
||||
|
|
@ -273,11 +259,7 @@ struct memory_bitmap {
|
|||
|
||||
static void memory_bm_position_reset(struct memory_bitmap *bm)
|
||||
{
|
||||
struct zone_bitmap *zone_bm;
|
||||
|
||||
zone_bm = bm->zone_bm_list;
|
||||
bm->cur.zone_bm = zone_bm;
|
||||
bm->cur.block = zone_bm->bm_blocks;
|
||||
bm->cur.block = list_entry(bm->blocks.next, struct bm_block, hook);
|
||||
bm->cur.bit = 0;
|
||||
}
|
||||
|
||||
|
|
@ -285,151 +267,184 @@ static void memory_bm_free(struct memory_bitmap *bm, int clear_nosave_free);
|
|||
|
||||
/**
|
||||
* create_bm_block_list - create a list of block bitmap objects
|
||||
* @nr_blocks - number of blocks to allocate
|
||||
* @list - list to put the allocated blocks into
|
||||
* @ca - chain allocator to be used for allocating memory
|
||||
*/
|
||||
|
||||
static inline struct bm_block *
|
||||
create_bm_block_list(unsigned int nr_blocks, struct chain_allocator *ca)
|
||||
static int create_bm_block_list(unsigned long pages,
|
||||
struct list_head *list,
|
||||
struct chain_allocator *ca)
|
||||
{
|
||||
struct bm_block *bblist = NULL;
|
||||
unsigned int nr_blocks = DIV_ROUND_UP(pages, BM_BITS_PER_BLOCK);
|
||||
|
||||
while (nr_blocks-- > 0) {
|
||||
struct bm_block *bb;
|
||||
|
||||
bb = chain_alloc(ca, sizeof(struct bm_block));
|
||||
if (!bb)
|
||||
return NULL;
|
||||
|
||||
bb->next = bblist;
|
||||
bblist = bb;
|
||||
return -ENOMEM;
|
||||
list_add(&bb->hook, list);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct mem_extent {
|
||||
struct list_head hook;
|
||||
unsigned long start;
|
||||
unsigned long end;
|
||||
};
|
||||
|
||||
/**
|
||||
* free_mem_extents - free a list of memory extents
|
||||
* @list - list of extents to empty
|
||||
*/
|
||||
static void free_mem_extents(struct list_head *list)
|
||||
{
|
||||
struct mem_extent *ext, *aux;
|
||||
|
||||
list_for_each_entry_safe(ext, aux, list, hook) {
|
||||
list_del(&ext->hook);
|
||||
kfree(ext);
|
||||
}
|
||||
return bblist;
|
||||
}
|
||||
|
||||
/**
|
||||
* create_zone_bm_list - create a list of zone bitmap objects
|
||||
* create_mem_extents - create a list of memory extents representing
|
||||
* contiguous ranges of PFNs
|
||||
* @list - list to put the extents into
|
||||
* @gfp_mask - mask to use for memory allocations
|
||||
*/
|
||||
|
||||
static inline struct zone_bitmap *
|
||||
create_zone_bm_list(unsigned int nr_zones, struct chain_allocator *ca)
|
||||
static int create_mem_extents(struct list_head *list, gfp_t gfp_mask)
|
||||
{
|
||||
struct zone_bitmap *zbmlist = NULL;
|
||||
struct zone *zone;
|
||||
|
||||
while (nr_zones-- > 0) {
|
||||
struct zone_bitmap *zbm;
|
||||
INIT_LIST_HEAD(list);
|
||||
|
||||
zbm = chain_alloc(ca, sizeof(struct zone_bitmap));
|
||||
if (!zbm)
|
||||
return NULL;
|
||||
for_each_zone(zone) {
|
||||
unsigned long zone_start, zone_end;
|
||||
struct mem_extent *ext, *cur, *aux;
|
||||
|
||||
zbm->next = zbmlist;
|
||||
zbmlist = zbm;
|
||||
if (!populated_zone(zone))
|
||||
continue;
|
||||
|
||||
zone_start = zone->zone_start_pfn;
|
||||
zone_end = zone->zone_start_pfn + zone->spanned_pages;
|
||||
|
||||
list_for_each_entry(ext, list, hook)
|
||||
if (zone_start <= ext->end)
|
||||
break;
|
||||
|
||||
if (&ext->hook == list || zone_end < ext->start) {
|
||||
/* New extent is necessary */
|
||||
struct mem_extent *new_ext;
|
||||
|
||||
new_ext = kzalloc(sizeof(struct mem_extent), gfp_mask);
|
||||
if (!new_ext) {
|
||||
free_mem_extents(list);
|
||||
return -ENOMEM;
|
||||
}
|
||||
new_ext->start = zone_start;
|
||||
new_ext->end = zone_end;
|
||||
list_add_tail(&new_ext->hook, &ext->hook);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Merge this zone's range of PFNs with the existing one */
|
||||
if (zone_start < ext->start)
|
||||
ext->start = zone_start;
|
||||
if (zone_end > ext->end)
|
||||
ext->end = zone_end;
|
||||
|
||||
/* More merging may be possible */
|
||||
cur = ext;
|
||||
list_for_each_entry_safe_continue(cur, aux, list, hook) {
|
||||
if (zone_end < cur->start)
|
||||
break;
|
||||
if (zone_end < cur->end)
|
||||
ext->end = cur->end;
|
||||
list_del(&cur->hook);
|
||||
kfree(cur);
|
||||
}
|
||||
}
|
||||
return zbmlist;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* memory_bm_create - allocate memory for a memory bitmap
|
||||
*/
|
||||
|
||||
static int
|
||||
memory_bm_create(struct memory_bitmap *bm, gfp_t gfp_mask, int safe_needed)
|
||||
{
|
||||
struct chain_allocator ca;
|
||||
struct zone *zone;
|
||||
struct zone_bitmap *zone_bm;
|
||||
struct bm_block *bb;
|
||||
unsigned int nr;
|
||||
struct list_head mem_extents;
|
||||
struct mem_extent *ext;
|
||||
int error;
|
||||
|
||||
chain_init(&ca, gfp_mask, safe_needed);
|
||||
INIT_LIST_HEAD(&bm->blocks);
|
||||
|
||||
/* Compute the number of zones */
|
||||
nr = 0;
|
||||
for_each_zone(zone)
|
||||
if (populated_zone(zone))
|
||||
nr++;
|
||||
error = create_mem_extents(&mem_extents, gfp_mask);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/* Allocate the list of zones bitmap objects */
|
||||
zone_bm = create_zone_bm_list(nr, &ca);
|
||||
bm->zone_bm_list = zone_bm;
|
||||
if (!zone_bm) {
|
||||
chain_free(&ca, PG_UNSAFE_CLEAR);
|
||||
return -ENOMEM;
|
||||
}
|
||||
list_for_each_entry(ext, &mem_extents, hook) {
|
||||
struct bm_block *bb;
|
||||
unsigned long pfn = ext->start;
|
||||
unsigned long pages = ext->end - ext->start;
|
||||
|
||||
/* Initialize the zone bitmap objects */
|
||||
for_each_zone(zone) {
|
||||
unsigned long pfn;
|
||||
bb = list_entry(bm->blocks.prev, struct bm_block, hook);
|
||||
|
||||
if (!populated_zone(zone))
|
||||
continue;
|
||||
error = create_bm_block_list(pages, bm->blocks.prev, &ca);
|
||||
if (error)
|
||||
goto Error;
|
||||
|
||||
zone_bm->start_pfn = zone->zone_start_pfn;
|
||||
zone_bm->end_pfn = zone->zone_start_pfn + zone->spanned_pages;
|
||||
/* Allocate the list of bitmap block objects */
|
||||
nr = DIV_ROUND_UP(zone->spanned_pages, BM_BITS_PER_BLOCK);
|
||||
bb = create_bm_block_list(nr, &ca);
|
||||
zone_bm->bm_blocks = bb;
|
||||
zone_bm->cur_block = bb;
|
||||
if (!bb)
|
||||
goto Free;
|
||||
|
||||
nr = zone->spanned_pages;
|
||||
pfn = zone->zone_start_pfn;
|
||||
/* Initialize the bitmap block objects */
|
||||
while (bb) {
|
||||
unsigned long *ptr;
|
||||
|
||||
ptr = get_image_page(gfp_mask, safe_needed);
|
||||
bb->data = ptr;
|
||||
if (!ptr)
|
||||
goto Free;
|
||||
list_for_each_entry_continue(bb, &bm->blocks, hook) {
|
||||
bb->data = get_image_page(gfp_mask, safe_needed);
|
||||
if (!bb->data) {
|
||||
error = -ENOMEM;
|
||||
goto Error;
|
||||
}
|
||||
|
||||
bb->start_pfn = pfn;
|
||||
if (nr >= BM_BITS_PER_BLOCK) {
|
||||
if (pages >= BM_BITS_PER_BLOCK) {
|
||||
pfn += BM_BITS_PER_BLOCK;
|
||||
nr -= BM_BITS_PER_BLOCK;
|
||||
pages -= BM_BITS_PER_BLOCK;
|
||||
} else {
|
||||
/* This is executed only once in the loop */
|
||||
pfn += nr;
|
||||
pfn += pages;
|
||||
}
|
||||
bb->end_pfn = pfn;
|
||||
bb = bb->next;
|
||||
}
|
||||
zone_bm = zone_bm->next;
|
||||
}
|
||||
|
||||
bm->p_list = ca.chain;
|
||||
memory_bm_position_reset(bm);
|
||||
return 0;
|
||||
Exit:
|
||||
free_mem_extents(&mem_extents);
|
||||
return error;
|
||||
|
||||
Free:
|
||||
Error:
|
||||
bm->p_list = ca.chain;
|
||||
memory_bm_free(bm, PG_UNSAFE_CLEAR);
|
||||
return -ENOMEM;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* memory_bm_free - free memory occupied by the memory bitmap @bm
|
||||
*/
|
||||
|
||||
static void memory_bm_free(struct memory_bitmap *bm, int clear_nosave_free)
|
||||
{
|
||||
struct zone_bitmap *zone_bm;
|
||||
struct bm_block *bb;
|
||||
|
||||
/* Free the list of bit blocks for each zone_bitmap object */
|
||||
zone_bm = bm->zone_bm_list;
|
||||
while (zone_bm) {
|
||||
struct bm_block *bb;
|
||||
list_for_each_entry(bb, &bm->blocks, hook)
|
||||
if (bb->data)
|
||||
free_image_page(bb->data, clear_nosave_free);
|
||||
|
||||
bb = zone_bm->bm_blocks;
|
||||
while (bb) {
|
||||
if (bb->data)
|
||||
free_image_page(bb->data, clear_nosave_free);
|
||||
bb = bb->next;
|
||||
}
|
||||
zone_bm = zone_bm->next;
|
||||
}
|
||||
free_list_of_pages(bm->p_list, clear_nosave_free);
|
||||
bm->zone_bm_list = NULL;
|
||||
|
||||
INIT_LIST_HEAD(&bm->blocks);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -437,38 +452,33 @@ static void memory_bm_free(struct memory_bitmap *bm, int clear_nosave_free)
|
|||
* to given pfn. The cur_zone_bm member of @bm and the cur_block member
|
||||
* of @bm->cur_zone_bm are updated.
|
||||
*/
|
||||
|
||||
static int memory_bm_find_bit(struct memory_bitmap *bm, unsigned long pfn,
|
||||
void **addr, unsigned int *bit_nr)
|
||||
{
|
||||
struct zone_bitmap *zone_bm;
|
||||
struct bm_block *bb;
|
||||
|
||||
/* Check if the pfn is from the current zone */
|
||||
zone_bm = bm->cur.zone_bm;
|
||||
if (pfn < zone_bm->start_pfn || pfn >= zone_bm->end_pfn) {
|
||||
zone_bm = bm->zone_bm_list;
|
||||
/* We don't assume that the zones are sorted by pfns */
|
||||
while (pfn < zone_bm->start_pfn || pfn >= zone_bm->end_pfn) {
|
||||
zone_bm = zone_bm->next;
|
||||
|
||||
if (!zone_bm)
|
||||
return -EFAULT;
|
||||
}
|
||||
bm->cur.zone_bm = zone_bm;
|
||||
}
|
||||
/* Check if the pfn corresponds to the current bitmap block */
|
||||
bb = zone_bm->cur_block;
|
||||
/*
|
||||
* Check if the pfn corresponds to the current bitmap block and find
|
||||
* the block where it fits if this is not the case.
|
||||
*/
|
||||
bb = bm->cur.block;
|
||||
if (pfn < bb->start_pfn)
|
||||
bb = zone_bm->bm_blocks;
|
||||
list_for_each_entry_continue_reverse(bb, &bm->blocks, hook)
|
||||
if (pfn >= bb->start_pfn)
|
||||
break;
|
||||
|
||||
while (pfn >= bb->end_pfn) {
|
||||
bb = bb->next;
|
||||
if (pfn >= bb->end_pfn)
|
||||
list_for_each_entry_continue(bb, &bm->blocks, hook)
|
||||
if (pfn >= bb->start_pfn && pfn < bb->end_pfn)
|
||||
break;
|
||||
|
||||
BUG_ON(!bb);
|
||||
}
|
||||
zone_bm->cur_block = bb;
|
||||
if (&bb->hook == &bm->blocks)
|
||||
return -EFAULT;
|
||||
|
||||
/* The block has been found */
|
||||
bm->cur.block = bb;
|
||||
pfn -= bb->start_pfn;
|
||||
bm->cur.bit = pfn + 1;
|
||||
*bit_nr = pfn;
|
||||
*addr = bb->data;
|
||||
return 0;
|
||||
|
|
@ -519,6 +529,14 @@ static int memory_bm_test_bit(struct memory_bitmap *bm, unsigned long pfn)
|
|||
return test_bit(bit, addr);
|
||||
}
|
||||
|
||||
static bool memory_bm_pfn_present(struct memory_bitmap *bm, unsigned long pfn)
|
||||
{
|
||||
void *addr;
|
||||
unsigned int bit;
|
||||
|
||||
return !memory_bm_find_bit(bm, pfn, &addr, &bit);
|
||||
}
|
||||
|
||||
/**
|
||||
* memory_bm_next_pfn - find the pfn that corresponds to the next set bit
|
||||
* in the bitmap @bm. If the pfn cannot be found, BM_END_OF_MAP is
|
||||
|
|
@ -530,29 +548,21 @@ static int memory_bm_test_bit(struct memory_bitmap *bm, unsigned long pfn)
|
|||
|
||||
static unsigned long memory_bm_next_pfn(struct memory_bitmap *bm)
|
||||
{
|
||||
struct zone_bitmap *zone_bm;
|
||||
struct bm_block *bb;
|
||||
int bit;
|
||||
|
||||
bb = bm->cur.block;
|
||||
do {
|
||||
bb = bm->cur.block;
|
||||
do {
|
||||
bit = bm->cur.bit;
|
||||
bit = find_next_bit(bb->data, bm_block_bits(bb), bit);
|
||||
if (bit < bm_block_bits(bb))
|
||||
goto Return_pfn;
|
||||
bit = bm->cur.bit;
|
||||
bit = find_next_bit(bb->data, bm_block_bits(bb), bit);
|
||||
if (bit < bm_block_bits(bb))
|
||||
goto Return_pfn;
|
||||
|
||||
bb = list_entry(bb->hook.next, struct bm_block, hook);
|
||||
bm->cur.block = bb;
|
||||
bm->cur.bit = 0;
|
||||
} while (&bb->hook != &bm->blocks);
|
||||
|
||||
bb = bb->next;
|
||||
bm->cur.block = bb;
|
||||
bm->cur.bit = 0;
|
||||
} while (bb);
|
||||
zone_bm = bm->cur.zone_bm->next;
|
||||
if (zone_bm) {
|
||||
bm->cur.zone_bm = zone_bm;
|
||||
bm->cur.block = zone_bm->bm_blocks;
|
||||
bm->cur.bit = 0;
|
||||
}
|
||||
} while (zone_bm);
|
||||
memory_bm_position_reset(bm);
|
||||
return BM_END_OF_MAP;
|
||||
|
||||
|
|
@ -808,8 +818,7 @@ static unsigned int count_free_highmem_pages(void)
|
|||
* We should save the page if it isn't Nosave or NosaveFree, or Reserved,
|
||||
* and it isn't a part of a free chunk of pages.
|
||||
*/
|
||||
|
||||
static struct page *saveable_highmem_page(unsigned long pfn)
|
||||
static struct page *saveable_highmem_page(struct zone *zone, unsigned long pfn)
|
||||
{
|
||||
struct page *page;
|
||||
|
||||
|
|
@ -817,6 +826,8 @@ static struct page *saveable_highmem_page(unsigned long pfn)
|
|||
return NULL;
|
||||
|
||||
page = pfn_to_page(pfn);
|
||||
if (page_zone(page) != zone)
|
||||
return NULL;
|
||||
|
||||
BUG_ON(!PageHighMem(page));
|
||||
|
||||
|
|
@ -846,13 +857,16 @@ unsigned int count_highmem_pages(void)
|
|||
mark_free_pages(zone);
|
||||
max_zone_pfn = zone->zone_start_pfn + zone->spanned_pages;
|
||||
for (pfn = zone->zone_start_pfn; pfn < max_zone_pfn; pfn++)
|
||||
if (saveable_highmem_page(pfn))
|
||||
if (saveable_highmem_page(zone, pfn))
|
||||
n++;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
#else
|
||||
static inline void *saveable_highmem_page(unsigned long pfn) { return NULL; }
|
||||
static inline void *saveable_highmem_page(struct zone *z, unsigned long p)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif /* CONFIG_HIGHMEM */
|
||||
|
||||
/**
|
||||
|
|
@ -863,8 +877,7 @@ static inline void *saveable_highmem_page(unsigned long pfn) { return NULL; }
|
|||
* of pages statically defined as 'unsaveable', and it isn't a part of
|
||||
* a free chunk of pages.
|
||||
*/
|
||||
|
||||
static struct page *saveable_page(unsigned long pfn)
|
||||
static struct page *saveable_page(struct zone *zone, unsigned long pfn)
|
||||
{
|
||||
struct page *page;
|
||||
|
||||
|
|
@ -872,6 +885,8 @@ static struct page *saveable_page(unsigned long pfn)
|
|||
return NULL;
|
||||
|
||||
page = pfn_to_page(pfn);
|
||||
if (page_zone(page) != zone)
|
||||
return NULL;
|
||||
|
||||
BUG_ON(PageHighMem(page));
|
||||
|
||||
|
|
@ -903,7 +918,7 @@ unsigned int count_data_pages(void)
|
|||
mark_free_pages(zone);
|
||||
max_zone_pfn = zone->zone_start_pfn + zone->spanned_pages;
|
||||
for (pfn = zone->zone_start_pfn; pfn < max_zone_pfn; pfn++)
|
||||
if(saveable_page(pfn))
|
||||
if (saveable_page(zone, pfn))
|
||||
n++;
|
||||
}
|
||||
return n;
|
||||
|
|
@ -944,7 +959,7 @@ static inline struct page *
|
|||
page_is_saveable(struct zone *zone, unsigned long pfn)
|
||||
{
|
||||
return is_highmem(zone) ?
|
||||
saveable_highmem_page(pfn) : saveable_page(pfn);
|
||||
saveable_highmem_page(zone, pfn) : saveable_page(zone, pfn);
|
||||
}
|
||||
|
||||
static void copy_data_page(unsigned long dst_pfn, unsigned long src_pfn)
|
||||
|
|
@ -966,7 +981,7 @@ static void copy_data_page(unsigned long dst_pfn, unsigned long src_pfn)
|
|||
* data modified by kmap_atomic()
|
||||
*/
|
||||
safe_copy_page(buffer, s_page);
|
||||
dst = kmap_atomic(pfn_to_page(dst_pfn), KM_USER0);
|
||||
dst = kmap_atomic(d_page, KM_USER0);
|
||||
memcpy(dst, buffer, PAGE_SIZE);
|
||||
kunmap_atomic(dst, KM_USER0);
|
||||
} else {
|
||||
|
|
@ -975,7 +990,7 @@ static void copy_data_page(unsigned long dst_pfn, unsigned long src_pfn)
|
|||
}
|
||||
}
|
||||
#else
|
||||
#define page_is_saveable(zone, pfn) saveable_page(pfn)
|
||||
#define page_is_saveable(zone, pfn) saveable_page(zone, pfn)
|
||||
|
||||
static inline void copy_data_page(unsigned long dst_pfn, unsigned long src_pfn)
|
||||
{
|
||||
|
|
@ -1459,9 +1474,7 @@ load_header(struct swsusp_info *info)
|
|||
* unpack_orig_pfns - for each element of @buf[] (1 page at a time) set
|
||||
* the corresponding bit in the memory bitmap @bm
|
||||
*/
|
||||
|
||||
static inline void
|
||||
unpack_orig_pfns(unsigned long *buf, struct memory_bitmap *bm)
|
||||
static int unpack_orig_pfns(unsigned long *buf, struct memory_bitmap *bm)
|
||||
{
|
||||
int j;
|
||||
|
||||
|
|
@ -1469,8 +1482,13 @@ unpack_orig_pfns(unsigned long *buf, struct memory_bitmap *bm)
|
|||
if (unlikely(buf[j] == BM_END_OF_MAP))
|
||||
break;
|
||||
|
||||
memory_bm_set_bit(bm, buf[j]);
|
||||
if (memory_bm_pfn_present(bm, buf[j]))
|
||||
memory_bm_set_bit(bm, buf[j]);
|
||||
else
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* List of "safe" pages that may be used to store data loaded from the suspend
|
||||
|
|
@ -1608,7 +1626,7 @@ get_highmem_page_buffer(struct page *page, struct chain_allocator *ca)
|
|||
pbe = chain_alloc(ca, sizeof(struct highmem_pbe));
|
||||
if (!pbe) {
|
||||
swsusp_free();
|
||||
return NULL;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
pbe->orig_page = page;
|
||||
if (safe_highmem_pages > 0) {
|
||||
|
|
@ -1677,7 +1695,7 @@ prepare_highmem_image(struct memory_bitmap *bm, unsigned int *nr_highmem_p)
|
|||
static inline void *
|
||||
get_highmem_page_buffer(struct page *page, struct chain_allocator *ca)
|
||||
{
|
||||
return NULL;
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
static inline void copy_last_highmem_page(void) {}
|
||||
|
|
@ -1788,8 +1806,13 @@ prepare_image(struct memory_bitmap *new_bm, struct memory_bitmap *bm)
|
|||
static void *get_buffer(struct memory_bitmap *bm, struct chain_allocator *ca)
|
||||
{
|
||||
struct pbe *pbe;
|
||||
struct page *page = pfn_to_page(memory_bm_next_pfn(bm));
|
||||
struct page *page;
|
||||
unsigned long pfn = memory_bm_next_pfn(bm);
|
||||
|
||||
if (pfn == BM_END_OF_MAP)
|
||||
return ERR_PTR(-EFAULT);
|
||||
|
||||
page = pfn_to_page(pfn);
|
||||
if (PageHighMem(page))
|
||||
return get_highmem_page_buffer(page, ca);
|
||||
|
||||
|
|
@ -1805,7 +1828,7 @@ static void *get_buffer(struct memory_bitmap *bm, struct chain_allocator *ca)
|
|||
pbe = chain_alloc(ca, sizeof(struct pbe));
|
||||
if (!pbe) {
|
||||
swsusp_free();
|
||||
return NULL;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
pbe->orig_address = page_address(page);
|
||||
pbe->address = safe_pages_list;
|
||||
|
|
@ -1868,7 +1891,10 @@ int snapshot_write_next(struct snapshot_handle *handle, size_t count)
|
|||
return error;
|
||||
|
||||
} else if (handle->prev <= nr_meta_pages) {
|
||||
unpack_orig_pfns(buffer, ©_bm);
|
||||
error = unpack_orig_pfns(buffer, ©_bm);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (handle->prev == nr_meta_pages) {
|
||||
error = prepare_image(&orig_bm, ©_bm);
|
||||
if (error)
|
||||
|
|
@ -1879,12 +1905,14 @@ int snapshot_write_next(struct snapshot_handle *handle, size_t count)
|
|||
restore_pblist = NULL;
|
||||
handle->buffer = get_buffer(&orig_bm, &ca);
|
||||
handle->sync_read = 0;
|
||||
if (!handle->buffer)
|
||||
return -ENOMEM;
|
||||
if (IS_ERR(handle->buffer))
|
||||
return PTR_ERR(handle->buffer);
|
||||
}
|
||||
} else {
|
||||
copy_last_highmem_page();
|
||||
handle->buffer = get_buffer(&orig_bm, &ca);
|
||||
if (IS_ERR(handle->buffer))
|
||||
return PTR_ERR(handle->buffer);
|
||||
if (handle->buffer != buffer)
|
||||
handle->sync_read = 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -262,3 +262,125 @@ int swsusp_shrink_memory(void)
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Platforms, like ACPI, may want us to save some memory used by them during
|
||||
* hibernation and to restore the contents of this memory during the subsequent
|
||||
* resume. The code below implements a mechanism allowing us to do that.
|
||||
*/
|
||||
|
||||
struct nvs_page {
|
||||
unsigned long phys_start;
|
||||
unsigned int size;
|
||||
void *kaddr;
|
||||
void *data;
|
||||
struct list_head node;
|
||||
};
|
||||
|
||||
static LIST_HEAD(nvs_list);
|
||||
|
||||
/**
|
||||
* hibernate_nvs_register - register platform NVS memory region to save
|
||||
* @start - physical address of the region
|
||||
* @size - size of the region
|
||||
*
|
||||
* The NVS region need not be page-aligned (both ends) and we arrange
|
||||
* things so that the data from page-aligned addresses in this region will
|
||||
* be copied into separate RAM pages.
|
||||
*/
|
||||
int hibernate_nvs_register(unsigned long start, unsigned long size)
|
||||
{
|
||||
struct nvs_page *entry, *next;
|
||||
|
||||
while (size > 0) {
|
||||
unsigned int nr_bytes;
|
||||
|
||||
entry = kzalloc(sizeof(struct nvs_page), GFP_KERNEL);
|
||||
if (!entry)
|
||||
goto Error;
|
||||
|
||||
list_add_tail(&entry->node, &nvs_list);
|
||||
entry->phys_start = start;
|
||||
nr_bytes = PAGE_SIZE - (start & ~PAGE_MASK);
|
||||
entry->size = (size < nr_bytes) ? size : nr_bytes;
|
||||
|
||||
start += entry->size;
|
||||
size -= entry->size;
|
||||
}
|
||||
return 0;
|
||||
|
||||
Error:
|
||||
list_for_each_entry_safe(entry, next, &nvs_list, node) {
|
||||
list_del(&entry->node);
|
||||
kfree(entry);
|
||||
}
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/**
|
||||
* hibernate_nvs_free - free data pages allocated for saving NVS regions
|
||||
*/
|
||||
void hibernate_nvs_free(void)
|
||||
{
|
||||
struct nvs_page *entry;
|
||||
|
||||
list_for_each_entry(entry, &nvs_list, node)
|
||||
if (entry->data) {
|
||||
free_page((unsigned long)entry->data);
|
||||
entry->data = NULL;
|
||||
if (entry->kaddr) {
|
||||
iounmap(entry->kaddr);
|
||||
entry->kaddr = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* hibernate_nvs_alloc - allocate memory necessary for saving NVS regions
|
||||
*/
|
||||
int hibernate_nvs_alloc(void)
|
||||
{
|
||||
struct nvs_page *entry;
|
||||
|
||||
list_for_each_entry(entry, &nvs_list, node) {
|
||||
entry->data = (void *)__get_free_page(GFP_KERNEL);
|
||||
if (!entry->data) {
|
||||
hibernate_nvs_free();
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* hibernate_nvs_save - save NVS memory regions
|
||||
*/
|
||||
void hibernate_nvs_save(void)
|
||||
{
|
||||
struct nvs_page *entry;
|
||||
|
||||
printk(KERN_INFO "PM: Saving platform NVS memory\n");
|
||||
|
||||
list_for_each_entry(entry, &nvs_list, node)
|
||||
if (entry->data) {
|
||||
entry->kaddr = ioremap(entry->phys_start, entry->size);
|
||||
memcpy(entry->data, entry->kaddr, entry->size);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* hibernate_nvs_restore - restore NVS memory regions
|
||||
*
|
||||
* This function is going to be called with interrupts disabled, so it
|
||||
* cannot iounmap the virtual addresses used to access the NVS region.
|
||||
*/
|
||||
void hibernate_nvs_restore(void)
|
||||
{
|
||||
struct nvs_page *entry;
|
||||
|
||||
printk(KERN_INFO "PM: Restoring platform NVS memory\n");
|
||||
|
||||
list_for_each_entry(entry, &nvs_list, node)
|
||||
if (entry->data)
|
||||
memcpy(entry->kaddr, entry->data, entry->size);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -382,7 +382,7 @@ out:
|
|||
return error;
|
||||
}
|
||||
|
||||
asmlinkage long sys_syslog(int type, char __user *buf, int len)
|
||||
SYSCALL_DEFINE3(syslog, int, type, char __user *, buf, int, len)
|
||||
{
|
||||
return do_syslog(type, buf, len);
|
||||
}
|
||||
|
|
@ -619,7 +619,7 @@ static int acquire_console_semaphore_for_printk(unsigned int cpu)
|
|||
static const char recursion_bug_msg [] =
|
||||
KERN_CRIT "BUG: recent printk recursion!\n";
|
||||
static int recursion_bug;
|
||||
static int new_text_line = 1;
|
||||
static int new_text_line = 1;
|
||||
static char printk_buf[1024];
|
||||
|
||||
asmlinkage int vprintk(const char *fmt, va_list args)
|
||||
|
|
@ -662,7 +662,7 @@ asmlinkage int vprintk(const char *fmt, va_list args)
|
|||
if (recursion_bug) {
|
||||
recursion_bug = 0;
|
||||
strcpy(printk_buf, recursion_bug_msg);
|
||||
printed_len = sizeof(recursion_bug_msg);
|
||||
printed_len = strlen(recursion_bug_msg);
|
||||
}
|
||||
/* Emit the output into the temporary buffer */
|
||||
printed_len += vscnprintf(printk_buf + printed_len,
|
||||
|
|
@ -742,11 +742,6 @@ EXPORT_SYMBOL(vprintk);
|
|||
|
||||
#else
|
||||
|
||||
asmlinkage long sys_syslog(int type, char __user *buf, int len)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static void call_console_drivers(unsigned start, unsigned end)
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ static unsigned long prof_len, prof_shift;
|
|||
int prof_on __read_mostly;
|
||||
EXPORT_SYMBOL_GPL(prof_on);
|
||||
|
||||
static cpumask_t prof_cpu_mask = CPU_MASK_ALL;
|
||||
static cpumask_var_t prof_cpu_mask;
|
||||
#ifdef CONFIG_SMP
|
||||
static DEFINE_PER_CPU(struct profile_hit *[2], cpu_profile_hits);
|
||||
static DEFINE_PER_CPU(int, cpu_profile_flip);
|
||||
|
|
@ -113,9 +113,13 @@ int __ref profile_init(void)
|
|||
buffer_bytes = prof_len*sizeof(atomic_t);
|
||||
if (!slab_is_available()) {
|
||||
prof_buffer = alloc_bootmem(buffer_bytes);
|
||||
alloc_bootmem_cpumask_var(&prof_cpu_mask);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!alloc_cpumask_var(&prof_cpu_mask, GFP_KERNEL))
|
||||
return -ENOMEM;
|
||||
|
||||
prof_buffer = kzalloc(buffer_bytes, GFP_KERNEL);
|
||||
if (prof_buffer)
|
||||
return 0;
|
||||
|
|
@ -128,6 +132,7 @@ int __ref profile_init(void)
|
|||
if (prof_buffer)
|
||||
return 0;
|
||||
|
||||
free_cpumask_var(prof_cpu_mask);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
|
|
@ -386,13 +391,15 @@ out_free:
|
|||
return NOTIFY_BAD;
|
||||
case CPU_ONLINE:
|
||||
case CPU_ONLINE_FROZEN:
|
||||
cpu_set(cpu, prof_cpu_mask);
|
||||
if (prof_cpu_mask != NULL)
|
||||
cpumask_set_cpu(cpu, prof_cpu_mask);
|
||||
break;
|
||||
case CPU_UP_CANCELED:
|
||||
case CPU_UP_CANCELED_FROZEN:
|
||||
case CPU_DEAD:
|
||||
case CPU_DEAD_FROZEN:
|
||||
cpu_clear(cpu, prof_cpu_mask);
|
||||
if (prof_cpu_mask != NULL)
|
||||
cpumask_clear_cpu(cpu, prof_cpu_mask);
|
||||
if (per_cpu(cpu_profile_hits, cpu)[0]) {
|
||||
page = virt_to_page(per_cpu(cpu_profile_hits, cpu)[0]);
|
||||
per_cpu(cpu_profile_hits, cpu)[0] = NULL;
|
||||
|
|
@ -430,19 +437,19 @@ void profile_tick(int type)
|
|||
|
||||
if (type == CPU_PROFILING && timer_hook)
|
||||
timer_hook(regs);
|
||||
if (!user_mode(regs) && cpu_isset(smp_processor_id(), prof_cpu_mask))
|
||||
if (!user_mode(regs) && prof_cpu_mask != NULL &&
|
||||
cpumask_test_cpu(smp_processor_id(), prof_cpu_mask))
|
||||
profile_hit(type, (void *)profile_pc(regs));
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
#include <linux/proc_fs.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/ptrace.h>
|
||||
|
||||
static int prof_cpu_mask_read_proc(char *page, char **start, off_t off,
|
||||
int count, int *eof, void *data)
|
||||
{
|
||||
int len = cpumask_scnprintf(page, count, *(cpumask_t *)data);
|
||||
int len = cpumask_scnprintf(page, count, data);
|
||||
if (count - len < 2)
|
||||
return -EINVAL;
|
||||
len += sprintf(page + len, "\n");
|
||||
|
|
@ -452,16 +459,20 @@ static int prof_cpu_mask_read_proc(char *page, char **start, off_t off,
|
|||
static int prof_cpu_mask_write_proc(struct file *file,
|
||||
const char __user *buffer, unsigned long count, void *data)
|
||||
{
|
||||
cpumask_t *mask = (cpumask_t *)data;
|
||||
struct cpumask *mask = data;
|
||||
unsigned long full_count = count, err;
|
||||
cpumask_t new_value;
|
||||
cpumask_var_t new_value;
|
||||
|
||||
if (!alloc_cpumask_var(&new_value, GFP_KERNEL))
|
||||
return -ENOMEM;
|
||||
|
||||
err = cpumask_parse_user(buffer, count, new_value);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
*mask = new_value;
|
||||
return full_count;
|
||||
if (!err) {
|
||||
cpumask_copy(mask, new_value);
|
||||
err = full_count;
|
||||
}
|
||||
free_cpumask_var(new_value);
|
||||
return err;
|
||||
}
|
||||
|
||||
void create_prof_cpu_mask(struct proc_dir_entry *root_irq_dir)
|
||||
|
|
@ -472,7 +483,7 @@ void create_prof_cpu_mask(struct proc_dir_entry *root_irq_dir)
|
|||
entry = create_proc_entry("prof_cpu_mask", 0600, root_irq_dir);
|
||||
if (!entry)
|
||||
return;
|
||||
entry->data = (void *)&prof_cpu_mask;
|
||||
entry->data = prof_cpu_mask;
|
||||
entry->read_proc = prof_cpu_mask_read_proc;
|
||||
entry->write_proc = prof_cpu_mask_write_proc;
|
||||
}
|
||||
|
|
@ -544,7 +555,7 @@ static const struct file_operations proc_profile_operations = {
|
|||
};
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
static inline void profile_nop(void *unused)
|
||||
static void profile_nop(void *unused)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,17 @@
|
|||
#include <asm/pgtable.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
|
||||
/*
|
||||
* Initialize a new task whose father had been ptraced.
|
||||
*
|
||||
* Called from copy_process().
|
||||
*/
|
||||
void ptrace_fork(struct task_struct *child, unsigned long clone_flags)
|
||||
{
|
||||
arch_ptrace_fork(child, clone_flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* ptrace a task: make the debugger its new parent and
|
||||
* move it to the ptrace list.
|
||||
|
|
@ -72,6 +83,7 @@ void __ptrace_unlink(struct task_struct *child)
|
|||
child->parent = child->real_parent;
|
||||
list_del_init(&child->ptrace_entry);
|
||||
|
||||
arch_ptrace_untrace(child);
|
||||
if (task_is_traced(child))
|
||||
ptrace_untrace(child);
|
||||
}
|
||||
|
|
@ -115,6 +127,8 @@ int ptrace_check_attach(struct task_struct *child, int kill)
|
|||
|
||||
int __ptrace_may_access(struct task_struct *task, unsigned int mode)
|
||||
{
|
||||
const struct cred *cred = current_cred(), *tcred;
|
||||
|
||||
/* May we inspect the given task?
|
||||
* This check is used both for attaching with ptrace
|
||||
* and for allowing access to sensitive information in /proc.
|
||||
|
|
@ -127,13 +141,19 @@ int __ptrace_may_access(struct task_struct *task, unsigned int mode)
|
|||
/* Don't let security modules deny introspection */
|
||||
if (task == current)
|
||||
return 0;
|
||||
if (((current->uid != task->euid) ||
|
||||
(current->uid != task->suid) ||
|
||||
(current->uid != task->uid) ||
|
||||
(current->gid != task->egid) ||
|
||||
(current->gid != task->sgid) ||
|
||||
(current->gid != task->gid)) && !capable(CAP_SYS_PTRACE))
|
||||
rcu_read_lock();
|
||||
tcred = __task_cred(task);
|
||||
if ((cred->uid != tcred->euid ||
|
||||
cred->uid != tcred->suid ||
|
||||
cred->uid != tcred->uid ||
|
||||
cred->gid != tcred->egid ||
|
||||
cred->gid != tcred->sgid ||
|
||||
cred->gid != tcred->gid) &&
|
||||
!capable(CAP_SYS_PTRACE)) {
|
||||
rcu_read_unlock();
|
||||
return -EPERM;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
smp_rmb();
|
||||
if (task->mm)
|
||||
dumpable = get_dumpable(task->mm);
|
||||
|
|
@ -163,6 +183,14 @@ int ptrace_attach(struct task_struct *task)
|
|||
if (same_thread_group(task, current))
|
||||
goto out;
|
||||
|
||||
/* Protect exec's credential calculations against our interference;
|
||||
* SUID, SGID and LSM creds get determined differently under ptrace.
|
||||
*/
|
||||
retval = mutex_lock_interruptible(¤t->cred_exec_mutex);
|
||||
if (retval < 0)
|
||||
goto out;
|
||||
|
||||
retval = -EPERM;
|
||||
repeat:
|
||||
/*
|
||||
* Nasty, nasty.
|
||||
|
|
@ -202,6 +230,7 @@ repeat:
|
|||
bad:
|
||||
write_unlock_irqrestore(&tasklist_lock, flags);
|
||||
task_unlock(task);
|
||||
mutex_unlock(¤t->cred_exec_mutex);
|
||||
out:
|
||||
return retval;
|
||||
}
|
||||
|
|
@ -545,7 +574,7 @@ struct task_struct *ptrace_get_task_struct(pid_t pid)
|
|||
#define arch_ptrace_attach(child) do { } while (0)
|
||||
#endif
|
||||
|
||||
asmlinkage long sys_ptrace(long request, long pid, long addr, long data)
|
||||
SYSCALL_DEFINE4(ptrace, long, request, long, pid, long, addr, long, data)
|
||||
{
|
||||
struct task_struct *child;
|
||||
long ret;
|
||||
|
|
|
|||
|
|
@ -63,14 +63,14 @@ static struct rcu_ctrlblk rcu_ctrlblk = {
|
|||
.completed = -300,
|
||||
.pending = -300,
|
||||
.lock = __SPIN_LOCK_UNLOCKED(&rcu_ctrlblk.lock),
|
||||
.cpumask = CPU_MASK_NONE,
|
||||
.cpumask = CPU_BITS_NONE,
|
||||
};
|
||||
static struct rcu_ctrlblk rcu_bh_ctrlblk = {
|
||||
.cur = -300,
|
||||
.completed = -300,
|
||||
.pending = -300,
|
||||
.lock = __SPIN_LOCK_UNLOCKED(&rcu_bh_ctrlblk.lock),
|
||||
.cpumask = CPU_MASK_NONE,
|
||||
.cpumask = CPU_BITS_NONE,
|
||||
};
|
||||
|
||||
DEFINE_PER_CPU(struct rcu_data, rcu_data) = { 0L };
|
||||
|
|
@ -85,7 +85,6 @@ static void force_quiescent_state(struct rcu_data *rdp,
|
|||
struct rcu_ctrlblk *rcp)
|
||||
{
|
||||
int cpu;
|
||||
cpumask_t cpumask;
|
||||
unsigned long flags;
|
||||
|
||||
set_need_resched();
|
||||
|
|
@ -96,10 +95,10 @@ static void force_quiescent_state(struct rcu_data *rdp,
|
|||
* Don't send IPI to itself. With irqs disabled,
|
||||
* rdp->cpu is the current cpu.
|
||||
*
|
||||
* cpu_online_map is updated by the _cpu_down()
|
||||
* cpu_online_mask is updated by the _cpu_down()
|
||||
* using __stop_machine(). Since we're in irqs disabled
|
||||
* section, __stop_machine() is not exectuting, hence
|
||||
* the cpu_online_map is stable.
|
||||
* the cpu_online_mask is stable.
|
||||
*
|
||||
* However, a cpu might have been offlined _just_ before
|
||||
* we disabled irqs while entering here.
|
||||
|
|
@ -107,13 +106,14 @@ static void force_quiescent_state(struct rcu_data *rdp,
|
|||
* notification, leading to the offlined cpu's bit
|
||||
* being set in the rcp->cpumask.
|
||||
*
|
||||
* Hence cpumask = (rcp->cpumask & cpu_online_map) to prevent
|
||||
* Hence cpumask = (rcp->cpumask & cpu_online_mask) to prevent
|
||||
* sending smp_reschedule() to an offlined CPU.
|
||||
*/
|
||||
cpus_and(cpumask, rcp->cpumask, cpu_online_map);
|
||||
cpu_clear(rdp->cpu, cpumask);
|
||||
for_each_cpu_mask_nr(cpu, cpumask)
|
||||
smp_send_reschedule(cpu);
|
||||
for_each_cpu_and(cpu,
|
||||
to_cpumask(rcp->cpumask), cpu_online_mask) {
|
||||
if (cpu != rdp->cpu)
|
||||
smp_send_reschedule(cpu);
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&rcp->lock, flags);
|
||||
}
|
||||
|
|
@ -191,9 +191,9 @@ static void print_other_cpu_stall(struct rcu_ctrlblk *rcp)
|
|||
|
||||
/* OK, time to rat on our buddy... */
|
||||
|
||||
printk(KERN_ERR "RCU detected CPU stalls:");
|
||||
printk(KERN_ERR "INFO: RCU detected CPU stalls:");
|
||||
for_each_possible_cpu(cpu) {
|
||||
if (cpu_isset(cpu, rcp->cpumask))
|
||||
if (cpumask_test_cpu(cpu, to_cpumask(rcp->cpumask)))
|
||||
printk(" %d", cpu);
|
||||
}
|
||||
printk(" (detected by %d, t=%ld jiffies)\n",
|
||||
|
|
@ -204,7 +204,7 @@ static void print_cpu_stall(struct rcu_ctrlblk *rcp)
|
|||
{
|
||||
unsigned long flags;
|
||||
|
||||
printk(KERN_ERR "RCU detected CPU %d stall (t=%lu/%lu jiffies)\n",
|
||||
printk(KERN_ERR "INFO: RCU detected CPU %d stall (t=%lu/%lu jiffies)\n",
|
||||
smp_processor_id(), jiffies,
|
||||
jiffies - rcp->gp_start);
|
||||
dump_stack();
|
||||
|
|
@ -221,7 +221,8 @@ static void check_cpu_stall(struct rcu_ctrlblk *rcp)
|
|||
long delta;
|
||||
|
||||
delta = jiffies - rcp->jiffies_stall;
|
||||
if (cpu_isset(smp_processor_id(), rcp->cpumask) && delta >= 0) {
|
||||
if (cpumask_test_cpu(smp_processor_id(), to_cpumask(rcp->cpumask)) &&
|
||||
delta >= 0) {
|
||||
|
||||
/* We haven't checked in, so go dump stack. */
|
||||
print_cpu_stall(rcp);
|
||||
|
|
@ -393,7 +394,8 @@ static void rcu_start_batch(struct rcu_ctrlblk *rcp)
|
|||
* unnecessarily.
|
||||
*/
|
||||
smp_mb();
|
||||
cpus_andnot(rcp->cpumask, cpu_online_map, nohz_cpu_mask);
|
||||
cpumask_andnot(to_cpumask(rcp->cpumask),
|
||||
cpu_online_mask, nohz_cpu_mask);
|
||||
|
||||
rcp->signaled = 0;
|
||||
}
|
||||
|
|
@ -406,8 +408,8 @@ static void rcu_start_batch(struct rcu_ctrlblk *rcp)
|
|||
*/
|
||||
static void cpu_quiet(int cpu, struct rcu_ctrlblk *rcp)
|
||||
{
|
||||
cpu_clear(cpu, rcp->cpumask);
|
||||
if (cpus_empty(rcp->cpumask)) {
|
||||
cpumask_clear_cpu(cpu, to_cpumask(rcp->cpumask));
|
||||
if (cpumask_empty(to_cpumask(rcp->cpumask))) {
|
||||
/* batch completed ! */
|
||||
rcp->completed = rcp->cur;
|
||||
rcu_start_batch(rcp);
|
||||
|
|
|
|||
|
|
@ -77,8 +77,15 @@ void wakeme_after_rcu(struct rcu_head *head)
|
|||
* sections are delimited by rcu_read_lock() and rcu_read_unlock(),
|
||||
* and may be nested.
|
||||
*/
|
||||
void synchronize_rcu(void); /* Makes kernel-doc tools happy */
|
||||
synchronize_rcu_xxx(synchronize_rcu, call_rcu)
|
||||
void synchronize_rcu(void)
|
||||
{
|
||||
struct rcu_synchronize rcu;
|
||||
init_completion(&rcu.completion);
|
||||
/* Will wake me after RCU finished. */
|
||||
call_rcu(&rcu.head, wakeme_after_rcu);
|
||||
/* Wait for it. */
|
||||
wait_for_completion(&rcu.completion);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(synchronize_rcu);
|
||||
|
||||
static void rcu_barrier_callback(struct rcu_head *notused)
|
||||
|
|
|
|||
|
|
@ -164,7 +164,8 @@ static char *rcu_try_flip_state_names[] =
|
|||
{ "idle", "waitack", "waitzero", "waitmb" };
|
||||
#endif /* #ifdef CONFIG_RCU_TRACE */
|
||||
|
||||
static cpumask_t rcu_cpu_online_map __read_mostly = CPU_MASK_NONE;
|
||||
static DECLARE_BITMAP(rcu_cpu_online_map, NR_CPUS) __read_mostly
|
||||
= CPU_BITS_NONE;
|
||||
|
||||
/*
|
||||
* Enum and per-CPU flag to determine when each CPU has seen
|
||||
|
|
@ -551,6 +552,16 @@ void rcu_irq_exit(void)
|
|||
}
|
||||
}
|
||||
|
||||
void rcu_nmi_enter(void)
|
||||
{
|
||||
rcu_irq_enter();
|
||||
}
|
||||
|
||||
void rcu_nmi_exit(void)
|
||||
{
|
||||
rcu_irq_exit();
|
||||
}
|
||||
|
||||
static void dyntick_save_progress_counter(int cpu)
|
||||
{
|
||||
struct rcu_dyntick_sched *rdssp = &per_cpu(rcu_dyntick_sched, cpu);
|
||||
|
|
@ -748,7 +759,7 @@ rcu_try_flip_idle(void)
|
|||
|
||||
/* Now ask each CPU for acknowledgement of the flip. */
|
||||
|
||||
for_each_cpu_mask_nr(cpu, rcu_cpu_online_map) {
|
||||
for_each_cpu(cpu, to_cpumask(rcu_cpu_online_map)) {
|
||||
per_cpu(rcu_flip_flag, cpu) = rcu_flipped;
|
||||
dyntick_save_progress_counter(cpu);
|
||||
}
|
||||
|
|
@ -766,7 +777,7 @@ rcu_try_flip_waitack(void)
|
|||
int cpu;
|
||||
|
||||
RCU_TRACE_ME(rcupreempt_trace_try_flip_a1);
|
||||
for_each_cpu_mask_nr(cpu, rcu_cpu_online_map)
|
||||
for_each_cpu(cpu, to_cpumask(rcu_cpu_online_map))
|
||||
if (rcu_try_flip_waitack_needed(cpu) &&
|
||||
per_cpu(rcu_flip_flag, cpu) != rcu_flip_seen) {
|
||||
RCU_TRACE_ME(rcupreempt_trace_try_flip_ae1);
|
||||
|
|
@ -798,7 +809,7 @@ rcu_try_flip_waitzero(void)
|
|||
/* Check to see if the sum of the "last" counters is zero. */
|
||||
|
||||
RCU_TRACE_ME(rcupreempt_trace_try_flip_z1);
|
||||
for_each_cpu_mask_nr(cpu, rcu_cpu_online_map)
|
||||
for_each_cpu(cpu, to_cpumask(rcu_cpu_online_map))
|
||||
sum += RCU_DATA_CPU(cpu)->rcu_flipctr[lastidx];
|
||||
if (sum != 0) {
|
||||
RCU_TRACE_ME(rcupreempt_trace_try_flip_ze1);
|
||||
|
|
@ -813,7 +824,7 @@ rcu_try_flip_waitzero(void)
|
|||
smp_mb(); /* ^^^^^^^^^^^^ */
|
||||
|
||||
/* Call for a memory barrier from each CPU. */
|
||||
for_each_cpu_mask_nr(cpu, rcu_cpu_online_map) {
|
||||
for_each_cpu(cpu, to_cpumask(rcu_cpu_online_map)) {
|
||||
per_cpu(rcu_mb_flag, cpu) = rcu_mb_needed;
|
||||
dyntick_save_progress_counter(cpu);
|
||||
}
|
||||
|
|
@ -833,7 +844,7 @@ rcu_try_flip_waitmb(void)
|
|||
int cpu;
|
||||
|
||||
RCU_TRACE_ME(rcupreempt_trace_try_flip_m1);
|
||||
for_each_cpu_mask_nr(cpu, rcu_cpu_online_map)
|
||||
for_each_cpu(cpu, to_cpumask(rcu_cpu_online_map))
|
||||
if (rcu_try_flip_waitmb_needed(cpu) &&
|
||||
per_cpu(rcu_mb_flag, cpu) != rcu_mb_done) {
|
||||
RCU_TRACE_ME(rcupreempt_trace_try_flip_me1);
|
||||
|
|
@ -1022,7 +1033,7 @@ void rcu_offline_cpu(int cpu)
|
|||
RCU_DATA_CPU(cpu)->rcu_flipctr[0] = 0;
|
||||
RCU_DATA_CPU(cpu)->rcu_flipctr[1] = 0;
|
||||
|
||||
cpu_clear(cpu, rcu_cpu_online_map);
|
||||
cpumask_clear_cpu(cpu, to_cpumask(rcu_cpu_online_map));
|
||||
|
||||
spin_unlock_irqrestore(&rcu_ctrlblk.fliplock, flags);
|
||||
|
||||
|
|
@ -1062,7 +1073,7 @@ void __cpuinit rcu_online_cpu(int cpu)
|
|||
struct rcu_data *rdp;
|
||||
|
||||
spin_lock_irqsave(&rcu_ctrlblk.fliplock, flags);
|
||||
cpu_set(cpu, rcu_cpu_online_map);
|
||||
cpumask_set_cpu(cpu, to_cpumask(rcu_cpu_online_map));
|
||||
spin_unlock_irqrestore(&rcu_ctrlblk.fliplock, flags);
|
||||
|
||||
/*
|
||||
|
|
@ -1166,7 +1177,16 @@ EXPORT_SYMBOL_GPL(call_rcu_sched);
|
|||
* in -rt this does -not- necessarily result in all currently executing
|
||||
* interrupt -handlers- having completed.
|
||||
*/
|
||||
synchronize_rcu_xxx(__synchronize_sched, call_rcu_sched)
|
||||
void __synchronize_sched(void)
|
||||
{
|
||||
struct rcu_synchronize rcu;
|
||||
|
||||
init_completion(&rcu.completion);
|
||||
/* Will wake me after RCU finished. */
|
||||
call_rcu_sched(&rcu.head, wakeme_after_rcu);
|
||||
/* Wait for it. */
|
||||
wait_for_completion(&rcu.completion);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__synchronize_sched);
|
||||
|
||||
/*
|
||||
|
|
@ -1420,7 +1440,7 @@ void __init __rcu_init(void)
|
|||
* We don't need protection against CPU-Hotplug here
|
||||
* since
|
||||
* a) If a CPU comes online while we are iterating over the
|
||||
* cpu_online_map below, we would only end up making a
|
||||
* cpu_online_mask below, we would only end up making a
|
||||
* duplicate call to rcu_online_cpu() which sets the corresponding
|
||||
* CPU's mask in the rcu_cpu_online_map.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -149,12 +149,12 @@ static void rcupreempt_trace_sum(struct rcupreempt_trace *sp)
|
|||
sp->done_length += cp->done_length;
|
||||
sp->done_add += cp->done_add;
|
||||
sp->done_remove += cp->done_remove;
|
||||
atomic_set(&sp->done_invoked, atomic_read(&cp->done_invoked));
|
||||
atomic_add(atomic_read(&cp->done_invoked), &sp->done_invoked);
|
||||
sp->rcu_check_callbacks += cp->rcu_check_callbacks;
|
||||
atomic_set(&sp->rcu_try_flip_1,
|
||||
atomic_read(&cp->rcu_try_flip_1));
|
||||
atomic_set(&sp->rcu_try_flip_e1,
|
||||
atomic_read(&cp->rcu_try_flip_e1));
|
||||
atomic_add(atomic_read(&cp->rcu_try_flip_1),
|
||||
&sp->rcu_try_flip_1);
|
||||
atomic_add(atomic_read(&cp->rcu_try_flip_e1),
|
||||
&sp->rcu_try_flip_e1);
|
||||
sp->rcu_try_flip_i1 += cp->rcu_try_flip_i1;
|
||||
sp->rcu_try_flip_ie1 += cp->rcu_try_flip_ie1;
|
||||
sp->rcu_try_flip_g1 += cp->rcu_try_flip_g1;
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@
|
|||
#include <linux/moduleparam.h>
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/freezer.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/delay.h>
|
||||
|
|
@ -108,7 +109,6 @@ struct rcu_torture {
|
|||
int rtort_mbtest;
|
||||
};
|
||||
|
||||
static int fullstop = 0; /* stop generating callbacks at test end. */
|
||||
static LIST_HEAD(rcu_torture_freelist);
|
||||
static struct rcu_torture *rcu_torture_current = NULL;
|
||||
static long rcu_torture_current_version = 0;
|
||||
|
|
@ -136,6 +136,46 @@ static int stutter_pause_test = 0;
|
|||
#endif
|
||||
int rcutorture_runnable = RCUTORTURE_RUNNABLE_INIT;
|
||||
|
||||
/* Mediate rmmod and system shutdown. Concurrent rmmod & shutdown illegal! */
|
||||
|
||||
#define FULLSTOP_DONTSTOP 0 /* Normal operation. */
|
||||
#define FULLSTOP_SHUTDOWN 1 /* System shutdown with rcutorture running. */
|
||||
#define FULLSTOP_RMMOD 2 /* Normal rmmod of rcutorture. */
|
||||
static int fullstop = FULLSTOP_RMMOD;
|
||||
DEFINE_MUTEX(fullstop_mutex); /* Protect fullstop transitions and spawning */
|
||||
/* of kthreads. */
|
||||
|
||||
/*
|
||||
* Detect and respond to a system shutdown.
|
||||
*/
|
||||
static int
|
||||
rcutorture_shutdown_notify(struct notifier_block *unused1,
|
||||
unsigned long unused2, void *unused3)
|
||||
{
|
||||
mutex_lock(&fullstop_mutex);
|
||||
if (fullstop == FULLSTOP_DONTSTOP)
|
||||
fullstop = FULLSTOP_SHUTDOWN;
|
||||
else
|
||||
printk(KERN_WARNING /* but going down anyway, so... */
|
||||
"Concurrent 'rmmod rcutorture' and shutdown illegal!\n");
|
||||
mutex_unlock(&fullstop_mutex);
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Absorb kthreads into a kernel function that won't return, so that
|
||||
* they won't ever access module text or data again.
|
||||
*/
|
||||
static void rcutorture_shutdown_absorb(char *title)
|
||||
{
|
||||
if (ACCESS_ONCE(fullstop) == FULLSTOP_SHUTDOWN) {
|
||||
printk(KERN_NOTICE
|
||||
"rcutorture thread %s parking due to system shutdown\n",
|
||||
title);
|
||||
schedule_timeout_uninterruptible(MAX_SCHEDULE_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate an element from the rcu_tortures pool.
|
||||
*/
|
||||
|
|
@ -197,13 +237,15 @@ rcu_random(struct rcu_random_state *rrsp)
|
|||
}
|
||||
|
||||
static void
|
||||
rcu_stutter_wait(void)
|
||||
rcu_stutter_wait(char *title)
|
||||
{
|
||||
while (stutter_pause_test || !rcutorture_runnable)
|
||||
while (stutter_pause_test || !rcutorture_runnable) {
|
||||
if (rcutorture_runnable)
|
||||
schedule_timeout_interruptible(1);
|
||||
else
|
||||
schedule_timeout_interruptible(round_jiffies_relative(HZ));
|
||||
rcutorture_shutdown_absorb(title);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -264,7 +306,7 @@ rcu_torture_cb(struct rcu_head *p)
|
|||
int i;
|
||||
struct rcu_torture *rp = container_of(p, struct rcu_torture, rtort_rcu);
|
||||
|
||||
if (fullstop) {
|
||||
if (fullstop != FULLSTOP_DONTSTOP) {
|
||||
/* Test is ending, just drop callbacks on the floor. */
|
||||
/* The next initialization will pick up the pieces. */
|
||||
return;
|
||||
|
|
@ -596,9 +638,10 @@ rcu_torture_writer(void *arg)
|
|||
}
|
||||
rcu_torture_current_version++;
|
||||
oldbatch = cur_ops->completed();
|
||||
rcu_stutter_wait();
|
||||
} while (!kthread_should_stop() && !fullstop);
|
||||
rcu_stutter_wait("rcu_torture_writer");
|
||||
} while (!kthread_should_stop() && fullstop == FULLSTOP_DONTSTOP);
|
||||
VERBOSE_PRINTK_STRING("rcu_torture_writer task stopping");
|
||||
rcutorture_shutdown_absorb("rcu_torture_writer");
|
||||
while (!kthread_should_stop())
|
||||
schedule_timeout_uninterruptible(1);
|
||||
return 0;
|
||||
|
|
@ -620,10 +663,11 @@ rcu_torture_fakewriter(void *arg)
|
|||
schedule_timeout_uninterruptible(1 + rcu_random(&rand)%10);
|
||||
udelay(rcu_random(&rand) & 0x3ff);
|
||||
cur_ops->sync();
|
||||
rcu_stutter_wait();
|
||||
} while (!kthread_should_stop() && !fullstop);
|
||||
rcu_stutter_wait("rcu_torture_fakewriter");
|
||||
} while (!kthread_should_stop() && fullstop == FULLSTOP_DONTSTOP);
|
||||
|
||||
VERBOSE_PRINTK_STRING("rcu_torture_fakewriter task stopping");
|
||||
rcutorture_shutdown_absorb("rcu_torture_fakewriter");
|
||||
while (!kthread_should_stop())
|
||||
schedule_timeout_uninterruptible(1);
|
||||
return 0;
|
||||
|
|
@ -729,9 +773,10 @@ rcu_torture_reader(void *arg)
|
|||
preempt_enable();
|
||||
cur_ops->readunlock(idx);
|
||||
schedule();
|
||||
rcu_stutter_wait();
|
||||
} while (!kthread_should_stop() && !fullstop);
|
||||
rcu_stutter_wait("rcu_torture_reader");
|
||||
} while (!kthread_should_stop() && fullstop == FULLSTOP_DONTSTOP);
|
||||
VERBOSE_PRINTK_STRING("rcu_torture_reader task stopping");
|
||||
rcutorture_shutdown_absorb("rcu_torture_reader");
|
||||
if (irqreader && cur_ops->irqcapable)
|
||||
del_timer_sync(&t);
|
||||
while (!kthread_should_stop())
|
||||
|
|
@ -831,6 +876,7 @@ rcu_torture_stats(void *arg)
|
|||
do {
|
||||
schedule_timeout_interruptible(stat_interval * HZ);
|
||||
rcu_torture_stats_print();
|
||||
rcutorture_shutdown_absorb("rcu_torture_stats");
|
||||
} while (!kthread_should_stop());
|
||||
VERBOSE_PRINTK_STRING("rcu_torture_stats task stopping");
|
||||
return 0;
|
||||
|
|
@ -899,6 +945,7 @@ rcu_torture_shuffle(void *arg)
|
|||
do {
|
||||
schedule_timeout_interruptible(shuffle_interval * HZ);
|
||||
rcu_torture_shuffle_tasks();
|
||||
rcutorture_shutdown_absorb("rcu_torture_shuffle");
|
||||
} while (!kthread_should_stop());
|
||||
VERBOSE_PRINTK_STRING("rcu_torture_shuffle task stopping");
|
||||
return 0;
|
||||
|
|
@ -917,6 +964,7 @@ rcu_torture_stutter(void *arg)
|
|||
if (!kthread_should_stop())
|
||||
schedule_timeout_interruptible(stutter * HZ);
|
||||
stutter_pause_test = 0;
|
||||
rcutorture_shutdown_absorb("rcu_torture_stutter");
|
||||
} while (!kthread_should_stop());
|
||||
VERBOSE_PRINTK_STRING("rcu_torture_stutter task stopping");
|
||||
return 0;
|
||||
|
|
@ -934,12 +982,28 @@ rcu_torture_print_module_parms(char *tag)
|
|||
stutter, irqreader);
|
||||
}
|
||||
|
||||
static struct notifier_block rcutorture_nb = {
|
||||
.notifier_call = rcutorture_shutdown_notify,
|
||||
};
|
||||
|
||||
static void
|
||||
rcu_torture_cleanup(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
fullstop = 1;
|
||||
mutex_lock(&fullstop_mutex);
|
||||
if (fullstop == FULLSTOP_SHUTDOWN) {
|
||||
printk(KERN_WARNING /* but going down anyway, so... */
|
||||
"Concurrent 'rmmod rcutorture' and shutdown illegal!\n");
|
||||
mutex_unlock(&fullstop_mutex);
|
||||
schedule_timeout_uninterruptible(10);
|
||||
if (cur_ops->cb_barrier != NULL)
|
||||
cur_ops->cb_barrier();
|
||||
return;
|
||||
}
|
||||
fullstop = FULLSTOP_RMMOD;
|
||||
mutex_unlock(&fullstop_mutex);
|
||||
unregister_reboot_notifier(&rcutorture_nb);
|
||||
if (stutter_task) {
|
||||
VERBOSE_PRINTK_STRING("Stopping rcu_torture_stutter task");
|
||||
kthread_stop(stutter_task);
|
||||
|
|
@ -1015,6 +1079,8 @@ rcu_torture_init(void)
|
|||
{ &rcu_ops, &rcu_sync_ops, &rcu_bh_ops, &rcu_bh_sync_ops,
|
||||
&srcu_ops, &sched_ops, &sched_ops_sync, };
|
||||
|
||||
mutex_lock(&fullstop_mutex);
|
||||
|
||||
/* Process args and tell the world that the torturer is on the job. */
|
||||
for (i = 0; i < ARRAY_SIZE(torture_ops); i++) {
|
||||
cur_ops = torture_ops[i];
|
||||
|
|
@ -1024,6 +1090,7 @@ rcu_torture_init(void)
|
|||
if (i == ARRAY_SIZE(torture_ops)) {
|
||||
printk(KERN_ALERT "rcutorture: invalid torture type: \"%s\"\n",
|
||||
torture_type);
|
||||
mutex_unlock(&fullstop_mutex);
|
||||
return (-EINVAL);
|
||||
}
|
||||
if (cur_ops->init)
|
||||
|
|
@ -1034,7 +1101,7 @@ rcu_torture_init(void)
|
|||
else
|
||||
nrealreaders = 2 * num_online_cpus();
|
||||
rcu_torture_print_module_parms("Start of test");
|
||||
fullstop = 0;
|
||||
fullstop = FULLSTOP_DONTSTOP;
|
||||
|
||||
/* Set up the freelist. */
|
||||
|
||||
|
|
@ -1146,9 +1213,12 @@ rcu_torture_init(void)
|
|||
goto unwind;
|
||||
}
|
||||
}
|
||||
register_reboot_notifier(&rcutorture_nb);
|
||||
mutex_unlock(&fullstop_mutex);
|
||||
return 0;
|
||||
|
||||
unwind:
|
||||
mutex_unlock(&fullstop_mutex);
|
||||
rcu_torture_cleanup();
|
||||
return firsterr;
|
||||
}
|
||||
|
|
|
|||
1532
kernel/rcutree.c
Normal file
1532
kernel/rcutree.c
Normal file
File diff suppressed because it is too large
Load diff
271
kernel/rcutree_trace.c
Normal file
271
kernel/rcutree_trace.c
Normal file
|
|
@ -0,0 +1,271 @@
|
|||
/*
|
||||
* Read-Copy Update tracing for classic implementation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* Copyright IBM Corporation, 2008
|
||||
*
|
||||
* Papers: http://www.rdrop.com/users/paulmck/RCU
|
||||
*
|
||||
* For detailed explanation of Read-Copy Update mechanism see -
|
||||
* Documentation/RCU
|
||||
*
|
||||
*/
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/sched.h>
|
||||
#include <asm/atomic.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/seq_file.h>
|
||||
|
||||
static void print_one_rcu_data(struct seq_file *m, struct rcu_data *rdp)
|
||||
{
|
||||
if (!rdp->beenonline)
|
||||
return;
|
||||
seq_printf(m, "%3d%cc=%ld g=%ld pq=%d pqc=%ld qp=%d rpfq=%ld rp=%x",
|
||||
rdp->cpu,
|
||||
cpu_is_offline(rdp->cpu) ? '!' : ' ',
|
||||
rdp->completed, rdp->gpnum,
|
||||
rdp->passed_quiesc, rdp->passed_quiesc_completed,
|
||||
rdp->qs_pending,
|
||||
rdp->n_rcu_pending_force_qs - rdp->n_rcu_pending,
|
||||
(int)(rdp->n_rcu_pending & 0xffff));
|
||||
#ifdef CONFIG_NO_HZ
|
||||
seq_printf(m, " dt=%d/%d dn=%d df=%lu",
|
||||
rdp->dynticks->dynticks,
|
||||
rdp->dynticks->dynticks_nesting,
|
||||
rdp->dynticks->dynticks_nmi,
|
||||
rdp->dynticks_fqs);
|
||||
#endif /* #ifdef CONFIG_NO_HZ */
|
||||
seq_printf(m, " of=%lu ri=%lu", rdp->offline_fqs, rdp->resched_ipi);
|
||||
seq_printf(m, " ql=%ld b=%ld\n", rdp->qlen, rdp->blimit);
|
||||
}
|
||||
|
||||
#define PRINT_RCU_DATA(name, func, m) \
|
||||
do { \
|
||||
int _p_r_d_i; \
|
||||
\
|
||||
for_each_possible_cpu(_p_r_d_i) \
|
||||
func(m, &per_cpu(name, _p_r_d_i)); \
|
||||
} while (0)
|
||||
|
||||
static int show_rcudata(struct seq_file *m, void *unused)
|
||||
{
|
||||
seq_puts(m, "rcu:\n");
|
||||
PRINT_RCU_DATA(rcu_data, print_one_rcu_data, m);
|
||||
seq_puts(m, "rcu_bh:\n");
|
||||
PRINT_RCU_DATA(rcu_bh_data, print_one_rcu_data, m);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rcudata_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, show_rcudata, NULL);
|
||||
}
|
||||
|
||||
static struct file_operations rcudata_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = rcudata_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static void print_one_rcu_data_csv(struct seq_file *m, struct rcu_data *rdp)
|
||||
{
|
||||
if (!rdp->beenonline)
|
||||
return;
|
||||
seq_printf(m, "%d,%s,%ld,%ld,%d,%ld,%d,%ld,%ld",
|
||||
rdp->cpu,
|
||||
cpu_is_offline(rdp->cpu) ? "\"Y\"" : "\"N\"",
|
||||
rdp->completed, rdp->gpnum,
|
||||
rdp->passed_quiesc, rdp->passed_quiesc_completed,
|
||||
rdp->qs_pending,
|
||||
rdp->n_rcu_pending_force_qs - rdp->n_rcu_pending,
|
||||
rdp->n_rcu_pending);
|
||||
#ifdef CONFIG_NO_HZ
|
||||
seq_printf(m, ",%d,%d,%d,%lu",
|
||||
rdp->dynticks->dynticks,
|
||||
rdp->dynticks->dynticks_nesting,
|
||||
rdp->dynticks->dynticks_nmi,
|
||||
rdp->dynticks_fqs);
|
||||
#endif /* #ifdef CONFIG_NO_HZ */
|
||||
seq_printf(m, ",%lu,%lu", rdp->offline_fqs, rdp->resched_ipi);
|
||||
seq_printf(m, ",%ld,%ld\n", rdp->qlen, rdp->blimit);
|
||||
}
|
||||
|
||||
static int show_rcudata_csv(struct seq_file *m, void *unused)
|
||||
{
|
||||
seq_puts(m, "\"CPU\",\"Online?\",\"c\",\"g\",\"pq\",\"pqc\",\"pq\",\"rpfq\",\"rp\",");
|
||||
#ifdef CONFIG_NO_HZ
|
||||
seq_puts(m, "\"dt\",\"dt nesting\",\"dn\",\"df\",");
|
||||
#endif /* #ifdef CONFIG_NO_HZ */
|
||||
seq_puts(m, "\"of\",\"ri\",\"ql\",\"b\"\n");
|
||||
seq_puts(m, "\"rcu:\"\n");
|
||||
PRINT_RCU_DATA(rcu_data, print_one_rcu_data_csv, m);
|
||||
seq_puts(m, "\"rcu_bh:\"\n");
|
||||
PRINT_RCU_DATA(rcu_bh_data, print_one_rcu_data_csv, m);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rcudata_csv_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, show_rcudata_csv, NULL);
|
||||
}
|
||||
|
||||
static struct file_operations rcudata_csv_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = rcudata_csv_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static void print_one_rcu_state(struct seq_file *m, struct rcu_state *rsp)
|
||||
{
|
||||
int level = 0;
|
||||
struct rcu_node *rnp;
|
||||
|
||||
seq_printf(m, "c=%ld g=%ld s=%d jfq=%ld j=%x "
|
||||
"nfqs=%lu/nfqsng=%lu(%lu) fqlh=%lu\n",
|
||||
rsp->completed, rsp->gpnum, rsp->signaled,
|
||||
(long)(rsp->jiffies_force_qs - jiffies),
|
||||
(int)(jiffies & 0xffff),
|
||||
rsp->n_force_qs, rsp->n_force_qs_ngp,
|
||||
rsp->n_force_qs - rsp->n_force_qs_ngp,
|
||||
rsp->n_force_qs_lh);
|
||||
for (rnp = &rsp->node[0]; rnp - &rsp->node[0] < NUM_RCU_NODES; rnp++) {
|
||||
if (rnp->level != level) {
|
||||
seq_puts(m, "\n");
|
||||
level = rnp->level;
|
||||
}
|
||||
seq_printf(m, "%lx/%lx %d:%d ^%d ",
|
||||
rnp->qsmask, rnp->qsmaskinit,
|
||||
rnp->grplo, rnp->grphi, rnp->grpnum);
|
||||
}
|
||||
seq_puts(m, "\n");
|
||||
}
|
||||
|
||||
static int show_rcuhier(struct seq_file *m, void *unused)
|
||||
{
|
||||
seq_puts(m, "rcu:\n");
|
||||
print_one_rcu_state(m, &rcu_state);
|
||||
seq_puts(m, "rcu_bh:\n");
|
||||
print_one_rcu_state(m, &rcu_bh_state);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rcuhier_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, show_rcuhier, NULL);
|
||||
}
|
||||
|
||||
static struct file_operations rcuhier_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = rcuhier_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static int show_rcugp(struct seq_file *m, void *unused)
|
||||
{
|
||||
seq_printf(m, "rcu: completed=%ld gpnum=%ld\n",
|
||||
rcu_state.completed, rcu_state.gpnum);
|
||||
seq_printf(m, "rcu_bh: completed=%ld gpnum=%ld\n",
|
||||
rcu_bh_state.completed, rcu_bh_state.gpnum);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rcugp_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, show_rcugp, NULL);
|
||||
}
|
||||
|
||||
static struct file_operations rcugp_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = rcugp_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static struct dentry *rcudir, *datadir, *datadir_csv, *hierdir, *gpdir;
|
||||
static int __init rcuclassic_trace_init(void)
|
||||
{
|
||||
rcudir = debugfs_create_dir("rcu", NULL);
|
||||
if (!rcudir)
|
||||
goto out;
|
||||
|
||||
datadir = debugfs_create_file("rcudata", 0444, rcudir,
|
||||
NULL, &rcudata_fops);
|
||||
if (!datadir)
|
||||
goto free_out;
|
||||
|
||||
datadir_csv = debugfs_create_file("rcudata.csv", 0444, rcudir,
|
||||
NULL, &rcudata_csv_fops);
|
||||
if (!datadir_csv)
|
||||
goto free_out;
|
||||
|
||||
gpdir = debugfs_create_file("rcugp", 0444, rcudir, NULL, &rcugp_fops);
|
||||
if (!gpdir)
|
||||
goto free_out;
|
||||
|
||||
hierdir = debugfs_create_file("rcuhier", 0444, rcudir,
|
||||
NULL, &rcuhier_fops);
|
||||
if (!hierdir)
|
||||
goto free_out;
|
||||
return 0;
|
||||
free_out:
|
||||
if (datadir)
|
||||
debugfs_remove(datadir);
|
||||
if (datadir_csv)
|
||||
debugfs_remove(datadir_csv);
|
||||
if (gpdir)
|
||||
debugfs_remove(gpdir);
|
||||
debugfs_remove(rcudir);
|
||||
out:
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void __exit rcuclassic_trace_cleanup(void)
|
||||
{
|
||||
debugfs_remove(datadir);
|
||||
debugfs_remove(datadir_csv);
|
||||
debugfs_remove(gpdir);
|
||||
debugfs_remove(hierdir);
|
||||
debugfs_remove(rcudir);
|
||||
}
|
||||
|
||||
|
||||
module_init(rcuclassic_trace_init);
|
||||
module_exit(rcuclassic_trace_cleanup);
|
||||
|
||||
MODULE_AUTHOR("Paul E. McKenney");
|
||||
MODULE_DESCRIPTION("Read-Copy Update tracing for hierarchical implementation");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -15,10 +15,11 @@
|
|||
#include <linux/uaccess.h>
|
||||
#include <linux/mm.h>
|
||||
|
||||
void res_counter_init(struct res_counter *counter)
|
||||
void res_counter_init(struct res_counter *counter, struct res_counter *parent)
|
||||
{
|
||||
spin_lock_init(&counter->lock);
|
||||
counter->limit = (unsigned long long)LLONG_MAX;
|
||||
counter->parent = parent;
|
||||
}
|
||||
|
||||
int res_counter_charge_locked(struct res_counter *counter, unsigned long val)
|
||||
|
|
@ -34,14 +35,34 @@ int res_counter_charge_locked(struct res_counter *counter, unsigned long val)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int res_counter_charge(struct res_counter *counter, unsigned long val)
|
||||
int res_counter_charge(struct res_counter *counter, unsigned long val,
|
||||
struct res_counter **limit_fail_at)
|
||||
{
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
struct res_counter *c, *u;
|
||||
|
||||
spin_lock_irqsave(&counter->lock, flags);
|
||||
ret = res_counter_charge_locked(counter, val);
|
||||
spin_unlock_irqrestore(&counter->lock, flags);
|
||||
*limit_fail_at = NULL;
|
||||
local_irq_save(flags);
|
||||
for (c = counter; c != NULL; c = c->parent) {
|
||||
spin_lock(&c->lock);
|
||||
ret = res_counter_charge_locked(c, val);
|
||||
spin_unlock(&c->lock);
|
||||
if (ret < 0) {
|
||||
*limit_fail_at = c;
|
||||
goto undo;
|
||||
}
|
||||
}
|
||||
ret = 0;
|
||||
goto done;
|
||||
undo:
|
||||
for (u = counter; u != c; u = u->parent) {
|
||||
spin_lock(&u->lock);
|
||||
res_counter_uncharge_locked(u, val);
|
||||
spin_unlock(&u->lock);
|
||||
}
|
||||
done:
|
||||
local_irq_restore(flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -56,10 +77,15 @@ void res_counter_uncharge_locked(struct res_counter *counter, unsigned long val)
|
|||
void res_counter_uncharge(struct res_counter *counter, unsigned long val)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct res_counter *c;
|
||||
|
||||
spin_lock_irqsave(&counter->lock, flags);
|
||||
res_counter_uncharge_locked(counter, val);
|
||||
spin_unlock_irqrestore(&counter->lock, flags);
|
||||
local_irq_save(flags);
|
||||
for (c = counter; c != NULL; c = c->parent) {
|
||||
spin_lock(&c->lock);
|
||||
res_counter_uncharge_locked(c, val);
|
||||
spin_unlock(&c->lock);
|
||||
}
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -620,10 +620,11 @@ resource_size_t resource_alignment(struct resource *res)
|
|||
* @start: resource start address
|
||||
* @n: resource region size
|
||||
* @name: reserving caller's ID string
|
||||
* @flags: IO resource flags
|
||||
*/
|
||||
struct resource * __request_region(struct resource *parent,
|
||||
resource_size_t start, resource_size_t n,
|
||||
const char *name)
|
||||
const char *name, int flags)
|
||||
{
|
||||
struct resource *res = kzalloc(sizeof(*res), GFP_KERNEL);
|
||||
|
||||
|
|
@ -634,6 +635,7 @@ struct resource * __request_region(struct resource *parent,
|
|||
res->start = start;
|
||||
res->end = start + n - 1;
|
||||
res->flags = IORESOURCE_BUSY;
|
||||
res->flags |= flags;
|
||||
|
||||
write_lock(&resource_lock);
|
||||
|
||||
|
|
@ -679,7 +681,7 @@ int __check_region(struct resource *parent, resource_size_t start,
|
|||
{
|
||||
struct resource * res;
|
||||
|
||||
res = __request_region(parent, start, n, "check-region");
|
||||
res = __request_region(parent, start, n, "check-region", 0);
|
||||
if (!res)
|
||||
return -EBUSY;
|
||||
|
||||
|
|
@ -776,7 +778,7 @@ struct resource * __devm_request_region(struct device *dev,
|
|||
dr->start = start;
|
||||
dr->n = n;
|
||||
|
||||
res = __request_region(parent, start, n, name);
|
||||
res = __request_region(parent, start, n, name, 0);
|
||||
if (res)
|
||||
devres_add(dev, dr);
|
||||
else
|
||||
|
|
@ -853,6 +855,15 @@ int iomem_map_sanity_check(resource_size_t addr, unsigned long size)
|
|||
if (PFN_DOWN(p->start) <= PFN_DOWN(addr) &&
|
||||
PFN_DOWN(p->end) >= PFN_DOWN(addr + size - 1))
|
||||
continue;
|
||||
/*
|
||||
* if a resource is "BUSY", it's not a hardware resource
|
||||
* but a driver mapping of such a resource; we don't want
|
||||
* to warn for those; some drivers legitimately map only
|
||||
* partial hardware resources. (example: vesafb)
|
||||
*/
|
||||
if (p->flags & IORESOURCE_BUSY)
|
||||
continue;
|
||||
|
||||
printk(KERN_WARNING "resource map sanity check conflict: "
|
||||
"0x%llx 0x%llx 0x%llx 0x%llx %s\n",
|
||||
(unsigned long long)addr,
|
||||
|
|
@ -867,3 +878,57 @@ int iomem_map_sanity_check(resource_size_t addr, unsigned long size)
|
|||
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_STRICT_DEVMEM
|
||||
static int strict_iomem_checks = 1;
|
||||
#else
|
||||
static int strict_iomem_checks;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* check if an address is reserved in the iomem resource tree
|
||||
* returns 1 if reserved, 0 if not reserved.
|
||||
*/
|
||||
int iomem_is_exclusive(u64 addr)
|
||||
{
|
||||
struct resource *p = &iomem_resource;
|
||||
int err = 0;
|
||||
loff_t l;
|
||||
int size = PAGE_SIZE;
|
||||
|
||||
if (!strict_iomem_checks)
|
||||
return 0;
|
||||
|
||||
addr = addr & PAGE_MASK;
|
||||
|
||||
read_lock(&resource_lock);
|
||||
for (p = p->child; p ; p = r_next(NULL, p, &l)) {
|
||||
/*
|
||||
* We can probably skip the resources without
|
||||
* IORESOURCE_IO attribute?
|
||||
*/
|
||||
if (p->start >= addr + size)
|
||||
break;
|
||||
if (p->end < addr)
|
||||
continue;
|
||||
if (p->flags & IORESOURCE_BUSY &&
|
||||
p->flags & IORESOURCE_EXCLUSIVE) {
|
||||
err = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
read_unlock(&resource_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __init strict_iomem(char *str)
|
||||
{
|
||||
if (strstr(str, "relaxed"))
|
||||
strict_iomem_checks = 0;
|
||||
if (strstr(str, "strict"))
|
||||
strict_iomem_checks = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
__setup("iomem=", strict_iomem);
|
||||
|
|
|
|||
1533
kernel/sched.c
1533
kernel/sched.c
File diff suppressed because it is too large
Load diff
|
|
@ -124,7 +124,7 @@ static u64 __update_sched_clock(struct sched_clock_data *scd, u64 now)
|
|||
|
||||
clock = scd->tick_gtod + delta;
|
||||
min_clock = wrap_max(scd->tick_gtod, scd->clock);
|
||||
max_clock = scd->tick_gtod + TICK_NSEC;
|
||||
max_clock = wrap_max(scd->clock, scd->tick_gtod + TICK_NSEC);
|
||||
|
||||
clock = wrap_max(clock, min_clock);
|
||||
clock = wrap_min(clock, max_clock);
|
||||
|
|
@ -227,6 +227,9 @@ EXPORT_SYMBOL_GPL(sched_clock_idle_sleep_event);
|
|||
*/
|
||||
void sched_clock_idle_wakeup_event(u64 delta_ns)
|
||||
{
|
||||
if (timekeeping_suspended)
|
||||
return;
|
||||
|
||||
sched_clock_tick();
|
||||
touch_softlockup_watchdog();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,24 +67,21 @@ static int convert_prio(int prio)
|
|||
* Returns: (int)bool - CPUs were found
|
||||
*/
|
||||
int cpupri_find(struct cpupri *cp, struct task_struct *p,
|
||||
cpumask_t *lowest_mask)
|
||||
struct cpumask *lowest_mask)
|
||||
{
|
||||
int idx = 0;
|
||||
int task_pri = convert_prio(p->prio);
|
||||
|
||||
for_each_cpupri_active(cp->pri_active, idx) {
|
||||
struct cpupri_vec *vec = &cp->pri_to_cpu[idx];
|
||||
cpumask_t mask;
|
||||
|
||||
if (idx >= task_pri)
|
||||
break;
|
||||
|
||||
cpus_and(mask, p->cpus_allowed, vec->mask);
|
||||
|
||||
if (cpus_empty(mask))
|
||||
if (cpumask_any_and(&p->cpus_allowed, vec->mask) >= nr_cpu_ids)
|
||||
continue;
|
||||
|
||||
*lowest_mask = mask;
|
||||
cpumask_and(lowest_mask, &p->cpus_allowed, vec->mask);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
@ -126,7 +123,7 @@ void cpupri_set(struct cpupri *cp, int cpu, int newpri)
|
|||
vec->count--;
|
||||
if (!vec->count)
|
||||
clear_bit(oldpri, cp->pri_active);
|
||||
cpu_clear(cpu, vec->mask);
|
||||
cpumask_clear_cpu(cpu, vec->mask);
|
||||
|
||||
spin_unlock_irqrestore(&vec->lock, flags);
|
||||
}
|
||||
|
|
@ -136,7 +133,7 @@ void cpupri_set(struct cpupri *cp, int cpu, int newpri)
|
|||
|
||||
spin_lock_irqsave(&vec->lock, flags);
|
||||
|
||||
cpu_set(cpu, vec->mask);
|
||||
cpumask_set_cpu(cpu, vec->mask);
|
||||
vec->count++;
|
||||
if (vec->count == 1)
|
||||
set_bit(newpri, cp->pri_active);
|
||||
|
|
@ -150,10 +147,11 @@ void cpupri_set(struct cpupri *cp, int cpu, int newpri)
|
|||
/**
|
||||
* cpupri_init - initialize the cpupri structure
|
||||
* @cp: The cpupri context
|
||||
* @bootmem: true if allocations need to use bootmem
|
||||
*
|
||||
* Returns: (void)
|
||||
* Returns: -ENOMEM if memory fails.
|
||||
*/
|
||||
void cpupri_init(struct cpupri *cp)
|
||||
int __init_refok cpupri_init(struct cpupri *cp, bool bootmem)
|
||||
{
|
||||
int i;
|
||||
|
||||
|
|
@ -164,11 +162,30 @@ void cpupri_init(struct cpupri *cp)
|
|||
|
||||
spin_lock_init(&vec->lock);
|
||||
vec->count = 0;
|
||||
cpus_clear(vec->mask);
|
||||
if (bootmem)
|
||||
alloc_bootmem_cpumask_var(&vec->mask);
|
||||
else if (!alloc_cpumask_var(&vec->mask, GFP_KERNEL))
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
for_each_possible_cpu(i)
|
||||
cp->cpu_to_pri[i] = CPUPRI_INVALID;
|
||||
return 0;
|
||||
|
||||
cleanup:
|
||||
for (i--; i >= 0; i--)
|
||||
free_cpumask_var(cp->pri_to_cpu[i].mask);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/**
|
||||
* cpupri_cleanup - clean up the cpupri structure
|
||||
* @cp: The cpupri context
|
||||
*/
|
||||
void cpupri_cleanup(struct cpupri *cp)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < CPUPRI_NR_PRIORITIES; i++)
|
||||
free_cpumask_var(cp->pri_to_cpu[i].mask);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
struct cpupri_vec {
|
||||
spinlock_t lock;
|
||||
int count;
|
||||
cpumask_t mask;
|
||||
cpumask_var_t mask;
|
||||
};
|
||||
|
||||
struct cpupri {
|
||||
|
|
@ -27,7 +27,8 @@ struct cpupri {
|
|||
int cpupri_find(struct cpupri *cp,
|
||||
struct task_struct *p, cpumask_t *lowest_mask);
|
||||
void cpupri_set(struct cpupri *cp, int cpu, int pri);
|
||||
void cpupri_init(struct cpupri *cp);
|
||||
int cpupri_init(struct cpupri *cp, bool bootmem);
|
||||
void cpupri_cleanup(struct cpupri *cp);
|
||||
#else
|
||||
#define cpupri_set(cp, cpu, pri) do { } while (0)
|
||||
#define cpupri_init() do { } while (0)
|
||||
|
|
|
|||
|
|
@ -53,6 +53,40 @@ static unsigned long nsec_low(unsigned long long nsec)
|
|||
|
||||
#define SPLIT_NS(x) nsec_high(x), nsec_low(x)
|
||||
|
||||
#ifdef CONFIG_FAIR_GROUP_SCHED
|
||||
static void print_cfs_group_stats(struct seq_file *m, int cpu,
|
||||
struct task_group *tg)
|
||||
{
|
||||
struct sched_entity *se = tg->se[cpu];
|
||||
if (!se)
|
||||
return;
|
||||
|
||||
#define P(F) \
|
||||
SEQ_printf(m, " .%-30s: %lld\n", #F, (long long)F)
|
||||
#define PN(F) \
|
||||
SEQ_printf(m, " .%-30s: %lld.%06ld\n", #F, SPLIT_NS((long long)F))
|
||||
|
||||
PN(se->exec_start);
|
||||
PN(se->vruntime);
|
||||
PN(se->sum_exec_runtime);
|
||||
#ifdef CONFIG_SCHEDSTATS
|
||||
PN(se->wait_start);
|
||||
PN(se->sleep_start);
|
||||
PN(se->block_start);
|
||||
PN(se->sleep_max);
|
||||
PN(se->block_max);
|
||||
PN(se->exec_max);
|
||||
PN(se->slice_max);
|
||||
PN(se->wait_max);
|
||||
PN(se->wait_sum);
|
||||
P(se->wait_count);
|
||||
#endif
|
||||
P(se->load.weight);
|
||||
#undef PN
|
||||
#undef P
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
print_task(struct seq_file *m, struct rq *rq, struct task_struct *p)
|
||||
{
|
||||
|
|
@ -111,6 +145,19 @@ static void print_rq(struct seq_file *m, struct rq *rq, int rq_cpu)
|
|||
read_unlock_irqrestore(&tasklist_lock, flags);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_CGROUP_SCHED) && \
|
||||
(defined(CONFIG_FAIR_GROUP_SCHED) || defined(CONFIG_RT_GROUP_SCHED))
|
||||
static void task_group_path(struct task_group *tg, char *buf, int buflen)
|
||||
{
|
||||
/* may be NULL if the underlying cgroup isn't fully-created yet */
|
||||
if (!tg->css.cgroup) {
|
||||
buf[0] = '\0';
|
||||
return;
|
||||
}
|
||||
cgroup_path(tg->css.cgroup, buf, buflen);
|
||||
}
|
||||
#endif
|
||||
|
||||
void print_cfs_rq(struct seq_file *m, int cpu, struct cfs_rq *cfs_rq)
|
||||
{
|
||||
s64 MIN_vruntime = -1, min_vruntime, max_vruntime = -1,
|
||||
|
|
@ -120,21 +167,20 @@ void print_cfs_rq(struct seq_file *m, int cpu, struct cfs_rq *cfs_rq)
|
|||
unsigned long flags;
|
||||
|
||||
#if defined(CONFIG_CGROUP_SCHED) && defined(CONFIG_FAIR_GROUP_SCHED)
|
||||
char path[128] = "";
|
||||
struct cgroup *cgroup = NULL;
|
||||
char path[128];
|
||||
struct task_group *tg = cfs_rq->tg;
|
||||
|
||||
if (tg)
|
||||
cgroup = tg->css.cgroup;
|
||||
|
||||
if (cgroup)
|
||||
cgroup_path(cgroup, path, sizeof(path));
|
||||
task_group_path(tg, path, sizeof(path));
|
||||
|
||||
SEQ_printf(m, "\ncfs_rq[%d]:%s\n", cpu, path);
|
||||
#elif defined(CONFIG_USER_SCHED) && defined(CONFIG_FAIR_GROUP_SCHED)
|
||||
{
|
||||
uid_t uid = cfs_rq->tg->uid;
|
||||
SEQ_printf(m, "\ncfs_rq[%d] for UID: %u\n", cpu, uid);
|
||||
}
|
||||
#else
|
||||
SEQ_printf(m, "\ncfs_rq[%d]:\n", cpu);
|
||||
#endif
|
||||
|
||||
SEQ_printf(m, " .%-30s: %Ld.%06ld\n", "exec_clock",
|
||||
SPLIT_NS(cfs_rq->exec_clock));
|
||||
|
||||
|
|
@ -168,21 +214,17 @@ void print_cfs_rq(struct seq_file *m, int cpu, struct cfs_rq *cfs_rq)
|
|||
#ifdef CONFIG_SMP
|
||||
SEQ_printf(m, " .%-30s: %lu\n", "shares", cfs_rq->shares);
|
||||
#endif
|
||||
print_cfs_group_stats(m, cpu, cfs_rq->tg);
|
||||
#endif
|
||||
}
|
||||
|
||||
void print_rt_rq(struct seq_file *m, int cpu, struct rt_rq *rt_rq)
|
||||
{
|
||||
#if defined(CONFIG_CGROUP_SCHED) && defined(CONFIG_RT_GROUP_SCHED)
|
||||
char path[128] = "";
|
||||
struct cgroup *cgroup = NULL;
|
||||
char path[128];
|
||||
struct task_group *tg = rt_rq->tg;
|
||||
|
||||
if (tg)
|
||||
cgroup = tg->css.cgroup;
|
||||
|
||||
if (cgroup)
|
||||
cgroup_path(cgroup, path, sizeof(path));
|
||||
task_group_path(tg, path, sizeof(path));
|
||||
|
||||
SEQ_printf(m, "\nrt_rq[%d]:%s\n", cpu, path);
|
||||
#else
|
||||
|
|
@ -272,7 +314,7 @@ static int sched_debug_show(struct seq_file *m, void *v)
|
|||
u64 now = ktime_to_ns(ktime_get());
|
||||
int cpu;
|
||||
|
||||
SEQ_printf(m, "Sched Debug Version: v0.07, %s %.*s\n",
|
||||
SEQ_printf(m, "Sched Debug Version: v0.08, %s %.*s\n",
|
||||
init_utsname()->release,
|
||||
(int)strcspn(init_utsname()->version, " "),
|
||||
init_utsname()->version);
|
||||
|
|
|
|||
|
|
@ -283,7 +283,7 @@ static void update_min_vruntime(struct cfs_rq *cfs_rq)
|
|||
struct sched_entity,
|
||||
run_node);
|
||||
|
||||
if (vruntime == cfs_rq->min_vruntime)
|
||||
if (!cfs_rq->curr)
|
||||
vruntime = se->vruntime;
|
||||
else
|
||||
vruntime = min_vruntime(vruntime, se->vruntime);
|
||||
|
|
@ -385,20 +385,6 @@ int sched_nr_latency_handler(struct ctl_table *table, int write,
|
|||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* delta *= P[w / rw]
|
||||
*/
|
||||
static inline unsigned long
|
||||
calc_delta_weight(unsigned long delta, struct sched_entity *se)
|
||||
{
|
||||
for_each_sched_entity(se) {
|
||||
delta = calc_delta_mine(delta,
|
||||
se->load.weight, &cfs_rq_of(se)->load);
|
||||
}
|
||||
|
||||
return delta;
|
||||
}
|
||||
|
||||
/*
|
||||
* delta /= w
|
||||
*/
|
||||
|
|
@ -440,12 +426,23 @@ static u64 __sched_period(unsigned long nr_running)
|
|||
*/
|
||||
static u64 sched_slice(struct cfs_rq *cfs_rq, struct sched_entity *se)
|
||||
{
|
||||
unsigned long nr_running = cfs_rq->nr_running;
|
||||
u64 slice = __sched_period(cfs_rq->nr_running + !se->on_rq);
|
||||
|
||||
if (unlikely(!se->on_rq))
|
||||
nr_running++;
|
||||
for_each_sched_entity(se) {
|
||||
struct load_weight *load;
|
||||
|
||||
return calc_delta_weight(__sched_period(nr_running), se);
|
||||
cfs_rq = cfs_rq_of(se);
|
||||
load = &cfs_rq->load;
|
||||
|
||||
if (unlikely(!se->on_rq)) {
|
||||
struct load_weight lw = cfs_rq->load;
|
||||
|
||||
update_load_add(&lw, se->load.weight);
|
||||
load = &lw;
|
||||
}
|
||||
slice = calc_delta_mine(slice, se->load.weight, load);
|
||||
}
|
||||
return slice;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -492,6 +489,8 @@ static void update_curr(struct cfs_rq *cfs_rq)
|
|||
* overflow on 32 bits):
|
||||
*/
|
||||
delta_exec = (unsigned long)(now - curr->exec_start);
|
||||
if (!delta_exec)
|
||||
return;
|
||||
|
||||
__update_curr(cfs_rq, curr, delta_exec);
|
||||
curr->exec_start = now;
|
||||
|
|
@ -681,9 +680,13 @@ place_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int initial)
|
|||
unsigned long thresh = sysctl_sched_latency;
|
||||
|
||||
/*
|
||||
* convert the sleeper threshold into virtual time
|
||||
* Convert the sleeper threshold into virtual time.
|
||||
* SCHED_IDLE is a special sub-class. We care about
|
||||
* fairness only relative to other SCHED_IDLE tasks,
|
||||
* all of which have the same weight.
|
||||
*/
|
||||
if (sched_feat(NORMALIZED_SLEEPER))
|
||||
if (sched_feat(NORMALIZED_SLEEPER) &&
|
||||
task_of(se)->policy != SCHED_IDLE)
|
||||
thresh = calc_delta_fair(thresh, se);
|
||||
|
||||
vruntime -= thresh;
|
||||
|
|
@ -1017,16 +1020,33 @@ static void yield_task_fair(struct rq *rq)
|
|||
* search starts with cpus closest then further out as needed,
|
||||
* so we always favor a closer, idle cpu.
|
||||
* Domains may include CPUs that are not usable for migration,
|
||||
* hence we need to mask them out (cpu_active_map)
|
||||
* hence we need to mask them out (cpu_active_mask)
|
||||
*
|
||||
* Returns the CPU we should wake onto.
|
||||
*/
|
||||
#if defined(ARCH_HAS_SCHED_WAKE_IDLE)
|
||||
static int wake_idle(int cpu, struct task_struct *p)
|
||||
{
|
||||
cpumask_t tmp;
|
||||
struct sched_domain *sd;
|
||||
int i;
|
||||
unsigned int chosen_wakeup_cpu;
|
||||
int this_cpu;
|
||||
|
||||
/*
|
||||
* At POWERSAVINGS_BALANCE_WAKEUP level, if both this_cpu and prev_cpu
|
||||
* are idle and this is not a kernel thread and this task's affinity
|
||||
* allows it to be moved to preferred cpu, then just move!
|
||||
*/
|
||||
|
||||
this_cpu = smp_processor_id();
|
||||
chosen_wakeup_cpu =
|
||||
cpu_rq(this_cpu)->rd->sched_mc_preferred_wakeup_cpu;
|
||||
|
||||
if (sched_mc_power_savings >= POWERSAVINGS_BALANCE_WAKEUP &&
|
||||
idle_cpu(cpu) && idle_cpu(this_cpu) &&
|
||||
p->mm && !(p->flags & PF_KTHREAD) &&
|
||||
cpu_isset(chosen_wakeup_cpu, p->cpus_allowed))
|
||||
return chosen_wakeup_cpu;
|
||||
|
||||
/*
|
||||
* If it is idle, then it is the best cpu to run this task.
|
||||
|
|
@ -1044,10 +1064,9 @@ static int wake_idle(int cpu, struct task_struct *p)
|
|||
if ((sd->flags & SD_WAKE_IDLE)
|
||||
|| ((sd->flags & SD_WAKE_IDLE_FAR)
|
||||
&& !task_hot(p, task_rq(p)->clock, sd))) {
|
||||
cpus_and(tmp, sd->span, p->cpus_allowed);
|
||||
cpus_and(tmp, tmp, cpu_active_map);
|
||||
for_each_cpu_mask_nr(i, tmp) {
|
||||
if (idle_cpu(i)) {
|
||||
for_each_cpu_and(i, sched_domain_span(sd),
|
||||
&p->cpus_allowed) {
|
||||
if (cpu_active(i) && idle_cpu(i)) {
|
||||
if (i != task_cpu(p)) {
|
||||
schedstat_inc(p,
|
||||
se.nr_wakeups_idle);
|
||||
|
|
@ -1240,13 +1259,13 @@ static int select_task_rq_fair(struct task_struct *p, int sync)
|
|||
* this_cpu and prev_cpu are present in:
|
||||
*/
|
||||
for_each_domain(this_cpu, sd) {
|
||||
if (cpu_isset(prev_cpu, sd->span)) {
|
||||
if (cpumask_test_cpu(prev_cpu, sched_domain_span(sd))) {
|
||||
this_sd = sd;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (unlikely(!cpu_isset(this_cpu, p->cpus_allowed)))
|
||||
if (unlikely(!cpumask_test_cpu(this_cpu, &p->cpus_allowed)))
|
||||
goto out;
|
||||
|
||||
/*
|
||||
|
|
@ -1328,14 +1347,18 @@ wakeup_preempt_entity(struct sched_entity *curr, struct sched_entity *se)
|
|||
|
||||
static void set_last_buddy(struct sched_entity *se)
|
||||
{
|
||||
for_each_sched_entity(se)
|
||||
cfs_rq_of(se)->last = se;
|
||||
if (likely(task_of(se)->policy != SCHED_IDLE)) {
|
||||
for_each_sched_entity(se)
|
||||
cfs_rq_of(se)->last = se;
|
||||
}
|
||||
}
|
||||
|
||||
static void set_next_buddy(struct sched_entity *se)
|
||||
{
|
||||
for_each_sched_entity(se)
|
||||
cfs_rq_of(se)->next = se;
|
||||
if (likely(task_of(se)->policy != SCHED_IDLE)) {
|
||||
for_each_sched_entity(se)
|
||||
cfs_rq_of(se)->next = se;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1345,12 +1368,11 @@ static void check_preempt_wakeup(struct rq *rq, struct task_struct *p, int sync)
|
|||
{
|
||||
struct task_struct *curr = rq->curr;
|
||||
struct sched_entity *se = &curr->se, *pse = &p->se;
|
||||
struct cfs_rq *cfs_rq = task_cfs_rq(curr);
|
||||
|
||||
update_curr(cfs_rq);
|
||||
|
||||
if (unlikely(rt_prio(p->prio))) {
|
||||
struct cfs_rq *cfs_rq = task_cfs_rq(curr);
|
||||
|
||||
update_rq_clock(rq);
|
||||
update_curr(cfs_rq);
|
||||
resched_task(curr);
|
||||
return;
|
||||
}
|
||||
|
|
@ -1382,12 +1404,18 @@ static void check_preempt_wakeup(struct rq *rq, struct task_struct *p, int sync)
|
|||
return;
|
||||
|
||||
/*
|
||||
* Batch tasks do not preempt (their preemption is driven by
|
||||
* Batch and idle tasks do not preempt (their preemption is driven by
|
||||
* the tick):
|
||||
*/
|
||||
if (unlikely(p->policy == SCHED_BATCH))
|
||||
if (unlikely(p->policy != SCHED_NORMAL))
|
||||
return;
|
||||
|
||||
/* Idle tasks are by definition preempted by everybody. */
|
||||
if (unlikely(curr->policy == SCHED_IDLE)) {
|
||||
resched_task(curr);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!sched_feat(WAKEUP_PREEMPT))
|
||||
return;
|
||||
|
||||
|
|
@ -1606,8 +1634,6 @@ static void task_tick_fair(struct rq *rq, struct task_struct *curr, int queued)
|
|||
}
|
||||
}
|
||||
|
||||
#define swap(a, b) do { typeof(a) tmp = (a); (a) = (b); (b) = tmp; } while (0)
|
||||
|
||||
/*
|
||||
* Share the fairness runtime between parent and child, thus the
|
||||
* total amount of pressure for CPU stays equal - new tasks
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ static inline void rt_set_overload(struct rq *rq)
|
|||
if (!rq->online)
|
||||
return;
|
||||
|
||||
cpu_set(rq->cpu, rq->rd->rto_mask);
|
||||
cpumask_set_cpu(rq->cpu, rq->rd->rto_mask);
|
||||
/*
|
||||
* Make sure the mask is visible before we set
|
||||
* the overload count. That is checked to determine
|
||||
|
|
@ -34,7 +34,7 @@ static inline void rt_clear_overload(struct rq *rq)
|
|||
|
||||
/* the order here really doesn't matter */
|
||||
atomic_dec(&rq->rd->rto_count);
|
||||
cpu_clear(rq->cpu, rq->rd->rto_mask);
|
||||
cpumask_clear_cpu(rq->cpu, rq->rd->rto_mask);
|
||||
}
|
||||
|
||||
static void update_rt_migration(struct rq *rq)
|
||||
|
|
@ -77,7 +77,7 @@ static inline u64 sched_rt_period(struct rt_rq *rt_rq)
|
|||
}
|
||||
|
||||
#define for_each_leaf_rt_rq(rt_rq, rq) \
|
||||
list_for_each_entry(rt_rq, &rq->leaf_rt_rq_list, leaf_rt_rq_list)
|
||||
list_for_each_entry_rcu(rt_rq, &rq->leaf_rt_rq_list, leaf_rt_rq_list)
|
||||
|
||||
static inline struct rq *rq_of_rt_rq(struct rt_rq *rt_rq)
|
||||
{
|
||||
|
|
@ -139,14 +139,14 @@ static int rt_se_boosted(struct sched_rt_entity *rt_se)
|
|||
}
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
static inline cpumask_t sched_rt_period_mask(void)
|
||||
static inline const struct cpumask *sched_rt_period_mask(void)
|
||||
{
|
||||
return cpu_rq(smp_processor_id())->rd->span;
|
||||
}
|
||||
#else
|
||||
static inline cpumask_t sched_rt_period_mask(void)
|
||||
static inline const struct cpumask *sched_rt_period_mask(void)
|
||||
{
|
||||
return cpu_online_map;
|
||||
return cpu_online_mask;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
@ -212,9 +212,9 @@ static inline int rt_rq_throttled(struct rt_rq *rt_rq)
|
|||
return rt_rq->rt_throttled;
|
||||
}
|
||||
|
||||
static inline cpumask_t sched_rt_period_mask(void)
|
||||
static inline const struct cpumask *sched_rt_period_mask(void)
|
||||
{
|
||||
return cpu_online_map;
|
||||
return cpu_online_mask;
|
||||
}
|
||||
|
||||
static inline
|
||||
|
|
@ -241,11 +241,11 @@ static int do_balance_runtime(struct rt_rq *rt_rq)
|
|||
int i, weight, more = 0;
|
||||
u64 rt_period;
|
||||
|
||||
weight = cpus_weight(rd->span);
|
||||
weight = cpumask_weight(rd->span);
|
||||
|
||||
spin_lock(&rt_b->rt_runtime_lock);
|
||||
rt_period = ktime_to_ns(rt_b->rt_period);
|
||||
for_each_cpu_mask_nr(i, rd->span) {
|
||||
for_each_cpu(i, rd->span) {
|
||||
struct rt_rq *iter = sched_rt_period_rt_rq(rt_b, i);
|
||||
s64 diff;
|
||||
|
||||
|
|
@ -324,7 +324,7 @@ static void __disable_runtime(struct rq *rq)
|
|||
/*
|
||||
* Greedy reclaim, take back as much as we can.
|
||||
*/
|
||||
for_each_cpu_mask(i, rd->span) {
|
||||
for_each_cpu(i, rd->span) {
|
||||
struct rt_rq *iter = sched_rt_period_rt_rq(rt_b, i);
|
||||
s64 diff;
|
||||
|
||||
|
|
@ -429,13 +429,13 @@ static inline int balance_runtime(struct rt_rq *rt_rq)
|
|||
static int do_sched_rt_period_timer(struct rt_bandwidth *rt_b, int overrun)
|
||||
{
|
||||
int i, idle = 1;
|
||||
cpumask_t span;
|
||||
const struct cpumask *span;
|
||||
|
||||
if (!rt_bandwidth_enabled() || rt_b->rt_runtime == RUNTIME_INF)
|
||||
return 1;
|
||||
|
||||
span = sched_rt_period_mask();
|
||||
for_each_cpu_mask(i, span) {
|
||||
for_each_cpu(i, span) {
|
||||
int enqueue = 0;
|
||||
struct rt_rq *rt_rq = sched_rt_period_rt_rq(rt_b, i);
|
||||
struct rq *rq = rq_of_rt_rq(rt_rq);
|
||||
|
|
@ -537,13 +537,13 @@ static void update_curr_rt(struct rq *rq)
|
|||
for_each_sched_rt_entity(rt_se) {
|
||||
rt_rq = rt_rq_of_se(rt_se);
|
||||
|
||||
spin_lock(&rt_rq->rt_runtime_lock);
|
||||
if (sched_rt_runtime(rt_rq) != RUNTIME_INF) {
|
||||
spin_lock(&rt_rq->rt_runtime_lock);
|
||||
rt_rq->rt_time += delta_exec;
|
||||
if (sched_rt_runtime_exceeded(rt_rq))
|
||||
resched_task(curr);
|
||||
spin_unlock(&rt_rq->rt_runtime_lock);
|
||||
}
|
||||
spin_unlock(&rt_rq->rt_runtime_lock);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -805,17 +805,20 @@ static int select_task_rq_rt(struct task_struct *p, int sync)
|
|||
|
||||
static void check_preempt_equal_prio(struct rq *rq, struct task_struct *p)
|
||||
{
|
||||
cpumask_t mask;
|
||||
cpumask_var_t mask;
|
||||
|
||||
if (rq->curr->rt.nr_cpus_allowed == 1)
|
||||
return;
|
||||
|
||||
if (p->rt.nr_cpus_allowed != 1
|
||||
&& cpupri_find(&rq->rd->cpupri, p, &mask))
|
||||
if (!alloc_cpumask_var(&mask, GFP_ATOMIC))
|
||||
return;
|
||||
|
||||
if (!cpupri_find(&rq->rd->cpupri, rq->curr, &mask))
|
||||
return;
|
||||
if (p->rt.nr_cpus_allowed != 1
|
||||
&& cpupri_find(&rq->rd->cpupri, p, mask))
|
||||
goto free;
|
||||
|
||||
if (!cpupri_find(&rq->rd->cpupri, rq->curr, mask))
|
||||
goto free;
|
||||
|
||||
/*
|
||||
* There appears to be other cpus that can accept
|
||||
|
|
@ -824,6 +827,8 @@ static void check_preempt_equal_prio(struct rq *rq, struct task_struct *p)
|
|||
*/
|
||||
requeue_task_rt(rq, p, 1);
|
||||
resched_task(rq->curr);
|
||||
free:
|
||||
free_cpumask_var(mask);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_SMP */
|
||||
|
|
@ -909,15 +914,12 @@ static void put_prev_task_rt(struct rq *rq, struct task_struct *p)
|
|||
/* Only try algorithms three times */
|
||||
#define RT_MAX_TRIES 3
|
||||
|
||||
static int double_lock_balance(struct rq *this_rq, struct rq *busiest);
|
||||
static void double_unlock_balance(struct rq *this_rq, struct rq *busiest);
|
||||
|
||||
static void deactivate_task(struct rq *rq, struct task_struct *p, int sleep);
|
||||
|
||||
static int pick_rt_task(struct rq *rq, struct task_struct *p, int cpu)
|
||||
{
|
||||
if (!task_running(rq, p) &&
|
||||
(cpu < 0 || cpu_isset(cpu, p->cpus_allowed)) &&
|
||||
(cpu < 0 || cpumask_test_cpu(cpu, &p->cpus_allowed)) &&
|
||||
(p->rt.nr_cpus_allowed > 1))
|
||||
return 1;
|
||||
return 0;
|
||||
|
|
@ -956,7 +958,7 @@ static struct task_struct *pick_next_highest_task_rt(struct rq *rq, int cpu)
|
|||
return next;
|
||||
}
|
||||
|
||||
static DEFINE_PER_CPU(cpumask_t, local_cpu_mask);
|
||||
static DEFINE_PER_CPU(cpumask_var_t, local_cpu_mask);
|
||||
|
||||
static inline int pick_optimal_cpu(int this_cpu, cpumask_t *mask)
|
||||
{
|
||||
|
|
@ -976,7 +978,7 @@ static inline int pick_optimal_cpu(int this_cpu, cpumask_t *mask)
|
|||
static int find_lowest_rq(struct task_struct *task)
|
||||
{
|
||||
struct sched_domain *sd;
|
||||
cpumask_t *lowest_mask = &__get_cpu_var(local_cpu_mask);
|
||||
struct cpumask *lowest_mask = __get_cpu_var(local_cpu_mask);
|
||||
int this_cpu = smp_processor_id();
|
||||
int cpu = task_cpu(task);
|
||||
|
||||
|
|
@ -991,7 +993,7 @@ static int find_lowest_rq(struct task_struct *task)
|
|||
* I guess we might want to change cpupri_find() to ignore those
|
||||
* in the first place.
|
||||
*/
|
||||
cpus_and(*lowest_mask, *lowest_mask, cpu_active_map);
|
||||
cpumask_and(lowest_mask, lowest_mask, cpu_active_mask);
|
||||
|
||||
/*
|
||||
* At this point we have built a mask of cpus representing the
|
||||
|
|
@ -1001,7 +1003,7 @@ static int find_lowest_rq(struct task_struct *task)
|
|||
* We prioritize the last cpu that the task executed on since
|
||||
* it is most likely cache-hot in that location.
|
||||
*/
|
||||
if (cpu_isset(cpu, *lowest_mask))
|
||||
if (cpumask_test_cpu(cpu, lowest_mask))
|
||||
return cpu;
|
||||
|
||||
/*
|
||||
|
|
@ -1016,7 +1018,8 @@ static int find_lowest_rq(struct task_struct *task)
|
|||
cpumask_t domain_mask;
|
||||
int best_cpu;
|
||||
|
||||
cpus_and(domain_mask, sd->span, *lowest_mask);
|
||||
cpumask_and(&domain_mask, sched_domain_span(sd),
|
||||
lowest_mask);
|
||||
|
||||
best_cpu = pick_optimal_cpu(this_cpu,
|
||||
&domain_mask);
|
||||
|
|
@ -1057,8 +1060,8 @@ static struct rq *find_lock_lowest_rq(struct task_struct *task, struct rq *rq)
|
|||
* Also make sure that it wasn't scheduled on its rq.
|
||||
*/
|
||||
if (unlikely(task_rq(task) != rq ||
|
||||
!cpu_isset(lowest_rq->cpu,
|
||||
task->cpus_allowed) ||
|
||||
!cpumask_test_cpu(lowest_rq->cpu,
|
||||
&task->cpus_allowed) ||
|
||||
task_running(rq, task) ||
|
||||
!task->se.on_rq)) {
|
||||
|
||||
|
|
@ -1179,7 +1182,7 @@ static int pull_rt_task(struct rq *this_rq)
|
|||
|
||||
next = pick_next_task_rt(this_rq);
|
||||
|
||||
for_each_cpu_mask_nr(cpu, this_rq->rd->rto_mask) {
|
||||
for_each_cpu(cpu, this_rq->rd->rto_mask) {
|
||||
if (this_cpu == cpu)
|
||||
continue;
|
||||
|
||||
|
|
@ -1308,9 +1311,9 @@ move_one_task_rt(struct rq *this_rq, int this_cpu, struct rq *busiest,
|
|||
}
|
||||
|
||||
static void set_cpus_allowed_rt(struct task_struct *p,
|
||||
const cpumask_t *new_mask)
|
||||
const struct cpumask *new_mask)
|
||||
{
|
||||
int weight = cpus_weight(*new_mask);
|
||||
int weight = cpumask_weight(new_mask);
|
||||
|
||||
BUG_ON(!rt_task(p));
|
||||
|
||||
|
|
@ -1331,7 +1334,7 @@ static void set_cpus_allowed_rt(struct task_struct *p,
|
|||
update_rt_migration(rq);
|
||||
}
|
||||
|
||||
p->cpus_allowed = *new_mask;
|
||||
cpumask_copy(&p->cpus_allowed, new_mask);
|
||||
p->rt.nr_cpus_allowed = weight;
|
||||
}
|
||||
|
||||
|
|
@ -1374,6 +1377,15 @@ static void switched_from_rt(struct rq *rq, struct task_struct *p,
|
|||
if (!rq->rt.rt_nr_running)
|
||||
pull_rt_task(rq);
|
||||
}
|
||||
|
||||
static inline void init_sched_rt_class(void)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for_each_possible_cpu(i)
|
||||
alloc_cpumask_var_node(&per_cpu(local_cpu_mask, i),
|
||||
GFP_KERNEL, cpu_to_node(i));
|
||||
}
|
||||
#endif /* CONFIG_SMP */
|
||||
|
||||
/*
|
||||
|
|
@ -1544,3 +1556,4 @@ static void print_rt_stats(struct seq_file *m, int cpu)
|
|||
rcu_read_unlock();
|
||||
}
|
||||
#endif /* CONFIG_SCHED_DEBUG */
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ static int show_schedstat(struct seq_file *seq, void *v)
|
|||
rq->yld_act_empty, rq->yld_exp_empty, rq->yld_count,
|
||||
rq->sched_switch, rq->sched_count, rq->sched_goidle,
|
||||
rq->ttwu_count, rq->ttwu_local,
|
||||
rq->rq_sched_info.cpu_time,
|
||||
rq->rq_cpu_time,
|
||||
rq->rq_sched_info.run_delay, rq->rq_sched_info.pcount);
|
||||
|
||||
seq_printf(seq, "\n");
|
||||
|
|
@ -42,7 +42,8 @@ static int show_schedstat(struct seq_file *seq, void *v)
|
|||
for_each_domain(cpu, sd) {
|
||||
enum cpu_idle_type itype;
|
||||
|
||||
cpumask_scnprintf(mask_str, mask_len, sd->span);
|
||||
cpumask_scnprintf(mask_str, mask_len,
|
||||
sched_domain_span(sd));
|
||||
seq_printf(seq, "domain%d %s", dcount++, mask_str);
|
||||
for (itype = CPU_IDLE; itype < CPU_MAX_IDLE_TYPES;
|
||||
itype++) {
|
||||
|
|
@ -123,7 +124,7 @@ static inline void
|
|||
rq_sched_info_depart(struct rq *rq, unsigned long long delta)
|
||||
{
|
||||
if (rq)
|
||||
rq->rq_sched_info.cpu_time += delta;
|
||||
rq->rq_cpu_time += delta;
|
||||
}
|
||||
|
||||
static inline void
|
||||
|
|
@ -236,7 +237,6 @@ static inline void sched_info_depart(struct task_struct *t)
|
|||
unsigned long long delta = task_rq(t)->clock -
|
||||
t->sched_info.last_arrival;
|
||||
|
||||
t->sched_info.cpu_time += delta;
|
||||
rq_sched_info_depart(task_rq(t), delta);
|
||||
|
||||
if (t->state == TASK_RUNNING)
|
||||
|
|
|
|||
124
kernel/signal.c
124
kernel/signal.c
|
|
@ -41,6 +41,8 @@
|
|||
|
||||
static struct kmem_cache *sigqueue_cachep;
|
||||
|
||||
DEFINE_TRACE(sched_signal_send);
|
||||
|
||||
static void __user *sig_handler(struct task_struct *t, int sig)
|
||||
{
|
||||
return t->sighand->action[sig - 1].sa.sa_handler;
|
||||
|
|
@ -177,6 +179,11 @@ int next_signal(struct sigpending *pending, sigset_t *mask)
|
|||
return sig;
|
||||
}
|
||||
|
||||
/*
|
||||
* allocate a new signal queue record
|
||||
* - this may be called without locks if and only if t == current, otherwise an
|
||||
* appopriate lock must be held to stop the target task from exiting
|
||||
*/
|
||||
static struct sigqueue *__sigqueue_alloc(struct task_struct *t, gfp_t flags,
|
||||
int override_rlimit)
|
||||
{
|
||||
|
|
@ -184,11 +191,12 @@ static struct sigqueue *__sigqueue_alloc(struct task_struct *t, gfp_t flags,
|
|||
struct user_struct *user;
|
||||
|
||||
/*
|
||||
* In order to avoid problems with "switch_user()", we want to make
|
||||
* sure that the compiler doesn't re-load "t->user"
|
||||
* We won't get problems with the target's UID changing under us
|
||||
* because changing it requires RCU be used, and if t != current, the
|
||||
* caller must be holding the RCU readlock (by way of a spinlock) and
|
||||
* we use RCU protection here
|
||||
*/
|
||||
user = t->user;
|
||||
barrier();
|
||||
user = get_uid(__task_cred(t)->user);
|
||||
atomic_inc(&user->sigpending);
|
||||
if (override_rlimit ||
|
||||
atomic_read(&user->sigpending) <=
|
||||
|
|
@ -196,12 +204,14 @@ static struct sigqueue *__sigqueue_alloc(struct task_struct *t, gfp_t flags,
|
|||
q = kmem_cache_alloc(sigqueue_cachep, flags);
|
||||
if (unlikely(q == NULL)) {
|
||||
atomic_dec(&user->sigpending);
|
||||
free_uid(user);
|
||||
} else {
|
||||
INIT_LIST_HEAD(&q->list);
|
||||
q->flags = 0;
|
||||
q->user = get_uid(user);
|
||||
q->user = user;
|
||||
}
|
||||
return(q);
|
||||
|
||||
return q;
|
||||
}
|
||||
|
||||
static void __sigqueue_free(struct sigqueue *q)
|
||||
|
|
@ -562,10 +572,12 @@ static int rm_from_queue(unsigned long mask, struct sigpending *s)
|
|||
|
||||
/*
|
||||
* Bad permissions for sending the signal
|
||||
* - the caller must hold at least the RCU read lock
|
||||
*/
|
||||
static int check_kill_permission(int sig, struct siginfo *info,
|
||||
struct task_struct *t)
|
||||
{
|
||||
const struct cred *cred = current_cred(), *tcred;
|
||||
struct pid *sid;
|
||||
int error;
|
||||
|
||||
|
|
@ -579,8 +591,11 @@ static int check_kill_permission(int sig, struct siginfo *info,
|
|||
if (error)
|
||||
return error;
|
||||
|
||||
if ((current->euid ^ t->suid) && (current->euid ^ t->uid) &&
|
||||
(current->uid ^ t->suid) && (current->uid ^ t->uid) &&
|
||||
tcred = __task_cred(t);
|
||||
if ((cred->euid ^ tcred->suid) &&
|
||||
(cred->euid ^ tcred->uid) &&
|
||||
(cred->uid ^ tcred->suid) &&
|
||||
(cred->uid ^ tcred->uid) &&
|
||||
!capable(CAP_KILL)) {
|
||||
switch (sig) {
|
||||
case SIGCONT:
|
||||
|
|
@ -843,8 +858,9 @@ static int send_signal(int sig, struct siginfo *info, struct task_struct *t,
|
|||
q->info.si_signo = sig;
|
||||
q->info.si_errno = 0;
|
||||
q->info.si_code = SI_USER;
|
||||
q->info.si_pid = task_pid_vnr(current);
|
||||
q->info.si_uid = current->uid;
|
||||
q->info.si_pid = task_tgid_nr_ns(current,
|
||||
task_active_pid_ns(t));
|
||||
q->info.si_uid = current_uid();
|
||||
break;
|
||||
case (unsigned long) SEND_SIG_PRIV:
|
||||
q->info.si_signo = sig;
|
||||
|
|
@ -1008,6 +1024,10 @@ struct sighand_struct *lock_task_sighand(struct task_struct *tsk, unsigned long
|
|||
return sighand;
|
||||
}
|
||||
|
||||
/*
|
||||
* send signal info to all the members of a group
|
||||
* - the caller must hold the RCU read lock at least
|
||||
*/
|
||||
int group_send_sig_info(int sig, struct siginfo *info, struct task_struct *p)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
|
@ -1029,8 +1049,8 @@ int group_send_sig_info(int sig, struct siginfo *info, struct task_struct *p)
|
|||
/*
|
||||
* __kill_pgrp_info() sends a signal to a process group: this is what the tty
|
||||
* control characters do (^C, ^Z etc)
|
||||
* - the caller must hold at least a readlock on tasklist_lock
|
||||
*/
|
||||
|
||||
int __kill_pgrp_info(int sig, struct siginfo *info, struct pid *pgrp)
|
||||
{
|
||||
struct task_struct *p = NULL;
|
||||
|
|
@ -1086,6 +1106,7 @@ int kill_pid_info_as_uid(int sig, struct siginfo *info, struct pid *pid,
|
|||
{
|
||||
int ret = -EINVAL;
|
||||
struct task_struct *p;
|
||||
const struct cred *pcred;
|
||||
|
||||
if (!valid_signal(sig))
|
||||
return ret;
|
||||
|
|
@ -1096,9 +1117,11 @@ int kill_pid_info_as_uid(int sig, struct siginfo *info, struct pid *pid,
|
|||
ret = -ESRCH;
|
||||
goto out_unlock;
|
||||
}
|
||||
if ((info == SEND_SIG_NOINFO || (!is_si_special(info) && SI_FROMUSER(info)))
|
||||
&& (euid != p->suid) && (euid != p->uid)
|
||||
&& (uid != p->suid) && (uid != p->uid)) {
|
||||
pcred = __task_cred(p);
|
||||
if ((info == SEND_SIG_NOINFO ||
|
||||
(!is_si_special(info) && SI_FROMUSER(info))) &&
|
||||
euid != pcred->suid && euid != pcred->uid &&
|
||||
uid != pcred->suid && uid != pcred->uid) {
|
||||
ret = -EPERM;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
|
@ -1369,10 +1392,9 @@ int do_notify_parent(struct task_struct *tsk, int sig)
|
|||
*/
|
||||
rcu_read_lock();
|
||||
info.si_pid = task_pid_nr_ns(tsk, tsk->parent->nsproxy->pid_ns);
|
||||
info.si_uid = __task_cred(tsk)->uid;
|
||||
rcu_read_unlock();
|
||||
|
||||
info.si_uid = tsk->uid;
|
||||
|
||||
thread_group_cputime(tsk, &cputime);
|
||||
info.si_utime = cputime_to_jiffies(cputime.utime);
|
||||
info.si_stime = cputime_to_jiffies(cputime.stime);
|
||||
|
|
@ -1440,10 +1462,9 @@ static void do_notify_parent_cldstop(struct task_struct *tsk, int why)
|
|||
*/
|
||||
rcu_read_lock();
|
||||
info.si_pid = task_pid_nr_ns(tsk, tsk->parent->nsproxy->pid_ns);
|
||||
info.si_uid = __task_cred(tsk)->uid;
|
||||
rcu_read_unlock();
|
||||
|
||||
info.si_uid = tsk->uid;
|
||||
|
||||
info.si_utime = cputime_to_clock_t(tsk->utime);
|
||||
info.si_stime = cputime_to_clock_t(tsk->stime);
|
||||
|
||||
|
|
@ -1598,7 +1619,7 @@ void ptrace_notify(int exit_code)
|
|||
info.si_signo = SIGTRAP;
|
||||
info.si_code = exit_code;
|
||||
info.si_pid = task_pid_vnr(current);
|
||||
info.si_uid = current->uid;
|
||||
info.si_uid = current_uid();
|
||||
|
||||
/* Let the debugger run. */
|
||||
spin_lock_irq(¤t->sighand->siglock);
|
||||
|
|
@ -1710,7 +1731,7 @@ static int ptrace_signal(int signr, siginfo_t *info,
|
|||
info->si_errno = 0;
|
||||
info->si_code = SI_USER;
|
||||
info->si_pid = task_pid_vnr(current->parent);
|
||||
info->si_uid = current->parent->uid;
|
||||
info->si_uid = task_uid(current->parent);
|
||||
}
|
||||
|
||||
/* If the (new) signal is now blocked, requeue it. */
|
||||
|
|
@ -1940,7 +1961,7 @@ EXPORT_SYMBOL(unblock_all_signals);
|
|||
* System call entry points.
|
||||
*/
|
||||
|
||||
asmlinkage long sys_restart_syscall(void)
|
||||
SYSCALL_DEFINE0(restart_syscall)
|
||||
{
|
||||
struct restart_block *restart = ¤t_thread_info()->restart_block;
|
||||
return restart->fn(restart);
|
||||
|
|
@ -1993,8 +2014,8 @@ int sigprocmask(int how, sigset_t *set, sigset_t *oldset)
|
|||
return error;
|
||||
}
|
||||
|
||||
asmlinkage long
|
||||
sys_rt_sigprocmask(int how, sigset_t __user *set, sigset_t __user *oset, size_t sigsetsize)
|
||||
SYSCALL_DEFINE4(rt_sigprocmask, int, how, sigset_t __user *, set,
|
||||
sigset_t __user *, oset, size_t, sigsetsize)
|
||||
{
|
||||
int error = -EINVAL;
|
||||
sigset_t old_set, new_set;
|
||||
|
|
@ -2053,8 +2074,7 @@ out:
|
|||
return error;
|
||||
}
|
||||
|
||||
asmlinkage long
|
||||
sys_rt_sigpending(sigset_t __user *set, size_t sigsetsize)
|
||||
SYSCALL_DEFINE2(rt_sigpending, sigset_t __user *, set, size_t, sigsetsize)
|
||||
{
|
||||
return do_sigpending(set, sigsetsize);
|
||||
}
|
||||
|
|
@ -2125,11 +2145,9 @@ int copy_siginfo_to_user(siginfo_t __user *to, siginfo_t *from)
|
|||
|
||||
#endif
|
||||
|
||||
asmlinkage long
|
||||
sys_rt_sigtimedwait(const sigset_t __user *uthese,
|
||||
siginfo_t __user *uinfo,
|
||||
const struct timespec __user *uts,
|
||||
size_t sigsetsize)
|
||||
SYSCALL_DEFINE4(rt_sigtimedwait, const sigset_t __user *, uthese,
|
||||
siginfo_t __user *, uinfo, const struct timespec __user *, uts,
|
||||
size_t, sigsetsize)
|
||||
{
|
||||
int ret, sig;
|
||||
sigset_t these;
|
||||
|
|
@ -2202,8 +2220,7 @@ sys_rt_sigtimedwait(const sigset_t __user *uthese,
|
|||
return ret;
|
||||
}
|
||||
|
||||
asmlinkage long
|
||||
sys_kill(pid_t pid, int sig)
|
||||
SYSCALL_DEFINE2(kill, pid_t, pid, int, sig)
|
||||
{
|
||||
struct siginfo info;
|
||||
|
||||
|
|
@ -2211,7 +2228,7 @@ sys_kill(pid_t pid, int sig)
|
|||
info.si_errno = 0;
|
||||
info.si_code = SI_USER;
|
||||
info.si_pid = task_tgid_vnr(current);
|
||||
info.si_uid = current->uid;
|
||||
info.si_uid = current_uid();
|
||||
|
||||
return kill_something_info(sig, &info, pid);
|
||||
}
|
||||
|
|
@ -2228,7 +2245,7 @@ static int do_tkill(pid_t tgid, pid_t pid, int sig)
|
|||
info.si_errno = 0;
|
||||
info.si_code = SI_TKILL;
|
||||
info.si_pid = task_tgid_vnr(current);
|
||||
info.si_uid = current->uid;
|
||||
info.si_uid = current_uid();
|
||||
|
||||
rcu_read_lock();
|
||||
p = find_task_by_vpid(pid);
|
||||
|
|
@ -2262,7 +2279,7 @@ static int do_tkill(pid_t tgid, pid_t pid, int sig)
|
|||
* exists but it's not belonging to the target process anymore. This
|
||||
* method solves the problem of threads exiting and PIDs getting reused.
|
||||
*/
|
||||
asmlinkage long sys_tgkill(pid_t tgid, pid_t pid, int sig)
|
||||
SYSCALL_DEFINE3(tgkill, pid_t, tgid, pid_t, pid, int, sig)
|
||||
{
|
||||
/* This is only valid for single tasks */
|
||||
if (pid <= 0 || tgid <= 0)
|
||||
|
|
@ -2274,8 +2291,7 @@ asmlinkage long sys_tgkill(pid_t tgid, pid_t pid, int sig)
|
|||
/*
|
||||
* Send a signal to only one task, even if it's a CLONE_THREAD task.
|
||||
*/
|
||||
asmlinkage long
|
||||
sys_tkill(pid_t pid, int sig)
|
||||
SYSCALL_DEFINE2(tkill, pid_t, pid, int, sig)
|
||||
{
|
||||
/* This is only valid for single tasks */
|
||||
if (pid <= 0)
|
||||
|
|
@ -2284,8 +2300,8 @@ sys_tkill(pid_t pid, int sig)
|
|||
return do_tkill(0, pid, sig);
|
||||
}
|
||||
|
||||
asmlinkage long
|
||||
sys_rt_sigqueueinfo(pid_t pid, int sig, siginfo_t __user *uinfo)
|
||||
SYSCALL_DEFINE3(rt_sigqueueinfo, pid_t, pid, int, sig,
|
||||
siginfo_t __user *, uinfo)
|
||||
{
|
||||
siginfo_t info;
|
||||
|
||||
|
|
@ -2413,8 +2429,7 @@ out:
|
|||
|
||||
#ifdef __ARCH_WANT_SYS_SIGPENDING
|
||||
|
||||
asmlinkage long
|
||||
sys_sigpending(old_sigset_t __user *set)
|
||||
SYSCALL_DEFINE1(sigpending, old_sigset_t __user *, set)
|
||||
{
|
||||
return do_sigpending(set, sizeof(*set));
|
||||
}
|
||||
|
|
@ -2425,8 +2440,8 @@ sys_sigpending(old_sigset_t __user *set)
|
|||
/* Some platforms have their own version with special arguments others
|
||||
support only sys_rt_sigprocmask. */
|
||||
|
||||
asmlinkage long
|
||||
sys_sigprocmask(int how, old_sigset_t __user *set, old_sigset_t __user *oset)
|
||||
SYSCALL_DEFINE3(sigprocmask, int, how, old_sigset_t __user *, set,
|
||||
old_sigset_t __user *, oset)
|
||||
{
|
||||
int error;
|
||||
old_sigset_t old_set, new_set;
|
||||
|
|
@ -2476,11 +2491,10 @@ out:
|
|||
#endif /* __ARCH_WANT_SYS_SIGPROCMASK */
|
||||
|
||||
#ifdef __ARCH_WANT_SYS_RT_SIGACTION
|
||||
asmlinkage long
|
||||
sys_rt_sigaction(int sig,
|
||||
const struct sigaction __user *act,
|
||||
struct sigaction __user *oact,
|
||||
size_t sigsetsize)
|
||||
SYSCALL_DEFINE4(rt_sigaction, int, sig,
|
||||
const struct sigaction __user *, act,
|
||||
struct sigaction __user *, oact,
|
||||
size_t, sigsetsize)
|
||||
{
|
||||
struct k_sigaction new_sa, old_sa;
|
||||
int ret = -EINVAL;
|
||||
|
|
@ -2510,15 +2524,13 @@ out:
|
|||
/*
|
||||
* For backwards compatibility. Functionality superseded by sigprocmask.
|
||||
*/
|
||||
asmlinkage long
|
||||
sys_sgetmask(void)
|
||||
SYSCALL_DEFINE0(sgetmask)
|
||||
{
|
||||
/* SMP safe */
|
||||
return current->blocked.sig[0];
|
||||
}
|
||||
|
||||
asmlinkage long
|
||||
sys_ssetmask(int newmask)
|
||||
SYSCALL_DEFINE1(ssetmask, int, newmask)
|
||||
{
|
||||
int old;
|
||||
|
||||
|
|
@ -2538,8 +2550,7 @@ sys_ssetmask(int newmask)
|
|||
/*
|
||||
* For backwards compatibility. Functionality superseded by sigaction.
|
||||
*/
|
||||
asmlinkage unsigned long
|
||||
sys_signal(int sig, __sighandler_t handler)
|
||||
SYSCALL_DEFINE2(signal, int, sig, __sighandler_t, handler)
|
||||
{
|
||||
struct k_sigaction new_sa, old_sa;
|
||||
int ret;
|
||||
|
|
@ -2556,8 +2567,7 @@ sys_signal(int sig, __sighandler_t handler)
|
|||
|
||||
#ifdef __ARCH_WANT_SYS_PAUSE
|
||||
|
||||
asmlinkage long
|
||||
sys_pause(void)
|
||||
SYSCALL_DEFINE0(pause)
|
||||
{
|
||||
current->state = TASK_INTERRUPTIBLE;
|
||||
schedule();
|
||||
|
|
@ -2567,7 +2577,7 @@ sys_pause(void)
|
|||
#endif
|
||||
|
||||
#ifdef __ARCH_WANT_SYS_RT_SIGSUSPEND
|
||||
asmlinkage long sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize)
|
||||
SYSCALL_DEFINE2(rt_sigsuspend, sigset_t __user *, unewset, size_t, sigsetsize)
|
||||
{
|
||||
sigset_t newset;
|
||||
|
||||
|
|
|
|||
143
kernel/smp.c
143
kernel/smp.c
|
|
@ -24,8 +24,8 @@ struct call_function_data {
|
|||
struct call_single_data csd;
|
||||
spinlock_t lock;
|
||||
unsigned int refs;
|
||||
cpumask_t cpumask;
|
||||
struct rcu_head rcu_head;
|
||||
unsigned long cpumask_bits[];
|
||||
};
|
||||
|
||||
struct call_single_queue {
|
||||
|
|
@ -110,13 +110,13 @@ void generic_smp_call_function_interrupt(void)
|
|||
list_for_each_entry_rcu(data, &call_function_queue, csd.list) {
|
||||
int refs;
|
||||
|
||||
if (!cpu_isset(cpu, data->cpumask))
|
||||
if (!cpumask_test_cpu(cpu, to_cpumask(data->cpumask_bits)))
|
||||
continue;
|
||||
|
||||
data->csd.func(data->csd.info);
|
||||
|
||||
spin_lock(&data->lock);
|
||||
cpu_clear(cpu, data->cpumask);
|
||||
cpumask_clear_cpu(cpu, to_cpumask(data->cpumask_bits));
|
||||
WARN_ON(data->refs == 0);
|
||||
data->refs--;
|
||||
refs = data->refs;
|
||||
|
|
@ -223,7 +223,7 @@ int smp_call_function_single(int cpu, void (*func) (void *info), void *info,
|
|||
local_irq_save(flags);
|
||||
func(info);
|
||||
local_irq_restore(flags);
|
||||
} else if ((unsigned)cpu < NR_CPUS && cpu_online(cpu)) {
|
||||
} else if ((unsigned)cpu < nr_cpu_ids && cpu_online(cpu)) {
|
||||
struct call_single_data *data = NULL;
|
||||
|
||||
if (!wait) {
|
||||
|
|
@ -266,51 +266,19 @@ void __smp_call_function_single(int cpu, struct call_single_data *data)
|
|||
generic_exec_single(cpu, data);
|
||||
}
|
||||
|
||||
/* Dummy function */
|
||||
static void quiesce_dummy(void *unused)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensure stack based data used in call function mask is safe to free.
|
||||
*
|
||||
* This is needed by smp_call_function_mask when using on-stack data, because
|
||||
* a single call function queue is shared by all CPUs, and any CPU may pick up
|
||||
* the data item on the queue at any time before it is deleted. So we need to
|
||||
* ensure that all CPUs have transitioned through a quiescent state after
|
||||
* this call.
|
||||
*
|
||||
* This is a very slow function, implemented by sending synchronous IPIs to
|
||||
* all possible CPUs. For this reason, we have to alloc data rather than use
|
||||
* stack based data even in the case of synchronous calls. The stack based
|
||||
* data is then just used for deadlock/oom fallback which will be very rare.
|
||||
*
|
||||
* If a faster scheme can be made, we could go back to preferring stack based
|
||||
* data -- the data allocation/free is non-zero cost.
|
||||
*/
|
||||
static void smp_call_function_mask_quiesce_stack(cpumask_t mask)
|
||||
{
|
||||
struct call_single_data data;
|
||||
int cpu;
|
||||
|
||||
data.func = quiesce_dummy;
|
||||
data.info = NULL;
|
||||
|
||||
for_each_cpu_mask(cpu, mask) {
|
||||
data.flags = CSD_FLAG_WAIT;
|
||||
generic_exec_single(cpu, &data);
|
||||
}
|
||||
}
|
||||
/* FIXME: Shim for archs using old arch_send_call_function_ipi API. */
|
||||
#ifndef arch_send_call_function_ipi_mask
|
||||
#define arch_send_call_function_ipi_mask(maskp) \
|
||||
arch_send_call_function_ipi(*(maskp))
|
||||
#endif
|
||||
|
||||
/**
|
||||
* smp_call_function_mask(): Run a function on a set of other CPUs.
|
||||
* @mask: The set of cpus to run on.
|
||||
* smp_call_function_many(): Run a function on a set of other CPUs.
|
||||
* @mask: The set of cpus to run on (only runs on online subset).
|
||||
* @func: The function to run. This must be fast and non-blocking.
|
||||
* @info: An arbitrary pointer to pass to the function.
|
||||
* @wait: If true, wait (atomically) until function has completed on other CPUs.
|
||||
*
|
||||
* Returns 0 on success, else a negative status code.
|
||||
*
|
||||
* If @wait is true, then returns once @func has returned. Note that @wait
|
||||
* will be implicitly turned on in case of allocation failures, since
|
||||
* we fall back to on-stack allocation.
|
||||
|
|
@ -319,53 +287,57 @@ static void smp_call_function_mask_quiesce_stack(cpumask_t mask)
|
|||
* hardware interrupt handler or from a bottom half handler. Preemption
|
||||
* must be disabled when calling this function.
|
||||
*/
|
||||
int smp_call_function_mask(cpumask_t mask, void (*func)(void *), void *info,
|
||||
int wait)
|
||||
void smp_call_function_many(const struct cpumask *mask,
|
||||
void (*func)(void *), void *info,
|
||||
bool wait)
|
||||
{
|
||||
struct call_function_data d;
|
||||
struct call_function_data *data = NULL;
|
||||
cpumask_t allbutself;
|
||||
struct call_function_data *data;
|
||||
unsigned long flags;
|
||||
int cpu, num_cpus;
|
||||
int slowpath = 0;
|
||||
int cpu, next_cpu;
|
||||
|
||||
/* Can deadlock when called with interrupts disabled */
|
||||
WARN_ON(irqs_disabled());
|
||||
|
||||
cpu = smp_processor_id();
|
||||
allbutself = cpu_online_map;
|
||||
cpu_clear(cpu, allbutself);
|
||||
cpus_and(mask, mask, allbutself);
|
||||
num_cpus = cpus_weight(mask);
|
||||
/* So, what's a CPU they want? Ignoring this one. */
|
||||
cpu = cpumask_first_and(mask, cpu_online_mask);
|
||||
if (cpu == smp_processor_id())
|
||||
cpu = cpumask_next_and(cpu, mask, cpu_online_mask);
|
||||
/* No online cpus? We're done. */
|
||||
if (cpu >= nr_cpu_ids)
|
||||
return;
|
||||
|
||||
/*
|
||||
* If zero CPUs, return. If just a single CPU, turn this request
|
||||
* into a targetted single call instead since it's faster.
|
||||
*/
|
||||
if (!num_cpus)
|
||||
return 0;
|
||||
else if (num_cpus == 1) {
|
||||
cpu = first_cpu(mask);
|
||||
return smp_call_function_single(cpu, func, info, wait);
|
||||
/* Do we have another CPU which isn't us? */
|
||||
next_cpu = cpumask_next_and(cpu, mask, cpu_online_mask);
|
||||
if (next_cpu == smp_processor_id())
|
||||
next_cpu = cpumask_next_and(next_cpu, mask, cpu_online_mask);
|
||||
|
||||
/* Fastpath: do that cpu by itself. */
|
||||
if (next_cpu >= nr_cpu_ids) {
|
||||
smp_call_function_single(cpu, func, info, wait);
|
||||
return;
|
||||
}
|
||||
|
||||
data = kmalloc(sizeof(*data), GFP_ATOMIC);
|
||||
if (data) {
|
||||
data->csd.flags = CSD_FLAG_ALLOC;
|
||||
if (wait)
|
||||
data->csd.flags |= CSD_FLAG_WAIT;
|
||||
} else {
|
||||
data = &d;
|
||||
data->csd.flags = CSD_FLAG_WAIT;
|
||||
wait = 1;
|
||||
slowpath = 1;
|
||||
data = kmalloc(sizeof(*data) + cpumask_size(), GFP_ATOMIC);
|
||||
if (unlikely(!data)) {
|
||||
/* Slow path. */
|
||||
for_each_online_cpu(cpu) {
|
||||
if (cpu == smp_processor_id())
|
||||
continue;
|
||||
if (cpumask_test_cpu(cpu, mask))
|
||||
smp_call_function_single(cpu, func, info, wait);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock_init(&data->lock);
|
||||
data->csd.flags = CSD_FLAG_ALLOC;
|
||||
if (wait)
|
||||
data->csd.flags |= CSD_FLAG_WAIT;
|
||||
data->csd.func = func;
|
||||
data->csd.info = info;
|
||||
data->refs = num_cpus;
|
||||
data->cpumask = mask;
|
||||
cpumask_and(to_cpumask(data->cpumask_bits), mask, cpu_online_mask);
|
||||
cpumask_clear_cpu(smp_processor_id(), to_cpumask(data->cpumask_bits));
|
||||
data->refs = cpumask_weight(to_cpumask(data->cpumask_bits));
|
||||
|
||||
spin_lock_irqsave(&call_function_lock, flags);
|
||||
list_add_tail_rcu(&data->csd.list, &call_function_queue);
|
||||
|
|
@ -377,18 +349,13 @@ int smp_call_function_mask(cpumask_t mask, void (*func)(void *), void *info,
|
|||
smp_mb();
|
||||
|
||||
/* Send a message to all CPUs in the map */
|
||||
arch_send_call_function_ipi(mask);
|
||||
arch_send_call_function_ipi_mask(to_cpumask(data->cpumask_bits));
|
||||
|
||||
/* optionally wait for the CPUs to complete */
|
||||
if (wait) {
|
||||
if (wait)
|
||||
csd_flag_wait(&data->csd);
|
||||
if (unlikely(slowpath))
|
||||
smp_call_function_mask_quiesce_stack(mask);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(smp_call_function_mask);
|
||||
EXPORT_SYMBOL(smp_call_function_many);
|
||||
|
||||
/**
|
||||
* smp_call_function(): Run a function on all other CPUs.
|
||||
|
|
@ -396,7 +363,7 @@ EXPORT_SYMBOL(smp_call_function_mask);
|
|||
* @info: An arbitrary pointer to pass to the function.
|
||||
* @wait: If true, wait (atomically) until function has completed on other CPUs.
|
||||
*
|
||||
* Returns 0 on success, else a negative status code.
|
||||
* Returns 0.
|
||||
*
|
||||
* If @wait is true, then returns once @func has returned; otherwise
|
||||
* it returns just before the target cpu calls @func. In case of allocation
|
||||
|
|
@ -407,12 +374,10 @@ EXPORT_SYMBOL(smp_call_function_mask);
|
|||
*/
|
||||
int smp_call_function(void (*func)(void *), void *info, int wait)
|
||||
{
|
||||
int ret;
|
||||
|
||||
preempt_disable();
|
||||
ret = smp_call_function_mask(cpu_online_map, func, info, wait);
|
||||
smp_call_function_many(cpu_online_mask, func, info, wait);
|
||||
preempt_enable();
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(smp_call_function);
|
||||
|
||||
|
|
|
|||
|
|
@ -102,20 +102,6 @@ void local_bh_disable(void)
|
|||
|
||||
EXPORT_SYMBOL(local_bh_disable);
|
||||
|
||||
void __local_bh_enable(void)
|
||||
{
|
||||
WARN_ON_ONCE(in_irq());
|
||||
|
||||
/*
|
||||
* softirqs should never be enabled by __local_bh_enable(),
|
||||
* it always nests inside local_bh_enable() sections:
|
||||
*/
|
||||
WARN_ON_ONCE(softirq_count() == SOFTIRQ_OFFSET);
|
||||
|
||||
sub_preempt_count(SOFTIRQ_OFFSET);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__local_bh_enable);
|
||||
|
||||
/*
|
||||
* Special-case - softirqs can safely be enabled in
|
||||
* cond_resched_softirq(), or by __do_softirq(),
|
||||
|
|
@ -269,6 +255,7 @@ void irq_enter(void)
|
|||
{
|
||||
int cpu = smp_processor_id();
|
||||
|
||||
rcu_irq_enter();
|
||||
if (idle_cpu(cpu) && !in_interrupt()) {
|
||||
__irq_enter();
|
||||
tick_check_idle(cpu);
|
||||
|
|
@ -295,9 +282,9 @@ void irq_exit(void)
|
|||
|
||||
#ifdef CONFIG_NO_HZ
|
||||
/* Make sure that timer wheel updates are propagated */
|
||||
if (!in_interrupt() && idle_cpu(smp_processor_id()) && !need_resched())
|
||||
tick_nohz_stop_sched_tick(0);
|
||||
rcu_irq_exit();
|
||||
if (idle_cpu(smp_processor_id()) && !in_interrupt() && !need_resched())
|
||||
tick_nohz_stop_sched_tick(0);
|
||||
#endif
|
||||
preempt_enable_no_resched();
|
||||
}
|
||||
|
|
@ -746,7 +733,7 @@ static int __cpuinit cpu_callback(struct notifier_block *nfb,
|
|||
break;
|
||||
/* Unbind so it can run. Fall thru. */
|
||||
kthread_bind(per_cpu(ksoftirqd, hotcpu),
|
||||
any_online_cpu(cpu_online_map));
|
||||
cpumask_any(cpu_online_mask));
|
||||
case CPU_DEAD:
|
||||
case CPU_DEAD_FROZEN: {
|
||||
struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 };
|
||||
|
|
@ -797,3 +784,23 @@ int on_each_cpu(void (*func) (void *info), void *info, int wait)
|
|||
}
|
||||
EXPORT_SYMBOL(on_each_cpu);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* [ These __weak aliases are kept in a separate compilation unit, so that
|
||||
* GCC does not inline them incorrectly. ]
|
||||
*/
|
||||
|
||||
int __init __weak early_irq_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __init __weak arch_early_irq_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __weak arch_init_chip_data(struct irq_desc *desc, int cpu)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -164,7 +164,7 @@ unsigned long __read_mostly sysctl_hung_task_check_count = 1024;
|
|||
/*
|
||||
* Zero means infinite timeout - no checking done:
|
||||
*/
|
||||
unsigned long __read_mostly sysctl_hung_task_timeout_secs = 120;
|
||||
unsigned long __read_mostly sysctl_hung_task_timeout_secs = 480;
|
||||
|
||||
unsigned long __read_mostly sysctl_hung_task_warnings = 10;
|
||||
|
||||
|
|
@ -303,17 +303,15 @@ cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu)
|
|||
break;
|
||||
case CPU_ONLINE:
|
||||
case CPU_ONLINE_FROZEN:
|
||||
check_cpu = any_online_cpu(cpu_online_map);
|
||||
check_cpu = cpumask_any(cpu_online_mask);
|
||||
wake_up_process(per_cpu(watchdog_task, hotcpu));
|
||||
break;
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
case CPU_DOWN_PREPARE:
|
||||
case CPU_DOWN_PREPARE_FROZEN:
|
||||
if (hotcpu == check_cpu) {
|
||||
cpumask_t temp_cpu_online_map = cpu_online_map;
|
||||
|
||||
cpu_clear(hotcpu, temp_cpu_online_map);
|
||||
check_cpu = any_online_cpu(temp_cpu_online_map);
|
||||
/* Pick any other online cpu. */
|
||||
check_cpu = cpumask_any_but(cpu_online_mask, hotcpu);
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
@ -323,7 +321,7 @@ cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu)
|
|||
break;
|
||||
/* Unbind so it can run. Fall thru. */
|
||||
kthread_bind(per_cpu(watchdog_task, hotcpu),
|
||||
any_online_cpu(cpu_online_map));
|
||||
cpumask_any(cpu_online_mask));
|
||||
case CPU_DEAD:
|
||||
case CPU_DEAD_FROZEN:
|
||||
p = per_cpu(watchdog_task, hotcpu);
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
* Copyright (C) 2006 Red Hat, Inc., Ingo Molnar <mingo@redhat.com>
|
||||
*/
|
||||
#include <linux/sched.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kallsyms.h>
|
||||
#include <linux/stacktrace.h>
|
||||
|
|
@ -24,3 +25,13 @@ void print_stack_trace(struct stack_trace *trace, int spaces)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(print_stack_trace);
|
||||
|
||||
/*
|
||||
* Architectures that do not implement save_stack_trace_tsk get this
|
||||
* weak alias and a once-per-bootup warning (whenever this facility
|
||||
* is utilized - for example by procfs):
|
||||
*/
|
||||
__weak void
|
||||
save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
|
||||
{
|
||||
WARN_ONCE(1, KERN_INFO "save_stack_trace_tsk() not implemented yet.\n");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,10 @@ struct stop_machine_data {
|
|||
static unsigned int num_threads;
|
||||
static atomic_t thread_ack;
|
||||
static DEFINE_MUTEX(lock);
|
||||
|
||||
/* setup_lock protects refcount, stop_machine_wq and stop_machine_work. */
|
||||
static DEFINE_MUTEX(setup_lock);
|
||||
/* Users of stop_machine. */
|
||||
static int refcount;
|
||||
static struct workqueue_struct *stop_machine_wq;
|
||||
static struct stop_machine_data active, idle;
|
||||
static const cpumask_t *active_cpus;
|
||||
|
|
@ -69,10 +72,10 @@ static void stop_cpu(struct work_struct *unused)
|
|||
int err;
|
||||
|
||||
if (!active_cpus) {
|
||||
if (cpu == first_cpu(cpu_online_map))
|
||||
if (cpu == cpumask_first(cpu_online_mask))
|
||||
smdata = &active;
|
||||
} else {
|
||||
if (cpu_isset(cpu, *active_cpus))
|
||||
if (cpumask_test_cpu(cpu, active_cpus))
|
||||
smdata = &active;
|
||||
}
|
||||
/* Simple state machine */
|
||||
|
|
@ -109,7 +112,44 @@ static int chill(void *unused)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int __stop_machine(int (*fn)(void *), void *data, const cpumask_t *cpus)
|
||||
int stop_machine_create(void)
|
||||
{
|
||||
mutex_lock(&setup_lock);
|
||||
if (refcount)
|
||||
goto done;
|
||||
stop_machine_wq = create_rt_workqueue("kstop");
|
||||
if (!stop_machine_wq)
|
||||
goto err_out;
|
||||
stop_machine_work = alloc_percpu(struct work_struct);
|
||||
if (!stop_machine_work)
|
||||
goto err_out;
|
||||
done:
|
||||
refcount++;
|
||||
mutex_unlock(&setup_lock);
|
||||
return 0;
|
||||
|
||||
err_out:
|
||||
if (stop_machine_wq)
|
||||
destroy_workqueue(stop_machine_wq);
|
||||
mutex_unlock(&setup_lock);
|
||||
return -ENOMEM;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(stop_machine_create);
|
||||
|
||||
void stop_machine_destroy(void)
|
||||
{
|
||||
mutex_lock(&setup_lock);
|
||||
refcount--;
|
||||
if (refcount)
|
||||
goto done;
|
||||
destroy_workqueue(stop_machine_wq);
|
||||
free_percpu(stop_machine_work);
|
||||
done:
|
||||
mutex_unlock(&setup_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(stop_machine_destroy);
|
||||
|
||||
int __stop_machine(int (*fn)(void *), void *data, const struct cpumask *cpus)
|
||||
{
|
||||
struct work_struct *sm_work;
|
||||
int i, ret;
|
||||
|
|
@ -142,23 +182,18 @@ int __stop_machine(int (*fn)(void *), void *data, const cpumask_t *cpus)
|
|||
return ret;
|
||||
}
|
||||
|
||||
int stop_machine(int (*fn)(void *), void *data, const cpumask_t *cpus)
|
||||
int stop_machine(int (*fn)(void *), void *data, const struct cpumask *cpus)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = stop_machine_create();
|
||||
if (ret)
|
||||
return ret;
|
||||
/* No CPUs can come up or down during this. */
|
||||
get_online_cpus();
|
||||
ret = __stop_machine(fn, data, cpus);
|
||||
put_online_cpus();
|
||||
|
||||
stop_machine_destroy();
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(stop_machine);
|
||||
|
||||
static int __init stop_machine_init(void)
|
||||
{
|
||||
stop_machine_wq = create_rt_workqueue("kstop");
|
||||
stop_machine_work = alloc_percpu(struct work_struct);
|
||||
return 0;
|
||||
}
|
||||
core_initcall(stop_machine_init);
|
||||
|
|
|
|||
670
kernel/sys.c
670
kernel/sys.c
File diff suppressed because it is too large
Load diff
|
|
@ -131,6 +131,7 @@ cond_syscall(sys_io_destroy);
|
|||
cond_syscall(sys_io_submit);
|
||||
cond_syscall(sys_io_cancel);
|
||||
cond_syscall(sys_io_getevents);
|
||||
cond_syscall(sys_syslog);
|
||||
|
||||
/* arch-specific weak syscall entries */
|
||||
cond_syscall(sys_pciconfig_read);
|
||||
|
|
|
|||
|
|
@ -82,15 +82,14 @@ extern int percpu_pagelist_fraction;
|
|||
extern int compat_log;
|
||||
extern int latencytop_enabled;
|
||||
extern int sysctl_nr_open_min, sysctl_nr_open_max;
|
||||
#ifndef CONFIG_MMU
|
||||
extern int sysctl_nr_trim_pages;
|
||||
#endif
|
||||
#ifdef CONFIG_RCU_TORTURE_TEST
|
||||
extern int rcutorture_runnable;
|
||||
#endif /* #ifdef CONFIG_RCU_TORTURE_TEST */
|
||||
|
||||
/* Constants used for minimum and maximum */
|
||||
#if defined(CONFIG_HIGHMEM) || defined(CONFIG_DETECT_SOFTLOCKUP)
|
||||
static int one = 1;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DETECT_SOFTLOCKUP
|
||||
static int sixty = 60;
|
||||
static int neg_one = -1;
|
||||
|
|
@ -101,6 +100,7 @@ static int two = 2;
|
|||
#endif
|
||||
|
||||
static int zero;
|
||||
static int one = 1;
|
||||
static int one_hundred = 100;
|
||||
|
||||
/* this is needed for the proc_dointvec_minmax for [fs_]overflow UID and GID */
|
||||
|
|
@ -121,6 +121,10 @@ extern int sg_big_buff;
|
|||
#include <asm/system.h>
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SPARC64
|
||||
extern int sysctl_tsb_ratio;
|
||||
#endif
|
||||
|
||||
#ifdef __hppa__
|
||||
extern int pwrsw_enabled;
|
||||
extern int unaligned_enabled;
|
||||
|
|
@ -140,6 +144,7 @@ extern int acct_parm[];
|
|||
|
||||
#ifdef CONFIG_IA64
|
||||
extern int no_unaligned_warning;
|
||||
extern int unaligned_dump_stack;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_RT_MUTEXES
|
||||
|
|
@ -451,6 +456,16 @@ static struct ctl_table kern_table[] = {
|
|||
.proc_handler = &proc_dointvec,
|
||||
},
|
||||
#endif
|
||||
#ifdef CONFIG_SPARC64
|
||||
{
|
||||
.ctl_name = CTL_UNNUMBERED,
|
||||
.procname = "tsb-ratio",
|
||||
.data = &sysctl_tsb_ratio,
|
||||
.maxlen = sizeof (int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec,
|
||||
},
|
||||
#endif
|
||||
#ifdef __hppa__
|
||||
{
|
||||
.ctl_name = KERN_HPPA_PWRSW,
|
||||
|
|
@ -487,6 +502,26 @@ static struct ctl_table kern_table[] = {
|
|||
.proc_handler = &ftrace_enable_sysctl,
|
||||
},
|
||||
#endif
|
||||
#ifdef CONFIG_STACK_TRACER
|
||||
{
|
||||
.ctl_name = CTL_UNNUMBERED,
|
||||
.procname = "stack_tracer_enabled",
|
||||
.data = &stack_tracer_enabled,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &stack_trace_sysctl,
|
||||
},
|
||||
#endif
|
||||
#ifdef CONFIG_TRACING
|
||||
{
|
||||
.ctl_name = CTL_UNNUMBERED,
|
||||
.procname = "ftrace_dump_on_oops",
|
||||
.data = &ftrace_dump_on_oops,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec,
|
||||
},
|
||||
#endif
|
||||
#ifdef CONFIG_MODULES
|
||||
{
|
||||
.ctl_name = KERN_MODPROBE,
|
||||
|
|
@ -747,6 +782,14 @@ static struct ctl_table kern_table[] = {
|
|||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec,
|
||||
},
|
||||
{
|
||||
.ctl_name = CTL_UNNUMBERED,
|
||||
.procname = "unaligned-dump-stack",
|
||||
.data = &unaligned_dump_stack,
|
||||
.maxlen = sizeof (int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec,
|
||||
},
|
||||
#endif
|
||||
#ifdef CONFIG_DETECT_SOFTLOCKUP
|
||||
{
|
||||
|
|
@ -918,11 +961,21 @@ static struct ctl_table vm_table[] = {
|
|||
.data = &dirty_background_ratio,
|
||||
.maxlen = sizeof(dirty_background_ratio),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_minmax,
|
||||
.proc_handler = &dirty_background_ratio_handler,
|
||||
.strategy = &sysctl_intvec,
|
||||
.extra1 = &zero,
|
||||
.extra2 = &one_hundred,
|
||||
},
|
||||
{
|
||||
.ctl_name = CTL_UNNUMBERED,
|
||||
.procname = "dirty_background_bytes",
|
||||
.data = &dirty_background_bytes,
|
||||
.maxlen = sizeof(dirty_background_bytes),
|
||||
.mode = 0644,
|
||||
.proc_handler = &dirty_background_bytes_handler,
|
||||
.strategy = &sysctl_intvec,
|
||||
.extra1 = &one,
|
||||
},
|
||||
{
|
||||
.ctl_name = VM_DIRTY_RATIO,
|
||||
.procname = "dirty_ratio",
|
||||
|
|
@ -934,6 +987,16 @@ static struct ctl_table vm_table[] = {
|
|||
.extra1 = &zero,
|
||||
.extra2 = &one_hundred,
|
||||
},
|
||||
{
|
||||
.ctl_name = CTL_UNNUMBERED,
|
||||
.procname = "dirty_bytes",
|
||||
.data = &vm_dirty_bytes,
|
||||
.maxlen = sizeof(vm_dirty_bytes),
|
||||
.mode = 0644,
|
||||
.proc_handler = &dirty_bytes_handler,
|
||||
.strategy = &sysctl_intvec,
|
||||
.extra1 = &one,
|
||||
},
|
||||
{
|
||||
.procname = "dirty_writeback_centisecs",
|
||||
.data = &dirty_writeback_interval,
|
||||
|
|
@ -1051,6 +1114,17 @@ static struct ctl_table vm_table[] = {
|
|||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec
|
||||
},
|
||||
#else
|
||||
{
|
||||
.ctl_name = CTL_UNNUMBERED,
|
||||
.procname = "nr_trim_pages",
|
||||
.data = &sysctl_nr_trim_pages,
|
||||
.maxlen = sizeof(sysctl_nr_trim_pages),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_minmax,
|
||||
.strategy = &sysctl_intvec,
|
||||
.extra1 = &zero,
|
||||
},
|
||||
#endif
|
||||
{
|
||||
.ctl_name = VM_LAPTOP_MODE,
|
||||
|
|
@ -1623,7 +1697,7 @@ int do_sysctl(int __user *name, int nlen, void __user *oldval, size_t __user *ol
|
|||
return error;
|
||||
}
|
||||
|
||||
asmlinkage long sys_sysctl(struct __sysctl_args __user *args)
|
||||
SYSCALL_DEFINE1(sysctl, struct __sysctl_args __user *, args)
|
||||
{
|
||||
struct __sysctl_args tmp;
|
||||
int error;
|
||||
|
|
@ -1651,7 +1725,7 @@ out:
|
|||
|
||||
static int test_perm(int mode, int op)
|
||||
{
|
||||
if (!current->euid)
|
||||
if (!current_euid())
|
||||
mode >>= 6;
|
||||
else if (in_egroup_p(0))
|
||||
mode >>= 3;
|
||||
|
|
@ -2924,7 +2998,7 @@ int sysctl_ms_jiffies(struct ctl_table *table,
|
|||
#else /* CONFIG_SYSCTL_SYSCALL */
|
||||
|
||||
|
||||
asmlinkage long sys_sysctl(struct __sysctl_args __user *args)
|
||||
SYSCALL_DEFINE1(sysctl, struct __sysctl_args __user *, args)
|
||||
{
|
||||
struct __sysctl_args tmp;
|
||||
int error;
|
||||
|
|
|
|||
|
|
@ -730,7 +730,6 @@ static const struct trans_ctl_table trans_fs_quota_table[] = {
|
|||
};
|
||||
|
||||
static const struct trans_ctl_table trans_fs_xfs_table[] = {
|
||||
{ XFS_RESTRICT_CHOWN, "restrict_chown" },
|
||||
{ XFS_SGID_INHERIT, "irix_sgid_inherit" },
|
||||
{ XFS_SYMLINK_MODE, "irix_symlink_mode" },
|
||||
{ XFS_PANIC_MASK, "panic_mask" },
|
||||
|
|
|
|||
|
|
@ -290,18 +290,17 @@ ret:
|
|||
return;
|
||||
}
|
||||
|
||||
static int add_del_listener(pid_t pid, cpumask_t *maskp, int isadd)
|
||||
static int add_del_listener(pid_t pid, const struct cpumask *mask, int isadd)
|
||||
{
|
||||
struct listener_list *listeners;
|
||||
struct listener *s, *tmp;
|
||||
unsigned int cpu;
|
||||
cpumask_t mask = *maskp;
|
||||
|
||||
if (!cpus_subset(mask, cpu_possible_map))
|
||||
if (!cpumask_subset(mask, cpu_possible_mask))
|
||||
return -EINVAL;
|
||||
|
||||
if (isadd == REGISTER) {
|
||||
for_each_cpu_mask_nr(cpu, mask) {
|
||||
for_each_cpu(cpu, mask) {
|
||||
s = kmalloc_node(sizeof(struct listener), GFP_KERNEL,
|
||||
cpu_to_node(cpu));
|
||||
if (!s)
|
||||
|
|
@ -320,7 +319,7 @@ static int add_del_listener(pid_t pid, cpumask_t *maskp, int isadd)
|
|||
|
||||
/* Deregister or cleanup */
|
||||
cleanup:
|
||||
for_each_cpu_mask_nr(cpu, mask) {
|
||||
for_each_cpu(cpu, mask) {
|
||||
listeners = &per_cpu(listener_array, cpu);
|
||||
down_write(&listeners->sem);
|
||||
list_for_each_entry_safe(s, tmp, &listeners->list, list) {
|
||||
|
|
@ -335,7 +334,7 @@ cleanup:
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int parse(struct nlattr *na, cpumask_t *mask)
|
||||
static int parse(struct nlattr *na, struct cpumask *mask)
|
||||
{
|
||||
char *data;
|
||||
int len;
|
||||
|
|
@ -352,7 +351,7 @@ static int parse(struct nlattr *na, cpumask_t *mask)
|
|||
if (!data)
|
||||
return -ENOMEM;
|
||||
nla_strlcpy(data, na, len);
|
||||
ret = cpulist_parse(data, *mask);
|
||||
ret = cpulist_parse(data, mask);
|
||||
kfree(data);
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -428,23 +427,33 @@ err:
|
|||
|
||||
static int taskstats_user_cmd(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
int rc = 0;
|
||||
int rc;
|
||||
struct sk_buff *rep_skb;
|
||||
struct taskstats *stats;
|
||||
size_t size;
|
||||
cpumask_t mask;
|
||||
cpumask_var_t mask;
|
||||
|
||||
rc = parse(info->attrs[TASKSTATS_CMD_ATTR_REGISTER_CPUMASK], &mask);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
if (rc == 0)
|
||||
return add_del_listener(info->snd_pid, &mask, REGISTER);
|
||||
if (!alloc_cpumask_var(&mask, GFP_KERNEL))
|
||||
return -ENOMEM;
|
||||
|
||||
rc = parse(info->attrs[TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK], &mask);
|
||||
rc = parse(info->attrs[TASKSTATS_CMD_ATTR_REGISTER_CPUMASK], mask);
|
||||
if (rc < 0)
|
||||
goto free_return_rc;
|
||||
if (rc == 0) {
|
||||
rc = add_del_listener(info->snd_pid, mask, REGISTER);
|
||||
goto free_return_rc;
|
||||
}
|
||||
|
||||
rc = parse(info->attrs[TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK], mask);
|
||||
if (rc < 0)
|
||||
goto free_return_rc;
|
||||
if (rc == 0) {
|
||||
rc = add_del_listener(info->snd_pid, mask, DEREGISTER);
|
||||
free_return_rc:
|
||||
free_cpumask_var(mask);
|
||||
return rc;
|
||||
if (rc == 0)
|
||||
return add_del_listener(info->snd_pid, &mask, DEREGISTER);
|
||||
}
|
||||
free_cpumask_var(mask);
|
||||
|
||||
/*
|
||||
* Size includes space for nested attributes
|
||||
|
|
|
|||
|
|
@ -22,21 +22,11 @@
|
|||
|
||||
static u32 rand1, preh_val, posth_val, jph_val;
|
||||
static int errors, handler_errors, num_tests;
|
||||
static u32 (*target)(u32 value);
|
||||
static u32 (*target2)(u32 value);
|
||||
|
||||
static noinline u32 kprobe_target(u32 value)
|
||||
{
|
||||
/*
|
||||
* gcc ignores noinline on some architectures unless we stuff
|
||||
* sufficient lard into the function. The get_kprobe() here is
|
||||
* just for that.
|
||||
*
|
||||
* NOTE: We aren't concerned about the correctness of get_kprobe()
|
||||
* here; hence, this call is neither under !preempt nor with the
|
||||
* kprobe_mutex held. This is fine(tm)
|
||||
*/
|
||||
if (get_kprobe((void *)0xdeadbeef))
|
||||
printk(KERN_INFO "Kprobe smoke test: probe on 0xdeadbeef!\n");
|
||||
|
||||
return (value / div_factor);
|
||||
}
|
||||
|
||||
|
|
@ -74,7 +64,7 @@ static int test_kprobe(void)
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = kprobe_target(rand1);
|
||||
ret = target(rand1);
|
||||
unregister_kprobe(&kp);
|
||||
|
||||
if (preh_val == 0) {
|
||||
|
|
@ -92,6 +82,84 @@ static int test_kprobe(void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static noinline u32 kprobe_target2(u32 value)
|
||||
{
|
||||
return (value / div_factor) + 1;
|
||||
}
|
||||
|
||||
static int kp_pre_handler2(struct kprobe *p, struct pt_regs *regs)
|
||||
{
|
||||
preh_val = (rand1 / div_factor) + 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kp_post_handler2(struct kprobe *p, struct pt_regs *regs,
|
||||
unsigned long flags)
|
||||
{
|
||||
if (preh_val != (rand1 / div_factor) + 1) {
|
||||
handler_errors++;
|
||||
printk(KERN_ERR "Kprobe smoke test failed: "
|
||||
"incorrect value in post_handler2\n");
|
||||
}
|
||||
posth_val = preh_val + div_factor;
|
||||
}
|
||||
|
||||
static struct kprobe kp2 = {
|
||||
.symbol_name = "kprobe_target2",
|
||||
.pre_handler = kp_pre_handler2,
|
||||
.post_handler = kp_post_handler2
|
||||
};
|
||||
|
||||
static int test_kprobes(void)
|
||||
{
|
||||
int ret;
|
||||
struct kprobe *kps[2] = {&kp, &kp2};
|
||||
|
||||
kp.addr = 0; /* addr should be cleard for reusing kprobe. */
|
||||
ret = register_kprobes(kps, 2);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "Kprobe smoke test failed: "
|
||||
"register_kprobes returned %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
preh_val = 0;
|
||||
posth_val = 0;
|
||||
ret = target(rand1);
|
||||
|
||||
if (preh_val == 0) {
|
||||
printk(KERN_ERR "Kprobe smoke test failed: "
|
||||
"kprobe pre_handler not called\n");
|
||||
handler_errors++;
|
||||
}
|
||||
|
||||
if (posth_val == 0) {
|
||||
printk(KERN_ERR "Kprobe smoke test failed: "
|
||||
"kprobe post_handler not called\n");
|
||||
handler_errors++;
|
||||
}
|
||||
|
||||
preh_val = 0;
|
||||
posth_val = 0;
|
||||
ret = target2(rand1);
|
||||
|
||||
if (preh_val == 0) {
|
||||
printk(KERN_ERR "Kprobe smoke test failed: "
|
||||
"kprobe pre_handler2 not called\n");
|
||||
handler_errors++;
|
||||
}
|
||||
|
||||
if (posth_val == 0) {
|
||||
printk(KERN_ERR "Kprobe smoke test failed: "
|
||||
"kprobe post_handler2 not called\n");
|
||||
handler_errors++;
|
||||
}
|
||||
|
||||
unregister_kprobes(kps, 2);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static u32 j_kprobe_target(u32 value)
|
||||
{
|
||||
if (value != rand1) {
|
||||
|
|
@ -121,7 +189,7 @@ static int test_jprobe(void)
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = kprobe_target(rand1);
|
||||
ret = target(rand1);
|
||||
unregister_jprobe(&jp);
|
||||
if (jph_val == 0) {
|
||||
printk(KERN_ERR "Kprobe smoke test failed: "
|
||||
|
|
@ -132,6 +200,43 @@ static int test_jprobe(void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct jprobe jp2 = {
|
||||
.entry = j_kprobe_target,
|
||||
.kp.symbol_name = "kprobe_target2"
|
||||
};
|
||||
|
||||
static int test_jprobes(void)
|
||||
{
|
||||
int ret;
|
||||
struct jprobe *jps[2] = {&jp, &jp2};
|
||||
|
||||
jp.kp.addr = 0; /* addr should be cleard for reusing kprobe. */
|
||||
ret = register_jprobes(jps, 2);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "Kprobe smoke test failed: "
|
||||
"register_jprobes returned %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
jph_val = 0;
|
||||
ret = target(rand1);
|
||||
if (jph_val == 0) {
|
||||
printk(KERN_ERR "Kprobe smoke test failed: "
|
||||
"jprobe handler not called\n");
|
||||
handler_errors++;
|
||||
}
|
||||
|
||||
jph_val = 0;
|
||||
ret = target2(rand1);
|
||||
if (jph_val == 0) {
|
||||
printk(KERN_ERR "Kprobe smoke test failed: "
|
||||
"jprobe handler2 not called\n");
|
||||
handler_errors++;
|
||||
}
|
||||
unregister_jprobes(jps, 2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#ifdef CONFIG_KRETPROBES
|
||||
static u32 krph_val;
|
||||
|
||||
|
|
@ -177,7 +282,7 @@ static int test_kretprobe(void)
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = kprobe_target(rand1);
|
||||
ret = target(rand1);
|
||||
unregister_kretprobe(&rp);
|
||||
if (krph_val != rand1) {
|
||||
printk(KERN_ERR "Kprobe smoke test failed: "
|
||||
|
|
@ -187,12 +292,72 @@ static int test_kretprobe(void)
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int return_handler2(struct kretprobe_instance *ri, struct pt_regs *regs)
|
||||
{
|
||||
unsigned long ret = regs_return_value(regs);
|
||||
|
||||
if (ret != (rand1 / div_factor) + 1) {
|
||||
handler_errors++;
|
||||
printk(KERN_ERR "Kprobe smoke test failed: "
|
||||
"incorrect value in kretprobe handler2\n");
|
||||
}
|
||||
if (krph_val == 0) {
|
||||
handler_errors++;
|
||||
printk(KERN_ERR "Kprobe smoke test failed: "
|
||||
"call to kretprobe entry handler failed\n");
|
||||
}
|
||||
|
||||
krph_val = rand1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct kretprobe rp2 = {
|
||||
.handler = return_handler2,
|
||||
.entry_handler = entry_handler,
|
||||
.kp.symbol_name = "kprobe_target2"
|
||||
};
|
||||
|
||||
static int test_kretprobes(void)
|
||||
{
|
||||
int ret;
|
||||
struct kretprobe *rps[2] = {&rp, &rp2};
|
||||
|
||||
rp.kp.addr = 0; /* addr should be cleard for reusing kprobe. */
|
||||
ret = register_kretprobes(rps, 2);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "Kprobe smoke test failed: "
|
||||
"register_kretprobe returned %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
krph_val = 0;
|
||||
ret = target(rand1);
|
||||
if (krph_val != rand1) {
|
||||
printk(KERN_ERR "Kprobe smoke test failed: "
|
||||
"kretprobe handler not called\n");
|
||||
handler_errors++;
|
||||
}
|
||||
|
||||
krph_val = 0;
|
||||
ret = target2(rand1);
|
||||
if (krph_val != rand1) {
|
||||
printk(KERN_ERR "Kprobe smoke test failed: "
|
||||
"kretprobe handler2 not called\n");
|
||||
handler_errors++;
|
||||
}
|
||||
unregister_kretprobes(rps, 2);
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_KRETPROBES */
|
||||
|
||||
int init_test_probes(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
target = kprobe_target;
|
||||
target2 = kprobe_target2;
|
||||
|
||||
do {
|
||||
rand1 = random32();
|
||||
} while (rand1 <= div_factor);
|
||||
|
|
@ -203,16 +368,31 @@ int init_test_probes(void)
|
|||
if (ret < 0)
|
||||
errors++;
|
||||
|
||||
num_tests++;
|
||||
ret = test_kprobes();
|
||||
if (ret < 0)
|
||||
errors++;
|
||||
|
||||
num_tests++;
|
||||
ret = test_jprobe();
|
||||
if (ret < 0)
|
||||
errors++;
|
||||
|
||||
num_tests++;
|
||||
ret = test_jprobes();
|
||||
if (ret < 0)
|
||||
errors++;
|
||||
|
||||
#ifdef CONFIG_KRETPROBES
|
||||
num_tests++;
|
||||
ret = test_kretprobe();
|
||||
if (ret < 0)
|
||||
errors++;
|
||||
|
||||
num_tests++;
|
||||
ret = test_kretprobes();
|
||||
if (ret < 0)
|
||||
errors++;
|
||||
#endif /* CONFIG_KRETPROBES */
|
||||
|
||||
if (errors)
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@
|
|||
#include <linux/fs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/ptrace.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/unistd.h>
|
||||
|
|
@ -59,14 +60,15 @@ EXPORT_SYMBOL(sys_tz);
|
|||
* why not move it into the appropriate arch directory (for those
|
||||
* architectures that need it).
|
||||
*/
|
||||
asmlinkage long sys_time(time_t __user * tloc)
|
||||
SYSCALL_DEFINE1(time, time_t __user *, tloc)
|
||||
{
|
||||
time_t i = get_seconds();
|
||||
|
||||
if (tloc) {
|
||||
if (put_user(i,tloc))
|
||||
i = -EFAULT;
|
||||
return -EFAULT;
|
||||
}
|
||||
force_successful_syscall_return();
|
||||
return i;
|
||||
}
|
||||
|
||||
|
|
@ -77,7 +79,7 @@ asmlinkage long sys_time(time_t __user * tloc)
|
|||
* architectures that need it).
|
||||
*/
|
||||
|
||||
asmlinkage long sys_stime(time_t __user *tptr)
|
||||
SYSCALL_DEFINE1(stime, time_t __user *, tptr)
|
||||
{
|
||||
struct timespec tv;
|
||||
int err;
|
||||
|
|
@ -97,8 +99,8 @@ asmlinkage long sys_stime(time_t __user *tptr)
|
|||
|
||||
#endif /* __ARCH_WANT_SYS_TIME */
|
||||
|
||||
asmlinkage long sys_gettimeofday(struct timeval __user *tv,
|
||||
struct timezone __user *tz)
|
||||
SYSCALL_DEFINE2(gettimeofday, struct timeval __user *, tv,
|
||||
struct timezone __user *, tz)
|
||||
{
|
||||
if (likely(tv != NULL)) {
|
||||
struct timeval ktv;
|
||||
|
|
@ -182,8 +184,8 @@ int do_sys_settimeofday(struct timespec *tv, struct timezone *tz)
|
|||
return 0;
|
||||
}
|
||||
|
||||
asmlinkage long sys_settimeofday(struct timeval __user *tv,
|
||||
struct timezone __user *tz)
|
||||
SYSCALL_DEFINE2(settimeofday, struct timeval __user *, tv,
|
||||
struct timezone __user *, tz)
|
||||
{
|
||||
struct timeval user_tv;
|
||||
struct timespec new_ts;
|
||||
|
|
@ -203,7 +205,7 @@ asmlinkage long sys_settimeofday(struct timeval __user *tv,
|
|||
return do_sys_settimeofday(tv ? &new_ts : NULL, tz ? &new_tz : NULL);
|
||||
}
|
||||
|
||||
asmlinkage long sys_adjtimex(struct timex __user *txc_p)
|
||||
SYSCALL_DEFINE1(adjtimex, struct timex __user *, txc_p)
|
||||
{
|
||||
struct timex txc; /* Local copy of parameter */
|
||||
int ret;
|
||||
|
|
|
|||
|
|
@ -166,6 +166,8 @@ static void clockevents_notify_released(void)
|
|||
void clockevents_register_device(struct clock_event_device *dev)
|
||||
{
|
||||
BUG_ON(dev->mode != CLOCK_EVT_MODE_UNUSED);
|
||||
BUG_ON(!dev->cpumask);
|
||||
|
||||
/*
|
||||
* A nsec2cyc multiplicator of 0 is invalid and we'd crash
|
||||
* on it, so fix it up and emit a warning:
|
||||
|
|
|
|||
|
|
@ -145,10 +145,11 @@ static void clocksource_watchdog(unsigned long data)
|
|||
* Cycle through CPUs to check if the CPUs stay
|
||||
* synchronized to each other.
|
||||
*/
|
||||
int next_cpu = next_cpu_nr(raw_smp_processor_id(), cpu_online_map);
|
||||
int next_cpu = cpumask_next(raw_smp_processor_id(),
|
||||
cpu_online_mask);
|
||||
|
||||
if (next_cpu >= nr_cpu_ids)
|
||||
next_cpu = first_cpu(cpu_online_map);
|
||||
next_cpu = cpumask_first(cpu_online_mask);
|
||||
watchdog_timer.expires += WATCHDOG_INTERVAL;
|
||||
add_timer_on(&watchdog_timer, next_cpu);
|
||||
}
|
||||
|
|
@ -173,7 +174,7 @@ static void clocksource_check_watchdog(struct clocksource *cs)
|
|||
watchdog_last = watchdog->read();
|
||||
watchdog_timer.expires = jiffies + WATCHDOG_INTERVAL;
|
||||
add_timer_on(&watchdog_timer,
|
||||
first_cpu(cpu_online_map));
|
||||
cpumask_first(cpu_online_mask));
|
||||
}
|
||||
} else {
|
||||
if (cs->flags & CLOCK_SOURCE_IS_CONTINUOUS)
|
||||
|
|
@ -195,7 +196,7 @@ static void clocksource_check_watchdog(struct clocksource *cs)
|
|||
watchdog_timer.expires =
|
||||
jiffies + WATCHDOG_INTERVAL;
|
||||
add_timer_on(&watchdog_timer,
|
||||
first_cpu(cpu_online_map));
|
||||
cpumask_first(cpu_online_mask));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@
|
|||
*
|
||||
* The value 8 is somewhat carefully chosen, as anything
|
||||
* larger can result in overflows. NSEC_PER_JIFFY grows as
|
||||
* HZ shrinks, so values greater then 8 overflow 32bits when
|
||||
* HZ shrinks, so values greater than 8 overflow 32bits when
|
||||
* HZ=100.
|
||||
*/
|
||||
#define JIFFIES_SHIFT 8
|
||||
|
|
|
|||
|
|
@ -131,7 +131,7 @@ static enum hrtimer_restart ntp_leap_second(struct hrtimer *timer)
|
|||
{
|
||||
enum hrtimer_restart res = HRTIMER_NORESTART;
|
||||
|
||||
write_seqlock_irq(&xtime_lock);
|
||||
write_seqlock(&xtime_lock);
|
||||
|
||||
switch (time_state) {
|
||||
case TIME_OK:
|
||||
|
|
@ -164,7 +164,7 @@ static enum hrtimer_restart ntp_leap_second(struct hrtimer *timer)
|
|||
}
|
||||
update_vsyscall(&xtime, clock);
|
||||
|
||||
write_sequnlock_irq(&xtime_lock);
|
||||
write_sequnlock(&xtime_lock);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,9 @@
|
|||
*/
|
||||
|
||||
struct tick_device tick_broadcast_device;
|
||||
static cpumask_t tick_broadcast_mask;
|
||||
/* FIXME: Use cpumask_var_t. */
|
||||
static DECLARE_BITMAP(tick_broadcast_mask, NR_CPUS);
|
||||
static DECLARE_BITMAP(tmpmask, NR_CPUS);
|
||||
static DEFINE_SPINLOCK(tick_broadcast_lock);
|
||||
static int tick_broadcast_force;
|
||||
|
||||
|
|
@ -46,9 +48,9 @@ struct tick_device *tick_get_broadcast_device(void)
|
|||
return &tick_broadcast_device;
|
||||
}
|
||||
|
||||
cpumask_t *tick_get_broadcast_mask(void)
|
||||
struct cpumask *tick_get_broadcast_mask(void)
|
||||
{
|
||||
return &tick_broadcast_mask;
|
||||
return to_cpumask(tick_broadcast_mask);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -72,7 +74,7 @@ int tick_check_broadcast_device(struct clock_event_device *dev)
|
|||
|
||||
clockevents_exchange_device(NULL, dev);
|
||||
tick_broadcast_device.evtdev = dev;
|
||||
if (!cpus_empty(tick_broadcast_mask))
|
||||
if (!cpumask_empty(tick_get_broadcast_mask()))
|
||||
tick_broadcast_start_periodic(dev);
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -104,7 +106,7 @@ int tick_device_uses_broadcast(struct clock_event_device *dev, int cpu)
|
|||
*/
|
||||
if (!tick_device_is_functional(dev)) {
|
||||
dev->event_handler = tick_handle_periodic;
|
||||
cpu_set(cpu, tick_broadcast_mask);
|
||||
cpumask_set_cpu(cpu, tick_get_broadcast_mask());
|
||||
tick_broadcast_start_periodic(tick_broadcast_device.evtdev);
|
||||
ret = 1;
|
||||
} else {
|
||||
|
|
@ -116,7 +118,7 @@ int tick_device_uses_broadcast(struct clock_event_device *dev, int cpu)
|
|||
if (!(dev->features & CLOCK_EVT_FEAT_C3STOP)) {
|
||||
int cpu = smp_processor_id();
|
||||
|
||||
cpu_clear(cpu, tick_broadcast_mask);
|
||||
cpumask_clear_cpu(cpu, tick_get_broadcast_mask());
|
||||
tick_broadcast_clear_oneshot(cpu);
|
||||
}
|
||||
}
|
||||
|
|
@ -125,9 +127,9 @@ int tick_device_uses_broadcast(struct clock_event_device *dev, int cpu)
|
|||
}
|
||||
|
||||
/*
|
||||
* Broadcast the event to the cpus, which are set in the mask
|
||||
* Broadcast the event to the cpus, which are set in the mask (mangled).
|
||||
*/
|
||||
static void tick_do_broadcast(cpumask_t mask)
|
||||
static void tick_do_broadcast(struct cpumask *mask)
|
||||
{
|
||||
int cpu = smp_processor_id();
|
||||
struct tick_device *td;
|
||||
|
|
@ -135,21 +137,20 @@ static void tick_do_broadcast(cpumask_t mask)
|
|||
/*
|
||||
* Check, if the current cpu is in the mask
|
||||
*/
|
||||
if (cpu_isset(cpu, mask)) {
|
||||
cpu_clear(cpu, mask);
|
||||
if (cpumask_test_cpu(cpu, mask)) {
|
||||
cpumask_clear_cpu(cpu, mask);
|
||||
td = &per_cpu(tick_cpu_device, cpu);
|
||||
td->evtdev->event_handler(td->evtdev);
|
||||
}
|
||||
|
||||
if (!cpus_empty(mask)) {
|
||||
if (!cpumask_empty(mask)) {
|
||||
/*
|
||||
* It might be necessary to actually check whether the devices
|
||||
* have different broadcast functions. For now, just use the
|
||||
* one of the first device. This works as long as we have this
|
||||
* misfeature only on x86 (lapic)
|
||||
*/
|
||||
cpu = first_cpu(mask);
|
||||
td = &per_cpu(tick_cpu_device, cpu);
|
||||
td = &per_cpu(tick_cpu_device, cpumask_first(mask));
|
||||
td->evtdev->broadcast(mask);
|
||||
}
|
||||
}
|
||||
|
|
@ -160,12 +161,11 @@ static void tick_do_broadcast(cpumask_t mask)
|
|||
*/
|
||||
static void tick_do_periodic_broadcast(void)
|
||||
{
|
||||
cpumask_t mask;
|
||||
|
||||
spin_lock(&tick_broadcast_lock);
|
||||
|
||||
cpus_and(mask, cpu_online_map, tick_broadcast_mask);
|
||||
tick_do_broadcast(mask);
|
||||
cpumask_and(to_cpumask(tmpmask),
|
||||
cpu_online_mask, tick_get_broadcast_mask());
|
||||
tick_do_broadcast(to_cpumask(tmpmask));
|
||||
|
||||
spin_unlock(&tick_broadcast_lock);
|
||||
}
|
||||
|
|
@ -228,13 +228,13 @@ static void tick_do_broadcast_on_off(void *why)
|
|||
if (!tick_device_is_functional(dev))
|
||||
goto out;
|
||||
|
||||
bc_stopped = cpus_empty(tick_broadcast_mask);
|
||||
bc_stopped = cpumask_empty(tick_get_broadcast_mask());
|
||||
|
||||
switch (*reason) {
|
||||
case CLOCK_EVT_NOTIFY_BROADCAST_ON:
|
||||
case CLOCK_EVT_NOTIFY_BROADCAST_FORCE:
|
||||
if (!cpu_isset(cpu, tick_broadcast_mask)) {
|
||||
cpu_set(cpu, tick_broadcast_mask);
|
||||
if (!cpumask_test_cpu(cpu, tick_get_broadcast_mask())) {
|
||||
cpumask_set_cpu(cpu, tick_get_broadcast_mask());
|
||||
if (tick_broadcast_device.mode ==
|
||||
TICKDEV_MODE_PERIODIC)
|
||||
clockevents_shutdown(dev);
|
||||
|
|
@ -244,8 +244,8 @@ static void tick_do_broadcast_on_off(void *why)
|
|||
break;
|
||||
case CLOCK_EVT_NOTIFY_BROADCAST_OFF:
|
||||
if (!tick_broadcast_force &&
|
||||
cpu_isset(cpu, tick_broadcast_mask)) {
|
||||
cpu_clear(cpu, tick_broadcast_mask);
|
||||
cpumask_test_cpu(cpu, tick_get_broadcast_mask())) {
|
||||
cpumask_clear_cpu(cpu, tick_get_broadcast_mask());
|
||||
if (tick_broadcast_device.mode ==
|
||||
TICKDEV_MODE_PERIODIC)
|
||||
tick_setup_periodic(dev, 0);
|
||||
|
|
@ -253,7 +253,7 @@ static void tick_do_broadcast_on_off(void *why)
|
|||
break;
|
||||
}
|
||||
|
||||
if (cpus_empty(tick_broadcast_mask)) {
|
||||
if (cpumask_empty(tick_get_broadcast_mask())) {
|
||||
if (!bc_stopped)
|
||||
clockevents_shutdown(bc);
|
||||
} else if (bc_stopped) {
|
||||
|
|
@ -272,7 +272,7 @@ out:
|
|||
*/
|
||||
void tick_broadcast_on_off(unsigned long reason, int *oncpu)
|
||||
{
|
||||
if (!cpu_isset(*oncpu, cpu_online_map))
|
||||
if (!cpumask_test_cpu(*oncpu, cpu_online_mask))
|
||||
printk(KERN_ERR "tick-broadcast: ignoring broadcast for "
|
||||
"offline CPU #%d\n", *oncpu);
|
||||
else
|
||||
|
|
@ -303,10 +303,10 @@ void tick_shutdown_broadcast(unsigned int *cpup)
|
|||
spin_lock_irqsave(&tick_broadcast_lock, flags);
|
||||
|
||||
bc = tick_broadcast_device.evtdev;
|
||||
cpu_clear(cpu, tick_broadcast_mask);
|
||||
cpumask_clear_cpu(cpu, tick_get_broadcast_mask());
|
||||
|
||||
if (tick_broadcast_device.mode == TICKDEV_MODE_PERIODIC) {
|
||||
if (bc && cpus_empty(tick_broadcast_mask))
|
||||
if (bc && cpumask_empty(tick_get_broadcast_mask()))
|
||||
clockevents_shutdown(bc);
|
||||
}
|
||||
|
||||
|
|
@ -342,10 +342,10 @@ int tick_resume_broadcast(void)
|
|||
|
||||
switch (tick_broadcast_device.mode) {
|
||||
case TICKDEV_MODE_PERIODIC:
|
||||
if(!cpus_empty(tick_broadcast_mask))
|
||||
if (!cpumask_empty(tick_get_broadcast_mask()))
|
||||
tick_broadcast_start_periodic(bc);
|
||||
broadcast = cpu_isset(smp_processor_id(),
|
||||
tick_broadcast_mask);
|
||||
broadcast = cpumask_test_cpu(smp_processor_id(),
|
||||
tick_get_broadcast_mask());
|
||||
break;
|
||||
case TICKDEV_MODE_ONESHOT:
|
||||
broadcast = tick_resume_broadcast_oneshot(bc);
|
||||
|
|
@ -360,14 +360,15 @@ int tick_resume_broadcast(void)
|
|||
|
||||
#ifdef CONFIG_TICK_ONESHOT
|
||||
|
||||
static cpumask_t tick_broadcast_oneshot_mask;
|
||||
/* FIXME: use cpumask_var_t. */
|
||||
static DECLARE_BITMAP(tick_broadcast_oneshot_mask, NR_CPUS);
|
||||
|
||||
/*
|
||||
* Debugging: see timer_list.c
|
||||
* Exposed for debugging: see timer_list.c
|
||||
*/
|
||||
cpumask_t *tick_get_broadcast_oneshot_mask(void)
|
||||
struct cpumask *tick_get_broadcast_oneshot_mask(void)
|
||||
{
|
||||
return &tick_broadcast_oneshot_mask;
|
||||
return to_cpumask(tick_broadcast_oneshot_mask);
|
||||
}
|
||||
|
||||
static int tick_broadcast_set_event(ktime_t expires, int force)
|
||||
|
|
@ -389,7 +390,7 @@ int tick_resume_broadcast_oneshot(struct clock_event_device *bc)
|
|||
*/
|
||||
void tick_check_oneshot_broadcast(int cpu)
|
||||
{
|
||||
if (cpu_isset(cpu, tick_broadcast_oneshot_mask)) {
|
||||
if (cpumask_test_cpu(cpu, to_cpumask(tick_broadcast_oneshot_mask))) {
|
||||
struct tick_device *td = &per_cpu(tick_cpu_device, cpu);
|
||||
|
||||
clockevents_set_mode(td->evtdev, CLOCK_EVT_MODE_ONESHOT);
|
||||
|
|
@ -402,7 +403,6 @@ void tick_check_oneshot_broadcast(int cpu)
|
|||
static void tick_handle_oneshot_broadcast(struct clock_event_device *dev)
|
||||
{
|
||||
struct tick_device *td;
|
||||
cpumask_t mask;
|
||||
ktime_t now, next_event;
|
||||
int cpu;
|
||||
|
||||
|
|
@ -410,13 +410,13 @@ static void tick_handle_oneshot_broadcast(struct clock_event_device *dev)
|
|||
again:
|
||||
dev->next_event.tv64 = KTIME_MAX;
|
||||
next_event.tv64 = KTIME_MAX;
|
||||
mask = CPU_MASK_NONE;
|
||||
cpumask_clear(to_cpumask(tmpmask));
|
||||
now = ktime_get();
|
||||
/* Find all expired events */
|
||||
for_each_cpu_mask_nr(cpu, tick_broadcast_oneshot_mask) {
|
||||
for_each_cpu(cpu, tick_get_broadcast_oneshot_mask()) {
|
||||
td = &per_cpu(tick_cpu_device, cpu);
|
||||
if (td->evtdev->next_event.tv64 <= now.tv64)
|
||||
cpu_set(cpu, mask);
|
||||
cpumask_set_cpu(cpu, to_cpumask(tmpmask));
|
||||
else if (td->evtdev->next_event.tv64 < next_event.tv64)
|
||||
next_event.tv64 = td->evtdev->next_event.tv64;
|
||||
}
|
||||
|
|
@ -424,7 +424,7 @@ again:
|
|||
/*
|
||||
* Wakeup the cpus which have an expired event.
|
||||
*/
|
||||
tick_do_broadcast(mask);
|
||||
tick_do_broadcast(to_cpumask(tmpmask));
|
||||
|
||||
/*
|
||||
* Two reasons for reprogram:
|
||||
|
|
@ -476,15 +476,16 @@ void tick_broadcast_oneshot_control(unsigned long reason)
|
|||
goto out;
|
||||
|
||||
if (reason == CLOCK_EVT_NOTIFY_BROADCAST_ENTER) {
|
||||
if (!cpu_isset(cpu, tick_broadcast_oneshot_mask)) {
|
||||
cpu_set(cpu, tick_broadcast_oneshot_mask);
|
||||
if (!cpumask_test_cpu(cpu, tick_get_broadcast_oneshot_mask())) {
|
||||
cpumask_set_cpu(cpu, tick_get_broadcast_oneshot_mask());
|
||||
clockevents_set_mode(dev, CLOCK_EVT_MODE_SHUTDOWN);
|
||||
if (dev->next_event.tv64 < bc->next_event.tv64)
|
||||
tick_broadcast_set_event(dev->next_event, 1);
|
||||
}
|
||||
} else {
|
||||
if (cpu_isset(cpu, tick_broadcast_oneshot_mask)) {
|
||||
cpu_clear(cpu, tick_broadcast_oneshot_mask);
|
||||
if (cpumask_test_cpu(cpu, tick_get_broadcast_oneshot_mask())) {
|
||||
cpumask_clear_cpu(cpu,
|
||||
tick_get_broadcast_oneshot_mask());
|
||||
clockevents_set_mode(dev, CLOCK_EVT_MODE_ONESHOT);
|
||||
if (dev->next_event.tv64 != KTIME_MAX)
|
||||
tick_program_event(dev->next_event, 1);
|
||||
|
|
@ -502,15 +503,16 @@ out:
|
|||
*/
|
||||
static void tick_broadcast_clear_oneshot(int cpu)
|
||||
{
|
||||
cpu_clear(cpu, tick_broadcast_oneshot_mask);
|
||||
cpumask_clear_cpu(cpu, tick_get_broadcast_oneshot_mask());
|
||||
}
|
||||
|
||||
static void tick_broadcast_init_next_event(cpumask_t *mask, ktime_t expires)
|
||||
static void tick_broadcast_init_next_event(struct cpumask *mask,
|
||||
ktime_t expires)
|
||||
{
|
||||
struct tick_device *td;
|
||||
int cpu;
|
||||
|
||||
for_each_cpu_mask_nr(cpu, *mask) {
|
||||
for_each_cpu(cpu, mask) {
|
||||
td = &per_cpu(tick_cpu_device, cpu);
|
||||
if (td->evtdev)
|
||||
td->evtdev->next_event = expires;
|
||||
|
|
@ -526,7 +528,6 @@ void tick_broadcast_setup_oneshot(struct clock_event_device *bc)
|
|||
if (bc->event_handler != tick_handle_oneshot_broadcast) {
|
||||
int was_periodic = bc->mode == CLOCK_EVT_MODE_PERIODIC;
|
||||
int cpu = smp_processor_id();
|
||||
cpumask_t mask;
|
||||
|
||||
bc->event_handler = tick_handle_oneshot_broadcast;
|
||||
clockevents_set_mode(bc, CLOCK_EVT_MODE_ONESHOT);
|
||||
|
|
@ -540,13 +541,15 @@ void tick_broadcast_setup_oneshot(struct clock_event_device *bc)
|
|||
* oneshot_mask bits for those and program the
|
||||
* broadcast device to fire.
|
||||
*/
|
||||
mask = tick_broadcast_mask;
|
||||
cpu_clear(cpu, mask);
|
||||
cpus_or(tick_broadcast_oneshot_mask,
|
||||
tick_broadcast_oneshot_mask, mask);
|
||||
cpumask_copy(to_cpumask(tmpmask), tick_get_broadcast_mask());
|
||||
cpumask_clear_cpu(cpu, to_cpumask(tmpmask));
|
||||
cpumask_or(tick_get_broadcast_oneshot_mask(),
|
||||
tick_get_broadcast_oneshot_mask(),
|
||||
to_cpumask(tmpmask));
|
||||
|
||||
if (was_periodic && !cpus_empty(mask)) {
|
||||
tick_broadcast_init_next_event(&mask, tick_next_period);
|
||||
if (was_periodic && !cpumask_empty(to_cpumask(tmpmask))) {
|
||||
tick_broadcast_init_next_event(to_cpumask(tmpmask),
|
||||
tick_next_period);
|
||||
tick_broadcast_set_event(tick_next_period, 1);
|
||||
} else
|
||||
bc->next_event.tv64 = KTIME_MAX;
|
||||
|
|
@ -585,7 +588,7 @@ void tick_shutdown_broadcast_oneshot(unsigned int *cpup)
|
|||
* Clear the broadcast mask flag for the dead cpu, but do not
|
||||
* stop the broadcast device!
|
||||
*/
|
||||
cpu_clear(cpu, tick_broadcast_oneshot_mask);
|
||||
cpumask_clear_cpu(cpu, tick_get_broadcast_oneshot_mask());
|
||||
|
||||
spin_unlock_irqrestore(&tick_broadcast_lock, flags);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ void tick_setup_periodic(struct clock_event_device *dev, int broadcast)
|
|||
*/
|
||||
static void tick_setup_device(struct tick_device *td,
|
||||
struct clock_event_device *newdev, int cpu,
|
||||
const cpumask_t *cpumask)
|
||||
const struct cpumask *cpumask)
|
||||
{
|
||||
ktime_t next_event;
|
||||
void (*handler)(struct clock_event_device *) = NULL;
|
||||
|
|
@ -171,8 +171,8 @@ static void tick_setup_device(struct tick_device *td,
|
|||
* When the device is not per cpu, pin the interrupt to the
|
||||
* current cpu:
|
||||
*/
|
||||
if (!cpus_equal(newdev->cpumask, *cpumask))
|
||||
irq_set_affinity(newdev->irq, *cpumask);
|
||||
if (!cpumask_equal(newdev->cpumask, cpumask))
|
||||
irq_set_affinity(newdev->irq, cpumask);
|
||||
|
||||
/*
|
||||
* When global broadcasting is active, check if the current
|
||||
|
|
@ -202,14 +202,14 @@ static int tick_check_new_device(struct clock_event_device *newdev)
|
|||
spin_lock_irqsave(&tick_device_lock, flags);
|
||||
|
||||
cpu = smp_processor_id();
|
||||
if (!cpu_isset(cpu, newdev->cpumask))
|
||||
if (!cpumask_test_cpu(cpu, newdev->cpumask))
|
||||
goto out_bc;
|
||||
|
||||
td = &per_cpu(tick_cpu_device, cpu);
|
||||
curdev = td->evtdev;
|
||||
|
||||
/* cpu local device ? */
|
||||
if (!cpus_equal(newdev->cpumask, cpumask_of_cpu(cpu))) {
|
||||
if (!cpumask_equal(newdev->cpumask, cpumask_of(cpu))) {
|
||||
|
||||
/*
|
||||
* If the cpu affinity of the device interrupt can not
|
||||
|
|
@ -222,7 +222,7 @@ static int tick_check_new_device(struct clock_event_device *newdev)
|
|||
* If we have a cpu local device already, do not replace it
|
||||
* by a non cpu local device
|
||||
*/
|
||||
if (curdev && cpus_equal(curdev->cpumask, cpumask_of_cpu(cpu)))
|
||||
if (curdev && cpumask_equal(curdev->cpumask, cpumask_of(cpu)))
|
||||
goto out_bc;
|
||||
}
|
||||
|
||||
|
|
@ -254,7 +254,7 @@ static int tick_check_new_device(struct clock_event_device *newdev)
|
|||
curdev = NULL;
|
||||
}
|
||||
clockevents_exchange_device(curdev, newdev);
|
||||
tick_setup_device(td, newdev, cpu, &cpumask_of_cpu(cpu));
|
||||
tick_setup_device(td, newdev, cpu, cpumask_of(cpu));
|
||||
if (newdev->features & CLOCK_EVT_FEAT_ONESHOT)
|
||||
tick_oneshot_notify();
|
||||
|
||||
|
|
@ -299,9 +299,9 @@ static void tick_shutdown(unsigned int *cpup)
|
|||
}
|
||||
/* Transfer the do_timer job away from this cpu */
|
||||
if (*cpup == tick_do_timer_cpu) {
|
||||
int cpu = first_cpu(cpu_online_map);
|
||||
int cpu = cpumask_first(cpu_online_mask);
|
||||
|
||||
tick_do_timer_cpu = (cpu != NR_CPUS) ? cpu :
|
||||
tick_do_timer_cpu = (cpu < nr_cpu_ids) ? cpu :
|
||||
TICK_DO_TIMER_NONE;
|
||||
}
|
||||
spin_unlock_irqrestore(&tick_device_lock, flags);
|
||||
|
|
|
|||
|
|
@ -144,7 +144,7 @@ void tick_nohz_update_jiffies(void)
|
|||
if (!ts->tick_stopped)
|
||||
return;
|
||||
|
||||
cpu_clear(cpu, nohz_cpu_mask);
|
||||
cpumask_clear_cpu(cpu, nohz_cpu_mask);
|
||||
now = ktime_get();
|
||||
ts->idle_waketime = now;
|
||||
|
||||
|
|
@ -247,7 +247,7 @@ void tick_nohz_stop_sched_tick(int inidle)
|
|||
if (need_resched())
|
||||
goto end;
|
||||
|
||||
if (unlikely(local_softirq_pending())) {
|
||||
if (unlikely(local_softirq_pending() && cpu_online(cpu))) {
|
||||
static int ratelimit;
|
||||
|
||||
if (ratelimit < 10) {
|
||||
|
|
@ -282,8 +282,31 @@ void tick_nohz_stop_sched_tick(int inidle)
|
|||
/* Schedule the tick, if we are at least one jiffie off */
|
||||
if ((long)delta_jiffies >= 1) {
|
||||
|
||||
/*
|
||||
* calculate the expiry time for the next timer wheel
|
||||
* timer
|
||||
*/
|
||||
expires = ktime_add_ns(last_update, tick_period.tv64 *
|
||||
delta_jiffies);
|
||||
|
||||
/*
|
||||
* If this cpu is the one which updates jiffies, then
|
||||
* give up the assignment and let it be taken by the
|
||||
* cpu which runs the tick timer next, which might be
|
||||
* this cpu as well. If we don't drop this here the
|
||||
* jiffies might be stale and do_timer() never
|
||||
* invoked.
|
||||
*/
|
||||
if (cpu == tick_do_timer_cpu)
|
||||
tick_do_timer_cpu = TICK_DO_TIMER_NONE;
|
||||
|
||||
if (delta_jiffies > 1)
|
||||
cpu_set(cpu, nohz_cpu_mask);
|
||||
cpumask_set_cpu(cpu, nohz_cpu_mask);
|
||||
|
||||
/* Skip reprogram of event if its not changed */
|
||||
if (ts->tick_stopped && ktime_equal(expires, dev->next_event))
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* nohz_stop_sched_tick can be called several times before
|
||||
* the nohz_restart_sched_tick is called. This happens when
|
||||
|
|
@ -296,7 +319,7 @@ void tick_nohz_stop_sched_tick(int inidle)
|
|||
/*
|
||||
* sched tick not stopped!
|
||||
*/
|
||||
cpu_clear(cpu, nohz_cpu_mask);
|
||||
cpumask_clear_cpu(cpu, nohz_cpu_mask);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
|
@ -306,17 +329,6 @@ void tick_nohz_stop_sched_tick(int inidle)
|
|||
rcu_enter_nohz();
|
||||
}
|
||||
|
||||
/*
|
||||
* If this cpu is the one which updates jiffies, then
|
||||
* give up the assignment and let it be taken by the
|
||||
* cpu which runs the tick timer next, which might be
|
||||
* this cpu as well. If we don't drop this here the
|
||||
* jiffies might be stale and do_timer() never
|
||||
* invoked.
|
||||
*/
|
||||
if (cpu == tick_do_timer_cpu)
|
||||
tick_do_timer_cpu = TICK_DO_TIMER_NONE;
|
||||
|
||||
ts->idle_sleeps++;
|
||||
|
||||
/*
|
||||
|
|
@ -332,12 +344,7 @@ void tick_nohz_stop_sched_tick(int inidle)
|
|||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* calculate the expiry time for the next timer wheel
|
||||
* timer
|
||||
*/
|
||||
expires = ktime_add_ns(last_update, tick_period.tv64 *
|
||||
delta_jiffies);
|
||||
/* Mark expiries */
|
||||
ts->idle_expires = expires;
|
||||
|
||||
if (ts->nohz_mode == NOHZ_MODE_HIGHRES) {
|
||||
|
|
@ -354,7 +361,7 @@ void tick_nohz_stop_sched_tick(int inidle)
|
|||
* softirq.
|
||||
*/
|
||||
tick_do_update_jiffies64(ktime_get());
|
||||
cpu_clear(cpu, nohz_cpu_mask);
|
||||
cpumask_clear_cpu(cpu, nohz_cpu_mask);
|
||||
}
|
||||
raise_softirq_irqoff(TIMER_SOFTIRQ);
|
||||
out:
|
||||
|
|
@ -412,7 +419,9 @@ void tick_nohz_restart_sched_tick(void)
|
|||
{
|
||||
int cpu = smp_processor_id();
|
||||
struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu);
|
||||
#ifndef CONFIG_VIRT_CPU_ACCOUNTING
|
||||
unsigned long ticks;
|
||||
#endif
|
||||
ktime_t now;
|
||||
|
||||
local_irq_disable();
|
||||
|
|
@ -432,8 +441,9 @@ void tick_nohz_restart_sched_tick(void)
|
|||
select_nohz_load_balancer(0);
|
||||
now = ktime_get();
|
||||
tick_do_update_jiffies64(now);
|
||||
cpu_clear(cpu, nohz_cpu_mask);
|
||||
cpumask_clear_cpu(cpu, nohz_cpu_mask);
|
||||
|
||||
#ifndef CONFIG_VIRT_CPU_ACCOUNTING
|
||||
/*
|
||||
* We stopped the tick in idle. Update process times would miss the
|
||||
* time we slept as update_process_times does only a 1 tick
|
||||
|
|
@ -443,12 +453,9 @@ void tick_nohz_restart_sched_tick(void)
|
|||
/*
|
||||
* We might be one off. Do not randomly account a huge number of ticks!
|
||||
*/
|
||||
if (ticks && ticks < LONG_MAX) {
|
||||
add_preempt_count(HARDIRQ_OFFSET);
|
||||
account_system_time(current, HARDIRQ_OFFSET,
|
||||
jiffies_to_cputime(ticks));
|
||||
sub_preempt_count(HARDIRQ_OFFSET);
|
||||
}
|
||||
if (ticks && ticks < LONG_MAX)
|
||||
account_idle_ticks(ticks);
|
||||
#endif
|
||||
|
||||
touch_softlockup_watchdog();
|
||||
/*
|
||||
|
|
@ -681,7 +688,6 @@ void tick_setup_sched_timer(void)
|
|||
*/
|
||||
hrtimer_init(&ts->sched_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
|
||||
ts->sched_timer.function = tick_sched_timer;
|
||||
ts->sched_timer.cb_mode = HRTIMER_CB_IRQSAFE_PERCPU;
|
||||
|
||||
/* Get the next period (per cpu) */
|
||||
hrtimer_set_expires(&ts->sched_timer, tick_init_jiffy_update());
|
||||
|
|
|
|||
|
|
@ -46,6 +46,9 @@ struct timespec xtime __attribute__ ((aligned (16)));
|
|||
struct timespec wall_to_monotonic __attribute__ ((aligned (16)));
|
||||
static unsigned long total_sleep_time; /* seconds */
|
||||
|
||||
/* flag for if timekeeping is suspended */
|
||||
int __read_mostly timekeeping_suspended;
|
||||
|
||||
static struct timespec xtime_cache __attribute__ ((aligned (16)));
|
||||
void update_xtime_cache(u64 nsec)
|
||||
{
|
||||
|
|
@ -92,6 +95,8 @@ void getnstimeofday(struct timespec *ts)
|
|||
unsigned long seq;
|
||||
s64 nsecs;
|
||||
|
||||
WARN_ON(timekeeping_suspended);
|
||||
|
||||
do {
|
||||
seq = read_seqbegin(&xtime_lock);
|
||||
|
||||
|
|
@ -299,8 +304,6 @@ void __init timekeeping_init(void)
|
|||
write_sequnlock_irqrestore(&xtime_lock, flags);
|
||||
}
|
||||
|
||||
/* flag for if timekeeping is suspended */
|
||||
static int timekeeping_suspended;
|
||||
/* time in seconds when suspend began */
|
||||
static unsigned long timekeeping_suspend_time;
|
||||
|
||||
|
|
|
|||
|
|
@ -1018,21 +1018,6 @@ unsigned long get_next_timer_interrupt(unsigned long now)
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_VIRT_CPU_ACCOUNTING
|
||||
void account_process_tick(struct task_struct *p, int user_tick)
|
||||
{
|
||||
cputime_t one_jiffy = jiffies_to_cputime(1);
|
||||
|
||||
if (user_tick) {
|
||||
account_user_time(p, one_jiffy);
|
||||
account_user_time_scaled(p, cputime_to_scaled(one_jiffy));
|
||||
} else {
|
||||
account_system_time(p, HARDIRQ_OFFSET, one_jiffy);
|
||||
account_system_time_scaled(p, cputime_to_scaled(one_jiffy));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Called from the timer interrupt handler to charge one tick to the current
|
||||
* process. user_tick is 1 if the tick is user time, 0 for system.
|
||||
|
|
@ -1144,7 +1129,7 @@ void do_timer(unsigned long ticks)
|
|||
* For backwards compatibility? This can be done in libc so Alpha
|
||||
* and all newer ports shouldn't need it.
|
||||
*/
|
||||
asmlinkage unsigned long sys_alarm(unsigned int seconds)
|
||||
SYSCALL_DEFINE1(alarm, unsigned int, seconds)
|
||||
{
|
||||
return alarm_setitimer(seconds);
|
||||
}
|
||||
|
|
@ -1167,7 +1152,7 @@ asmlinkage unsigned long sys_alarm(unsigned int seconds)
|
|||
*
|
||||
* This is SMP safe as current->tgid does not change.
|
||||
*/
|
||||
asmlinkage long sys_getpid(void)
|
||||
SYSCALL_DEFINE0(getpid)
|
||||
{
|
||||
return task_tgid_vnr(current);
|
||||
}
|
||||
|
|
@ -1178,7 +1163,7 @@ asmlinkage long sys_getpid(void)
|
|||
* value of ->real_parent under rcu_read_lock(), see
|
||||
* release_task()->call_rcu(delayed_put_task_struct).
|
||||
*/
|
||||
asmlinkage long sys_getppid(void)
|
||||
SYSCALL_DEFINE0(getppid)
|
||||
{
|
||||
int pid;
|
||||
|
||||
|
|
@ -1189,28 +1174,28 @@ asmlinkage long sys_getppid(void)
|
|||
return pid;
|
||||
}
|
||||
|
||||
asmlinkage long sys_getuid(void)
|
||||
SYSCALL_DEFINE0(getuid)
|
||||
{
|
||||
/* Only we change this so SMP safe */
|
||||
return current->uid;
|
||||
return current_uid();
|
||||
}
|
||||
|
||||
asmlinkage long sys_geteuid(void)
|
||||
SYSCALL_DEFINE0(geteuid)
|
||||
{
|
||||
/* Only we change this so SMP safe */
|
||||
return current->euid;
|
||||
return current_euid();
|
||||
}
|
||||
|
||||
asmlinkage long sys_getgid(void)
|
||||
SYSCALL_DEFINE0(getgid)
|
||||
{
|
||||
/* Only we change this so SMP safe */
|
||||
return current->gid;
|
||||
return current_gid();
|
||||
}
|
||||
|
||||
asmlinkage long sys_getegid(void)
|
||||
SYSCALL_DEFINE0(getegid)
|
||||
{
|
||||
/* Only we change this so SMP safe */
|
||||
return current->egid;
|
||||
return current_egid();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1323,7 +1308,7 @@ signed long __sched schedule_timeout_uninterruptible(signed long timeout)
|
|||
EXPORT_SYMBOL(schedule_timeout_uninterruptible);
|
||||
|
||||
/* Thread ID - the internal kernel "pid" */
|
||||
asmlinkage long sys_gettid(void)
|
||||
SYSCALL_DEFINE0(gettid)
|
||||
{
|
||||
return task_pid_vnr(current);
|
||||
}
|
||||
|
|
@ -1415,7 +1400,7 @@ out:
|
|||
return 0;
|
||||
}
|
||||
|
||||
asmlinkage long sys_sysinfo(struct sysinfo __user *info)
|
||||
SYSCALL_DEFINE1(sysinfo, struct sysinfo __user *, info)
|
||||
{
|
||||
struct sysinfo val;
|
||||
|
||||
|
|
|
|||
|
|
@ -3,18 +3,34 @@
|
|||
# select HAVE_FUNCTION_TRACER:
|
||||
#
|
||||
|
||||
config USER_STACKTRACE_SUPPORT
|
||||
bool
|
||||
|
||||
config NOP_TRACER
|
||||
bool
|
||||
|
||||
config HAVE_FUNCTION_TRACER
|
||||
bool
|
||||
|
||||
config HAVE_FUNCTION_GRAPH_TRACER
|
||||
bool
|
||||
|
||||
config HAVE_FUNCTION_TRACE_MCOUNT_TEST
|
||||
bool
|
||||
help
|
||||
This gets selected when the arch tests the function_trace_stop
|
||||
variable at the mcount call site. Otherwise, this variable
|
||||
is tested by the called function.
|
||||
|
||||
config HAVE_DYNAMIC_FTRACE
|
||||
bool
|
||||
|
||||
config HAVE_FTRACE_MCOUNT_RECORD
|
||||
bool
|
||||
|
||||
config HAVE_HW_BRANCH_TRACER
|
||||
bool
|
||||
|
||||
config TRACER_MAX_TRACE
|
||||
bool
|
||||
|
||||
|
|
@ -47,6 +63,20 @@ config FUNCTION_TRACER
|
|||
(the bootup default), then the overhead of the instructions is very
|
||||
small and not measurable even in micro-benchmarks.
|
||||
|
||||
config FUNCTION_GRAPH_TRACER
|
||||
bool "Kernel Function Graph Tracer"
|
||||
depends on HAVE_FUNCTION_GRAPH_TRACER
|
||||
depends on FUNCTION_TRACER
|
||||
default y
|
||||
help
|
||||
Enable the kernel to trace a function at both its return
|
||||
and its entry.
|
||||
It's first purpose is to trace the duration of functions and
|
||||
draw a call graph for each thread with some informations like
|
||||
the return value.
|
||||
This is done by setting the current return address on the current
|
||||
task structure into a stack of calls.
|
||||
|
||||
config IRQSOFF_TRACER
|
||||
bool "Interrupts-off Latency Tracer"
|
||||
default n
|
||||
|
|
@ -138,6 +168,70 @@ config BOOT_TRACER
|
|||
selected, because the self-tests are an initcall as well and that
|
||||
would invalidate the boot trace. )
|
||||
|
||||
config TRACE_BRANCH_PROFILING
|
||||
bool "Trace likely/unlikely profiler"
|
||||
depends on DEBUG_KERNEL
|
||||
select TRACING
|
||||
help
|
||||
This tracer profiles all the the likely and unlikely macros
|
||||
in the kernel. It will display the results in:
|
||||
|
||||
/debugfs/tracing/profile_annotated_branch
|
||||
|
||||
Note: this will add a significant overhead, only turn this
|
||||
on if you need to profile the system's use of these macros.
|
||||
|
||||
Say N if unsure.
|
||||
|
||||
config PROFILE_ALL_BRANCHES
|
||||
bool "Profile all if conditionals"
|
||||
depends on TRACE_BRANCH_PROFILING
|
||||
help
|
||||
This tracer profiles all branch conditions. Every if ()
|
||||
taken in the kernel is recorded whether it hit or miss.
|
||||
The results will be displayed in:
|
||||
|
||||
/debugfs/tracing/profile_branch
|
||||
|
||||
This configuration, when enabled, will impose a great overhead
|
||||
on the system. This should only be enabled when the system
|
||||
is to be analyzed
|
||||
|
||||
Say N if unsure.
|
||||
|
||||
config TRACING_BRANCHES
|
||||
bool
|
||||
help
|
||||
Selected by tracers that will trace the likely and unlikely
|
||||
conditions. This prevents the tracers themselves from being
|
||||
profiled. Profiling the tracing infrastructure can only happen
|
||||
when the likelys and unlikelys are not being traced.
|
||||
|
||||
config BRANCH_TRACER
|
||||
bool "Trace likely/unlikely instances"
|
||||
depends on TRACE_BRANCH_PROFILING
|
||||
select TRACING_BRANCHES
|
||||
help
|
||||
This traces the events of likely and unlikely condition
|
||||
calls in the kernel. The difference between this and the
|
||||
"Trace likely/unlikely profiler" is that this is not a
|
||||
histogram of the callers, but actually places the calling
|
||||
events into a running trace buffer to see when and where the
|
||||
events happened, as well as their results.
|
||||
|
||||
Say N if unsure.
|
||||
|
||||
config POWER_TRACER
|
||||
bool "Trace power consumption behavior"
|
||||
depends on DEBUG_KERNEL
|
||||
depends on X86
|
||||
select TRACING
|
||||
help
|
||||
This tracer helps developers to analyze and optimize the kernels
|
||||
power management decisions, specifically the C-state and P-state
|
||||
behavior.
|
||||
|
||||
|
||||
config STACK_TRACER
|
||||
bool "Trace max stack"
|
||||
depends on HAVE_FUNCTION_TRACER
|
||||
|
|
@ -150,13 +244,26 @@ config STACK_TRACER
|
|||
|
||||
This tracer works by hooking into every function call that the
|
||||
kernel executes, and keeping a maximum stack depth value and
|
||||
stack-trace saved. Because this logic has to execute in every
|
||||
kernel function, all the time, this option can slow down the
|
||||
kernel measurably and is generally intended for kernel
|
||||
developers only.
|
||||
stack-trace saved. If this is configured with DYNAMIC_FTRACE
|
||||
then it will not have any overhead while the stack tracer
|
||||
is disabled.
|
||||
|
||||
To enable the stack tracer on bootup, pass in 'stacktrace'
|
||||
on the kernel command line.
|
||||
|
||||
The stack tracer can also be enabled or disabled via the
|
||||
sysctl kernel.stack_tracer_enabled
|
||||
|
||||
Say N if unsure.
|
||||
|
||||
config HW_BRANCH_TRACER
|
||||
depends on HAVE_HW_BRANCH_TRACER
|
||||
bool "Trace hw branches"
|
||||
select TRACING
|
||||
help
|
||||
This tracer records all branches on the system in a circular
|
||||
buffer giving access to the last N branches for each cpu.
|
||||
|
||||
config DYNAMIC_FTRACE
|
||||
bool "enable/disable ftrace tracepoints dynamically"
|
||||
depends on FUNCTION_TRACER
|
||||
|
|
|
|||
|
|
@ -10,6 +10,11 @@ CFLAGS_trace_selftest_dynamic.o = -pg
|
|||
obj-y += trace_selftest_dynamic.o
|
||||
endif
|
||||
|
||||
# If unlikely tracing is enabled, do not trace these files
|
||||
ifdef CONFIG_TRACING_BRANCHES
|
||||
KBUILD_CFLAGS += -DDISABLE_BRANCH_PROFILING
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_FUNCTION_TRACER) += libftrace.o
|
||||
obj-$(CONFIG_RING_BUFFER) += ring_buffer.o
|
||||
|
||||
|
|
@ -24,5 +29,9 @@ obj-$(CONFIG_NOP_TRACER) += trace_nop.o
|
|||
obj-$(CONFIG_STACK_TRACER) += trace_stack.o
|
||||
obj-$(CONFIG_MMIOTRACE) += trace_mmiotrace.o
|
||||
obj-$(CONFIG_BOOT_TRACER) += trace_boot.o
|
||||
obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += trace_functions_graph.o
|
||||
obj-$(CONFIG_TRACE_BRANCH_PROFILING) += trace_branch.o
|
||||
obj-$(CONFIG_HW_BRANCH_TRACER) += trace_hw_branches.o
|
||||
obj-$(CONFIG_POWER_TRACER) += trace_power.o
|
||||
|
||||
libftrace-y := ftrace.o
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue