Merge branch 'master' into for-linus
This commit is contained in:
@@ -80,11 +80,9 @@ obj-$(CONFIG_DETECT_HUNG_TASK) += hung_task.o
|
||||
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
|
||||
obj-$(CONFIG_TREE_PREEMPT_RCU) += rcutree.o
|
||||
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
|
||||
|
@@ -491,13 +491,17 @@ static void do_acct_process(struct bsd_acct_struct *acct,
|
||||
u64 run_time;
|
||||
struct timespec uptime;
|
||||
struct tty_struct *tty;
|
||||
const struct cred *orig_cred;
|
||||
|
||||
/* Perform file operations on behalf of whoever enabled accounting */
|
||||
orig_cred = override_creds(file->f_cred);
|
||||
|
||||
/*
|
||||
* First check to see if there is enough free_space to continue
|
||||
* the process accounting system.
|
||||
*/
|
||||
if (!check_free_space(acct, file))
|
||||
return;
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Fill the accounting struct with the needed info as recorded
|
||||
@@ -578,6 +582,8 @@ static void do_acct_process(struct bsd_acct_struct *acct,
|
||||
sizeof(acct_t), &file->f_pos);
|
||||
current->signal->rlim[RLIMIT_FSIZE].rlim_cur = flim;
|
||||
set_fs(fs);
|
||||
out:
|
||||
revert_creds(orig_cred);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -600,6 +600,7 @@ static struct inode_operations cgroup_dir_inode_operations;
|
||||
static struct file_operations proc_cgroupstats_operations;
|
||||
|
||||
static struct backing_dev_info cgroup_backing_dev_info = {
|
||||
.name = "cgroup",
|
||||
.capabilities = BDI_CAP_NO_ACCT_AND_WRITEBACK,
|
||||
};
|
||||
|
||||
|
293
kernel/cred.c
293
kernel/cred.c
@@ -18,6 +18,18 @@
|
||||
#include <linux/cn_proc.h>
|
||||
#include "cred-internals.h"
|
||||
|
||||
#if 0
|
||||
#define kdebug(FMT, ...) \
|
||||
printk("[%-5.5s%5u] "FMT"\n", current->comm, current->pid ,##__VA_ARGS__)
|
||||
#else
|
||||
static inline __attribute__((format(printf, 1, 2)))
|
||||
void no_printk(const char *fmt, ...)
|
||||
{
|
||||
}
|
||||
#define kdebug(FMT, ...) \
|
||||
no_printk("[%-5.5s%5u] "FMT"\n", current->comm, current->pid ,##__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
static struct kmem_cache *cred_jar;
|
||||
|
||||
/*
|
||||
@@ -36,6 +48,10 @@ static struct thread_group_cred init_tgcred = {
|
||||
*/
|
||||
struct cred init_cred = {
|
||||
.usage = ATOMIC_INIT(4),
|
||||
#ifdef CONFIG_DEBUG_CREDENTIALS
|
||||
.subscribers = ATOMIC_INIT(2),
|
||||
.magic = CRED_MAGIC,
|
||||
#endif
|
||||
.securebits = SECUREBITS_DEFAULT,
|
||||
.cap_inheritable = CAP_INIT_INH_SET,
|
||||
.cap_permitted = CAP_FULL_SET,
|
||||
@@ -48,6 +64,31 @@ struct cred init_cred = {
|
||||
#endif
|
||||
};
|
||||
|
||||
static inline void set_cred_subscribers(struct cred *cred, int n)
|
||||
{
|
||||
#ifdef CONFIG_DEBUG_CREDENTIALS
|
||||
atomic_set(&cred->subscribers, n);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int read_cred_subscribers(const struct cred *cred)
|
||||
{
|
||||
#ifdef CONFIG_DEBUG_CREDENTIALS
|
||||
return atomic_read(&cred->subscribers);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void alter_cred_subscribers(const struct cred *_cred, int n)
|
||||
{
|
||||
#ifdef CONFIG_DEBUG_CREDENTIALS
|
||||
struct cred *cred = (struct cred *) _cred;
|
||||
|
||||
atomic_add(n, &cred->subscribers);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Dispose of the shared task group credentials
|
||||
*/
|
||||
@@ -85,9 +126,22 @@ static void put_cred_rcu(struct rcu_head *rcu)
|
||||
{
|
||||
struct cred *cred = container_of(rcu, struct cred, rcu);
|
||||
|
||||
kdebug("put_cred_rcu(%p)", cred);
|
||||
|
||||
#ifdef CONFIG_DEBUG_CREDENTIALS
|
||||
if (cred->magic != CRED_MAGIC_DEAD ||
|
||||
atomic_read(&cred->usage) != 0 ||
|
||||
read_cred_subscribers(cred) != 0)
|
||||
panic("CRED: put_cred_rcu() sees %p with"
|
||||
" mag %x, put %p, usage %d, subscr %d\n",
|
||||
cred, cred->magic, cred->put_addr,
|
||||
atomic_read(&cred->usage),
|
||||
read_cred_subscribers(cred));
|
||||
#else
|
||||
if (atomic_read(&cred->usage) != 0)
|
||||
panic("CRED: put_cred_rcu() sees %p with usage %d\n",
|
||||
cred, atomic_read(&cred->usage));
|
||||
#endif
|
||||
|
||||
security_cred_free(cred);
|
||||
key_put(cred->thread_keyring);
|
||||
@@ -106,12 +160,90 @@ static void put_cred_rcu(struct rcu_head *rcu)
|
||||
*/
|
||||
void __put_cred(struct cred *cred)
|
||||
{
|
||||
kdebug("__put_cred(%p{%d,%d})", cred,
|
||||
atomic_read(&cred->usage),
|
||||
read_cred_subscribers(cred));
|
||||
|
||||
BUG_ON(atomic_read(&cred->usage) != 0);
|
||||
#ifdef CONFIG_DEBUG_CREDENTIALS
|
||||
BUG_ON(read_cred_subscribers(cred) != 0);
|
||||
cred->magic = CRED_MAGIC_DEAD;
|
||||
cred->put_addr = __builtin_return_address(0);
|
||||
#endif
|
||||
BUG_ON(cred == current->cred);
|
||||
BUG_ON(cred == current->real_cred);
|
||||
|
||||
call_rcu(&cred->rcu, put_cred_rcu);
|
||||
}
|
||||
EXPORT_SYMBOL(__put_cred);
|
||||
|
||||
/*
|
||||
* Clean up a task's credentials when it exits
|
||||
*/
|
||||
void exit_creds(struct task_struct *tsk)
|
||||
{
|
||||
struct cred *cred;
|
||||
|
||||
kdebug("exit_creds(%u,%p,%p,{%d,%d})", tsk->pid, tsk->real_cred, tsk->cred,
|
||||
atomic_read(&tsk->cred->usage),
|
||||
read_cred_subscribers(tsk->cred));
|
||||
|
||||
cred = (struct cred *) tsk->real_cred;
|
||||
tsk->real_cred = NULL;
|
||||
validate_creds(cred);
|
||||
alter_cred_subscribers(cred, -1);
|
||||
put_cred(cred);
|
||||
|
||||
cred = (struct cred *) tsk->cred;
|
||||
tsk->cred = NULL;
|
||||
validate_creds(cred);
|
||||
alter_cred_subscribers(cred, -1);
|
||||
put_cred(cred);
|
||||
|
||||
cred = (struct cred *) tsk->replacement_session_keyring;
|
||||
if (cred) {
|
||||
tsk->replacement_session_keyring = NULL;
|
||||
validate_creds(cred);
|
||||
put_cred(cred);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate blank credentials, such that the credentials can be filled in at a
|
||||
* later date without risk of ENOMEM.
|
||||
*/
|
||||
struct cred *cred_alloc_blank(void)
|
||||
{
|
||||
struct cred *new;
|
||||
|
||||
new = kmem_cache_zalloc(cred_jar, GFP_KERNEL);
|
||||
if (!new)
|
||||
return NULL;
|
||||
|
||||
#ifdef CONFIG_KEYS
|
||||
new->tgcred = kzalloc(sizeof(*new->tgcred), GFP_KERNEL);
|
||||
if (!new->tgcred) {
|
||||
kfree(new);
|
||||
return NULL;
|
||||
}
|
||||
atomic_set(&new->tgcred->usage, 1);
|
||||
#endif
|
||||
|
||||
atomic_set(&new->usage, 1);
|
||||
|
||||
if (security_cred_alloc_blank(new, GFP_KERNEL) < 0)
|
||||
goto error;
|
||||
|
||||
#ifdef CONFIG_DEBUG_CREDENTIALS
|
||||
new->magic = CRED_MAGIC;
|
||||
#endif
|
||||
return new;
|
||||
|
||||
error:
|
||||
abort_creds(new);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* prepare_creds - Prepare a new set of credentials for modification
|
||||
*
|
||||
@@ -132,16 +264,19 @@ struct cred *prepare_creds(void)
|
||||
const struct cred *old;
|
||||
struct cred *new;
|
||||
|
||||
BUG_ON(atomic_read(&task->real_cred->usage) < 1);
|
||||
validate_process_creds();
|
||||
|
||||
new = kmem_cache_alloc(cred_jar, GFP_KERNEL);
|
||||
if (!new)
|
||||
return NULL;
|
||||
|
||||
kdebug("prepare_creds() alloc %p", new);
|
||||
|
||||
old = task->cred;
|
||||
memcpy(new, old, sizeof(struct cred));
|
||||
|
||||
atomic_set(&new->usage, 1);
|
||||
set_cred_subscribers(new, 0);
|
||||
get_group_info(new->group_info);
|
||||
get_uid(new->user);
|
||||
|
||||
@@ -157,6 +292,7 @@ struct cred *prepare_creds(void)
|
||||
|
||||
if (security_prepare_creds(new, old, GFP_KERNEL) < 0)
|
||||
goto error;
|
||||
validate_creds(new);
|
||||
return new;
|
||||
|
||||
error:
|
||||
@@ -229,9 +365,12 @@ struct cred *prepare_usermodehelper_creds(void)
|
||||
if (!new)
|
||||
return NULL;
|
||||
|
||||
kdebug("prepare_usermodehelper_creds() alloc %p", new);
|
||||
|
||||
memcpy(new, &init_cred, sizeof(struct cred));
|
||||
|
||||
atomic_set(&new->usage, 1);
|
||||
set_cred_subscribers(new, 0);
|
||||
get_group_info(new->group_info);
|
||||
get_uid(new->user);
|
||||
|
||||
@@ -250,6 +389,7 @@ struct cred *prepare_usermodehelper_creds(void)
|
||||
#endif
|
||||
if (security_prepare_creds(new, &init_cred, GFP_ATOMIC) < 0)
|
||||
goto error;
|
||||
validate_creds(new);
|
||||
|
||||
BUG_ON(atomic_read(&new->usage) != 1);
|
||||
return new;
|
||||
@@ -286,6 +426,10 @@ int copy_creds(struct task_struct *p, unsigned long clone_flags)
|
||||
) {
|
||||
p->real_cred = get_cred(p->cred);
|
||||
get_cred(p->cred);
|
||||
alter_cred_subscribers(p->cred, 2);
|
||||
kdebug("share_creds(%p{%d,%d})",
|
||||
p->cred, atomic_read(&p->cred->usage),
|
||||
read_cred_subscribers(p->cred));
|
||||
atomic_inc(&p->cred->user->processes);
|
||||
return 0;
|
||||
}
|
||||
@@ -331,6 +475,8 @@ int copy_creds(struct task_struct *p, unsigned long clone_flags)
|
||||
|
||||
atomic_inc(&new->user->processes);
|
||||
p->cred = p->real_cred = get_cred(new);
|
||||
alter_cred_subscribers(new, 2);
|
||||
validate_creds(new);
|
||||
return 0;
|
||||
|
||||
error_put:
|
||||
@@ -355,13 +501,20 @@ error_put:
|
||||
int commit_creds(struct cred *new)
|
||||
{
|
||||
struct task_struct *task = current;
|
||||
const struct cred *old;
|
||||
const struct cred *old = task->real_cred;
|
||||
|
||||
BUG_ON(task->cred != task->real_cred);
|
||||
BUG_ON(atomic_read(&task->real_cred->usage) < 2);
|
||||
kdebug("commit_creds(%p{%d,%d})", new,
|
||||
atomic_read(&new->usage),
|
||||
read_cred_subscribers(new));
|
||||
|
||||
BUG_ON(task->cred != old);
|
||||
#ifdef CONFIG_DEBUG_CREDENTIALS
|
||||
BUG_ON(read_cred_subscribers(old) < 2);
|
||||
validate_creds(old);
|
||||
validate_creds(new);
|
||||
#endif
|
||||
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 */
|
||||
@@ -390,12 +543,14 @@ int commit_creds(struct cred *new)
|
||||
* cheaply with the new uid cache, so if it matters
|
||||
* we should be checking for it. -DaveM
|
||||
*/
|
||||
alter_cred_subscribers(new, 2);
|
||||
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);
|
||||
alter_cred_subscribers(old, -2);
|
||||
|
||||
sched_switch_user(task);
|
||||
|
||||
@@ -428,6 +583,13 @@ EXPORT_SYMBOL(commit_creds);
|
||||
*/
|
||||
void abort_creds(struct cred *new)
|
||||
{
|
||||
kdebug("abort_creds(%p{%d,%d})", new,
|
||||
atomic_read(&new->usage),
|
||||
read_cred_subscribers(new));
|
||||
|
||||
#ifdef CONFIG_DEBUG_CREDENTIALS
|
||||
BUG_ON(read_cred_subscribers(new) != 0);
|
||||
#endif
|
||||
BUG_ON(atomic_read(&new->usage) < 1);
|
||||
put_cred(new);
|
||||
}
|
||||
@@ -444,7 +606,20 @@ const struct cred *override_creds(const struct cred *new)
|
||||
{
|
||||
const struct cred *old = current->cred;
|
||||
|
||||
rcu_assign_pointer(current->cred, get_cred(new));
|
||||
kdebug("override_creds(%p{%d,%d})", new,
|
||||
atomic_read(&new->usage),
|
||||
read_cred_subscribers(new));
|
||||
|
||||
validate_creds(old);
|
||||
validate_creds(new);
|
||||
get_cred(new);
|
||||
alter_cred_subscribers(new, 1);
|
||||
rcu_assign_pointer(current->cred, new);
|
||||
alter_cred_subscribers(old, -1);
|
||||
|
||||
kdebug("override_creds() = %p{%d,%d}", old,
|
||||
atomic_read(&old->usage),
|
||||
read_cred_subscribers(old));
|
||||
return old;
|
||||
}
|
||||
EXPORT_SYMBOL(override_creds);
|
||||
@@ -460,7 +635,15 @@ void revert_creds(const struct cred *old)
|
||||
{
|
||||
const struct cred *override = current->cred;
|
||||
|
||||
kdebug("revert_creds(%p{%d,%d})", old,
|
||||
atomic_read(&old->usage),
|
||||
read_cred_subscribers(old));
|
||||
|
||||
validate_creds(old);
|
||||
validate_creds(override);
|
||||
alter_cred_subscribers(old, 1);
|
||||
rcu_assign_pointer(current->cred, old);
|
||||
alter_cred_subscribers(override, -1);
|
||||
put_cred(override);
|
||||
}
|
||||
EXPORT_SYMBOL(revert_creds);
|
||||
@@ -502,11 +685,15 @@ struct cred *prepare_kernel_cred(struct task_struct *daemon)
|
||||
if (!new)
|
||||
return NULL;
|
||||
|
||||
kdebug("prepare_kernel_cred() alloc %p", new);
|
||||
|
||||
if (daemon)
|
||||
old = get_task_cred(daemon);
|
||||
else
|
||||
old = get_cred(&init_cred);
|
||||
|
||||
validate_creds(old);
|
||||
|
||||
*new = *old;
|
||||
get_uid(new->user);
|
||||
get_group_info(new->group_info);
|
||||
@@ -526,7 +713,9 @@ struct cred *prepare_kernel_cred(struct task_struct *daemon)
|
||||
goto error;
|
||||
|
||||
atomic_set(&new->usage, 1);
|
||||
set_cred_subscribers(new, 0);
|
||||
put_cred(old);
|
||||
validate_creds(new);
|
||||
return new;
|
||||
|
||||
error:
|
||||
@@ -589,3 +778,95 @@ int set_create_files_as(struct cred *new, struct inode *inode)
|
||||
return security_kernel_create_files_as(new, inode);
|
||||
}
|
||||
EXPORT_SYMBOL(set_create_files_as);
|
||||
|
||||
#ifdef CONFIG_DEBUG_CREDENTIALS
|
||||
|
||||
/*
|
||||
* dump invalid credentials
|
||||
*/
|
||||
static void dump_invalid_creds(const struct cred *cred, const char *label,
|
||||
const struct task_struct *tsk)
|
||||
{
|
||||
printk(KERN_ERR "CRED: %s credentials: %p %s%s%s\n",
|
||||
label, cred,
|
||||
cred == &init_cred ? "[init]" : "",
|
||||
cred == tsk->real_cred ? "[real]" : "",
|
||||
cred == tsk->cred ? "[eff]" : "");
|
||||
printk(KERN_ERR "CRED: ->magic=%x, put_addr=%p\n",
|
||||
cred->magic, cred->put_addr);
|
||||
printk(KERN_ERR "CRED: ->usage=%d, subscr=%d\n",
|
||||
atomic_read(&cred->usage),
|
||||
read_cred_subscribers(cred));
|
||||
printk(KERN_ERR "CRED: ->*uid = { %d,%d,%d,%d }\n",
|
||||
cred->uid, cred->euid, cred->suid, cred->fsuid);
|
||||
printk(KERN_ERR "CRED: ->*gid = { %d,%d,%d,%d }\n",
|
||||
cred->gid, cred->egid, cred->sgid, cred->fsgid);
|
||||
#ifdef CONFIG_SECURITY
|
||||
printk(KERN_ERR "CRED: ->security is %p\n", cred->security);
|
||||
if ((unsigned long) cred->security >= PAGE_SIZE &&
|
||||
(((unsigned long) cred->security & 0xffffff00) !=
|
||||
(POISON_FREE << 24 | POISON_FREE << 16 | POISON_FREE << 8)))
|
||||
printk(KERN_ERR "CRED: ->security {%x, %x}\n",
|
||||
((u32*)cred->security)[0],
|
||||
((u32*)cred->security)[1]);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* report use of invalid credentials
|
||||
*/
|
||||
void __invalid_creds(const struct cred *cred, const char *file, unsigned line)
|
||||
{
|
||||
printk(KERN_ERR "CRED: Invalid credentials\n");
|
||||
printk(KERN_ERR "CRED: At %s:%u\n", file, line);
|
||||
dump_invalid_creds(cred, "Specified", current);
|
||||
BUG();
|
||||
}
|
||||
EXPORT_SYMBOL(__invalid_creds);
|
||||
|
||||
/*
|
||||
* check the credentials on a process
|
||||
*/
|
||||
void __validate_process_creds(struct task_struct *tsk,
|
||||
const char *file, unsigned line)
|
||||
{
|
||||
if (tsk->cred == tsk->real_cred) {
|
||||
if (unlikely(read_cred_subscribers(tsk->cred) < 2 ||
|
||||
creds_are_invalid(tsk->cred)))
|
||||
goto invalid_creds;
|
||||
} else {
|
||||
if (unlikely(read_cred_subscribers(tsk->real_cred) < 1 ||
|
||||
read_cred_subscribers(tsk->cred) < 1 ||
|
||||
creds_are_invalid(tsk->real_cred) ||
|
||||
creds_are_invalid(tsk->cred)))
|
||||
goto invalid_creds;
|
||||
}
|
||||
return;
|
||||
|
||||
invalid_creds:
|
||||
printk(KERN_ERR "CRED: Invalid process credentials\n");
|
||||
printk(KERN_ERR "CRED: At %s:%u\n", file, line);
|
||||
|
||||
dump_invalid_creds(tsk->real_cred, "Real", tsk);
|
||||
if (tsk->cred != tsk->real_cred)
|
||||
dump_invalid_creds(tsk->cred, "Effective", tsk);
|
||||
else
|
||||
printk(KERN_ERR "CRED: Effective creds == Real creds\n");
|
||||
BUG();
|
||||
}
|
||||
EXPORT_SYMBOL(__validate_process_creds);
|
||||
|
||||
/*
|
||||
* check creds for do_exit()
|
||||
*/
|
||||
void validate_creds_for_do_exit(struct task_struct *tsk)
|
||||
{
|
||||
kdebug("validate_creds_for_do_exit(%p,%p{%d,%d})",
|
||||
tsk->real_cred, tsk->cred,
|
||||
atomic_read(&tsk->cred->usage),
|
||||
read_cred_subscribers(tsk->cred));
|
||||
|
||||
__validate_process_creds(tsk, __FILE__, __LINE__);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_DEBUG_CREDENTIALS */
|
||||
|
@@ -901,6 +901,8 @@ NORET_TYPE void do_exit(long code)
|
||||
|
||||
tracehook_report_exit(&code);
|
||||
|
||||
validate_creds_for_do_exit(tsk);
|
||||
|
||||
/*
|
||||
* We're taking recursive faults here in do_exit. Safest is to just
|
||||
* leave this task alone and wait for reboot.
|
||||
@@ -1009,7 +1011,10 @@ NORET_TYPE void do_exit(long code)
|
||||
if (tsk->splice_pipe)
|
||||
__free_pipe_info(tsk->splice_pipe);
|
||||
|
||||
validate_creds_for_do_exit(tsk);
|
||||
|
||||
preempt_disable();
|
||||
exit_rcu();
|
||||
/* causes final put_task_struct in finish_task_switch(). */
|
||||
tsk->state = TASK_DEAD;
|
||||
schedule();
|
||||
|
@@ -152,8 +152,7 @@ void __put_task_struct(struct task_struct *tsk)
|
||||
WARN_ON(atomic_read(&tsk->usage));
|
||||
WARN_ON(tsk == current);
|
||||
|
||||
put_cred(tsk->real_cred);
|
||||
put_cred(tsk->cred);
|
||||
exit_creds(tsk);
|
||||
delayacct_tsk_free(tsk);
|
||||
|
||||
if (!profile_handoff_task(tsk))
|
||||
@@ -1008,10 +1007,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
|
||||
copy_flags(clone_flags, p);
|
||||
INIT_LIST_HEAD(&p->children);
|
||||
INIT_LIST_HEAD(&p->sibling);
|
||||
#ifdef CONFIG_PREEMPT_RCU
|
||||
p->rcu_read_lock_nesting = 0;
|
||||
p->rcu_flipctr_idx = 0;
|
||||
#endif /* #ifdef CONFIG_PREEMPT_RCU */
|
||||
rcu_copy_process(p);
|
||||
p->vfork_done = NULL;
|
||||
spin_lock_init(&p->alloc_lock);
|
||||
|
||||
@@ -1297,8 +1293,7 @@ bad_fork_cleanup_put_domain:
|
||||
module_put(task_thread_info(p)->exec_domain->module);
|
||||
bad_fork_cleanup_count:
|
||||
atomic_dec(&p->cred->user->processes);
|
||||
put_cred(p->real_cred);
|
||||
put_cred(p->cred);
|
||||
exit_creds(p);
|
||||
bad_fork_free:
|
||||
free_task(p);
|
||||
fork_out:
|
||||
|
@@ -115,6 +115,9 @@ struct futex_q {
|
||||
/* rt_waiter storage for requeue_pi: */
|
||||
struct rt_mutex_waiter *rt_waiter;
|
||||
|
||||
/* The expected requeue pi target futex key: */
|
||||
union futex_key *requeue_pi_key;
|
||||
|
||||
/* Bitset for the optional bitmasked wakeup */
|
||||
u32 bitset;
|
||||
};
|
||||
@@ -1089,6 +1092,10 @@ static int futex_proxy_trylock_atomic(u32 __user *pifutex,
|
||||
if (!top_waiter)
|
||||
return 0;
|
||||
|
||||
/* Ensure we requeue to the expected futex. */
|
||||
if (!match_futex(top_waiter->requeue_pi_key, key2))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Try to take the lock for top_waiter. Set the FUTEX_WAITERS bit in
|
||||
* the contended case or if set_waiters is 1. The pi_state is returned
|
||||
@@ -1276,6 +1283,12 @@ retry_private:
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Ensure we requeue to the expected futex for requeue_pi. */
|
||||
if (requeue_pi && !match_futex(this->requeue_pi_key, &key2)) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Requeue nr_requeue waiters and possibly one more in the case
|
||||
* of requeue_pi if we couldn't acquire the lock atomically.
|
||||
@@ -1751,6 +1764,7 @@ static int futex_wait(u32 __user *uaddr, int fshared,
|
||||
q.pi_state = NULL;
|
||||
q.bitset = bitset;
|
||||
q.rt_waiter = NULL;
|
||||
q.requeue_pi_key = NULL;
|
||||
|
||||
if (abs_time) {
|
||||
to = &timeout;
|
||||
@@ -1858,6 +1872,7 @@ static int futex_lock_pi(u32 __user *uaddr, int fshared,
|
||||
|
||||
q.pi_state = NULL;
|
||||
q.rt_waiter = NULL;
|
||||
q.requeue_pi_key = NULL;
|
||||
retry:
|
||||
q.key = FUTEX_KEY_INIT;
|
||||
ret = get_futex_key(uaddr, fshared, &q.key, VERIFY_WRITE);
|
||||
@@ -2118,11 +2133,11 @@ int handle_early_requeue_pi_wakeup(struct futex_hash_bucket *hb,
|
||||
* We call schedule in futex_wait_queue_me() when we enqueue and return there
|
||||
* via the following:
|
||||
* 1) wakeup on uaddr2 after an atomic lock acquisition by futex_requeue()
|
||||
* 2) wakeup on uaddr2 after a requeue and subsequent unlock
|
||||
* 3) signal (before or after requeue)
|
||||
* 4) timeout (before or after requeue)
|
||||
* 2) wakeup on uaddr2 after a requeue
|
||||
* 3) signal
|
||||
* 4) timeout
|
||||
*
|
||||
* If 3, we setup a restart_block with futex_wait_requeue_pi() as the function.
|
||||
* If 3, cleanup and return -ERESTARTNOINTR.
|
||||
*
|
||||
* If 2, we may then block on trying to take the rt_mutex and return via:
|
||||
* 5) successful lock
|
||||
@@ -2130,7 +2145,7 @@ int handle_early_requeue_pi_wakeup(struct futex_hash_bucket *hb,
|
||||
* 7) timeout
|
||||
* 8) other lock acquisition failure
|
||||
*
|
||||
* If 6, we setup a restart_block with futex_lock_pi() as the function.
|
||||
* If 6, return -EWOULDBLOCK (restarting the syscall would do the same).
|
||||
*
|
||||
* If 4 or 7, we cleanup and return with -ETIMEDOUT.
|
||||
*
|
||||
@@ -2169,15 +2184,16 @@ static int futex_wait_requeue_pi(u32 __user *uaddr, int fshared,
|
||||
debug_rt_mutex_init_waiter(&rt_waiter);
|
||||
rt_waiter.task = NULL;
|
||||
|
||||
q.pi_state = NULL;
|
||||
q.bitset = bitset;
|
||||
q.rt_waiter = &rt_waiter;
|
||||
|
||||
key2 = FUTEX_KEY_INIT;
|
||||
ret = get_futex_key(uaddr2, fshared, &key2, VERIFY_WRITE);
|
||||
if (unlikely(ret != 0))
|
||||
goto out;
|
||||
|
||||
q.pi_state = NULL;
|
||||
q.bitset = bitset;
|
||||
q.rt_waiter = &rt_waiter;
|
||||
q.requeue_pi_key = &key2;
|
||||
|
||||
/* Prepare to wait on uaddr. */
|
||||
ret = futex_wait_setup(uaddr, val, fshared, &q, &hb);
|
||||
if (ret)
|
||||
@@ -2248,14 +2264,11 @@ static int futex_wait_requeue_pi(u32 __user *uaddr, int fshared,
|
||||
rt_mutex_unlock(pi_mutex);
|
||||
} else if (ret == -EINTR) {
|
||||
/*
|
||||
* We've already been requeued, but we have no way to
|
||||
* restart by calling futex_lock_pi() directly. We
|
||||
* could restart the syscall, but that will look at
|
||||
* the user space value and return right away. So we
|
||||
* drop back with EWOULDBLOCK to tell user space that
|
||||
* "val" has been changed. That's the same what the
|
||||
* restart of the syscall would do in
|
||||
* futex_wait_setup().
|
||||
* We've already been requeued, but cannot restart by calling
|
||||
* futex_lock_pi() directly. We could restart this syscall, but
|
||||
* it would detect that the user space "val" changed and return
|
||||
* -EWOULDBLOCK. Save the overhead of the restart and return
|
||||
* -EWOULDBLOCK directly.
|
||||
*/
|
||||
ret = -EWOULDBLOCK;
|
||||
}
|
||||
|
@@ -485,6 +485,7 @@ void hrtimer_init_on_stack(struct hrtimer *timer, clockid_t clock_id,
|
||||
debug_object_init_on_stack(timer, &hrtimer_debug_descr);
|
||||
__hrtimer_init(timer, clock_id, mode);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hrtimer_init_on_stack);
|
||||
|
||||
void destroy_hrtimer_on_stack(struct hrtimer *timer)
|
||||
{
|
||||
@@ -1477,6 +1478,7 @@ void hrtimer_init_sleeper(struct hrtimer_sleeper *sl, struct task_struct *task)
|
||||
sl->timer.function = hrtimer_wakeup;
|
||||
sl->task = task;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hrtimer_init_sleeper);
|
||||
|
||||
static int __sched do_nanosleep(struct hrtimer_sleeper *t, enum hrtimer_mode mode)
|
||||
{
|
||||
|
@@ -222,6 +222,34 @@ int set_irq_chip_data(unsigned int irq, void *data)
|
||||
}
|
||||
EXPORT_SYMBOL(set_irq_chip_data);
|
||||
|
||||
/**
|
||||
* set_irq_nested_thread - Set/Reset the IRQ_NESTED_THREAD flag of an irq
|
||||
*
|
||||
* @irq: Interrupt number
|
||||
* @nest: 0 to clear / 1 to set the IRQ_NESTED_THREAD flag
|
||||
*
|
||||
* The IRQ_NESTED_THREAD flag indicates that on
|
||||
* request_threaded_irq() no separate interrupt thread should be
|
||||
* created for the irq as the handler are called nested in the
|
||||
* context of a demultiplexing interrupt handler thread.
|
||||
*/
|
||||
void set_irq_nested_thread(unsigned int irq, int nest)
|
||||
{
|
||||
struct irq_desc *desc = irq_to_desc(irq);
|
||||
unsigned long flags;
|
||||
|
||||
if (!desc)
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&desc->lock, flags);
|
||||
if (nest)
|
||||
desc->status |= IRQ_NESTED_THREAD;
|
||||
else
|
||||
desc->status &= ~IRQ_NESTED_THREAD;
|
||||
spin_unlock_irqrestore(&desc->lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(set_irq_nested_thread);
|
||||
|
||||
/*
|
||||
* default enable function
|
||||
*/
|
||||
@@ -299,6 +327,45 @@ static inline void mask_ack_irq(struct irq_desc *desc, int irq)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* handle_nested_irq - Handle a nested irq from a irq thread
|
||||
* @irq: the interrupt number
|
||||
*
|
||||
* Handle interrupts which are nested into a threaded interrupt
|
||||
* handler. The handler function is called inside the calling
|
||||
* threads context.
|
||||
*/
|
||||
void handle_nested_irq(unsigned int irq)
|
||||
{
|
||||
struct irq_desc *desc = irq_to_desc(irq);
|
||||
struct irqaction *action;
|
||||
irqreturn_t action_ret;
|
||||
|
||||
might_sleep();
|
||||
|
||||
spin_lock_irq(&desc->lock);
|
||||
|
||||
kstat_incr_irqs_this_cpu(irq, desc);
|
||||
|
||||
action = desc->action;
|
||||
if (unlikely(!action || (desc->status & IRQ_DISABLED)))
|
||||
goto out_unlock;
|
||||
|
||||
desc->status |= IRQ_INPROGRESS;
|
||||
spin_unlock_irq(&desc->lock);
|
||||
|
||||
action_ret = action->thread_fn(action->irq, action->dev_id);
|
||||
if (!noirqdebug)
|
||||
note_interrupt(irq, desc, action_ret);
|
||||
|
||||
spin_lock_irq(&desc->lock);
|
||||
desc->status &= ~IRQ_INPROGRESS;
|
||||
|
||||
out_unlock:
|
||||
spin_unlock_irq(&desc->lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(handle_nested_irq);
|
||||
|
||||
/**
|
||||
* handle_simple_irq - Simple and software-decoded IRQs.
|
||||
* @irq: the interrupt number
|
||||
@@ -382,7 +449,10 @@ handle_level_irq(unsigned int irq, struct irq_desc *desc)
|
||||
|
||||
spin_lock(&desc->lock);
|
||||
desc->status &= ~IRQ_INPROGRESS;
|
||||
if (!(desc->status & IRQ_DISABLED) && desc->chip->unmask)
|
||||
|
||||
if (unlikely(desc->status & IRQ_ONESHOT))
|
||||
desc->status |= IRQ_MASKED;
|
||||
else if (!(desc->status & IRQ_DISABLED) && desc->chip->unmask)
|
||||
desc->chip->unmask(irq);
|
||||
out_unlock:
|
||||
spin_unlock(&desc->lock);
|
||||
@@ -572,6 +642,7 @@ __set_irq_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
|
||||
desc->chip = &dummy_irq_chip;
|
||||
}
|
||||
|
||||
chip_bus_lock(irq, desc);
|
||||
spin_lock_irqsave(&desc->lock, flags);
|
||||
|
||||
/* Uninstall? */
|
||||
@@ -591,6 +662,7 @@ __set_irq_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
|
||||
desc->chip->startup(irq);
|
||||
}
|
||||
spin_unlock_irqrestore(&desc->lock, flags);
|
||||
chip_bus_sync_unlock(irq, desc);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__set_irq_handler);
|
||||
|
||||
|
@@ -161,7 +161,7 @@ int __init early_irq_init(void)
|
||||
|
||||
desc = irq_desc_legacy;
|
||||
legacy_count = ARRAY_SIZE(irq_desc_legacy);
|
||||
node = first_online_node;
|
||||
node = first_online_node;
|
||||
|
||||
/* allocate irq_desc_ptrs array based on nr_irqs */
|
||||
irq_desc_ptrs = kcalloc(nr_irqs, sizeof(void *), GFP_NOWAIT);
|
||||
@@ -172,6 +172,9 @@ int __init early_irq_init(void)
|
||||
|
||||
for (i = 0; i < legacy_count; i++) {
|
||||
desc[i].irq = i;
|
||||
#ifdef CONFIG_SMP
|
||||
desc[i].node = node;
|
||||
#endif
|
||||
desc[i].kstat_irqs = kstat_irqs_legacy + i * nr_cpu_ids;
|
||||
lockdep_set_class(&desc[i].lock, &irq_desc_lock_class);
|
||||
alloc_desc_masks(&desc[i], node, true);
|
||||
|
@@ -44,6 +44,19 @@ extern int irq_select_affinity_usr(unsigned int irq);
|
||||
|
||||
extern void irq_set_thread_affinity(struct irq_desc *desc);
|
||||
|
||||
/* Inline functions for support of irq chips on slow busses */
|
||||
static inline void chip_bus_lock(unsigned int irq, struct irq_desc *desc)
|
||||
{
|
||||
if (unlikely(desc->chip->bus_lock))
|
||||
desc->chip->bus_lock(irq);
|
||||
}
|
||||
|
||||
static inline void chip_bus_sync_unlock(unsigned int irq, struct irq_desc *desc)
|
||||
{
|
||||
if (unlikely(desc->chip->bus_sync_unlock))
|
||||
desc->chip->bus_sync_unlock(irq);
|
||||
}
|
||||
|
||||
/*
|
||||
* Debugging printout:
|
||||
*/
|
||||
|
@@ -230,9 +230,11 @@ void disable_irq_nosync(unsigned int irq)
|
||||
if (!desc)
|
||||
return;
|
||||
|
||||
chip_bus_lock(irq, desc);
|
||||
spin_lock_irqsave(&desc->lock, flags);
|
||||
__disable_irq(desc, irq, false);
|
||||
spin_unlock_irqrestore(&desc->lock, flags);
|
||||
chip_bus_sync_unlock(irq, desc);
|
||||
}
|
||||
EXPORT_SYMBOL(disable_irq_nosync);
|
||||
|
||||
@@ -294,7 +296,8 @@ void __enable_irq(struct irq_desc *desc, unsigned int irq, bool resume)
|
||||
* matches the last disable, processing of interrupts on this
|
||||
* IRQ line is re-enabled.
|
||||
*
|
||||
* This function may be called from IRQ context.
|
||||
* This function may be called from IRQ context only when
|
||||
* desc->chip->bus_lock and desc->chip->bus_sync_unlock are NULL !
|
||||
*/
|
||||
void enable_irq(unsigned int irq)
|
||||
{
|
||||
@@ -304,9 +307,11 @@ void enable_irq(unsigned int irq)
|
||||
if (!desc)
|
||||
return;
|
||||
|
||||
chip_bus_lock(irq, desc);
|
||||
spin_lock_irqsave(&desc->lock, flags);
|
||||
__enable_irq(desc, irq, false);
|
||||
spin_unlock_irqrestore(&desc->lock, flags);
|
||||
chip_bus_sync_unlock(irq, desc);
|
||||
}
|
||||
EXPORT_SYMBOL(enable_irq);
|
||||
|
||||
@@ -436,6 +441,26 @@ int __irq_set_trigger(struct irq_desc *desc, unsigned int irq,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Default primary interrupt handler for threaded interrupts. Is
|
||||
* assigned as primary handler when request_threaded_irq is called
|
||||
* with handler == NULL. Useful for oneshot interrupts.
|
||||
*/
|
||||
static irqreturn_t irq_default_primary_handler(int irq, void *dev_id)
|
||||
{
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
/*
|
||||
* Primary handler for nested threaded interrupts. Should never be
|
||||
* called.
|
||||
*/
|
||||
static irqreturn_t irq_nested_primary_handler(int irq, void *dev_id)
|
||||
{
|
||||
WARN(1, "Primary handler called for nested irq %d\n", irq);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
static int irq_wait_for_interrupt(struct irqaction *action)
|
||||
{
|
||||
while (!kthread_should_stop()) {
|
||||
@@ -451,6 +476,23 @@ static int irq_wait_for_interrupt(struct irqaction *action)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Oneshot interrupts keep the irq line masked until the threaded
|
||||
* handler finished. unmask if the interrupt has not been disabled and
|
||||
* is marked MASKED.
|
||||
*/
|
||||
static void irq_finalize_oneshot(unsigned int irq, struct irq_desc *desc)
|
||||
{
|
||||
chip_bus_lock(irq, desc);
|
||||
spin_lock_irq(&desc->lock);
|
||||
if (!(desc->status & IRQ_DISABLED) && (desc->status & IRQ_MASKED)) {
|
||||
desc->status &= ~IRQ_MASKED;
|
||||
desc->chip->unmask(irq);
|
||||
}
|
||||
spin_unlock_irq(&desc->lock);
|
||||
chip_bus_sync_unlock(irq, desc);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
/*
|
||||
* Check whether we need to change the affinity of the interrupt thread.
|
||||
@@ -492,7 +534,7 @@ static int irq_thread(void *data)
|
||||
struct sched_param param = { .sched_priority = MAX_USER_RT_PRIO/2, };
|
||||
struct irqaction *action = data;
|
||||
struct irq_desc *desc = irq_to_desc(action->irq);
|
||||
int wake;
|
||||
int wake, oneshot = desc->status & IRQ_ONESHOT;
|
||||
|
||||
sched_setscheduler(current, SCHED_FIFO, ¶m);
|
||||
current->irqaction = action;
|
||||
@@ -518,6 +560,9 @@ static int irq_thread(void *data)
|
||||
spin_unlock_irq(&desc->lock);
|
||||
|
||||
action->thread_fn(action->irq, action->dev_id);
|
||||
|
||||
if (oneshot)
|
||||
irq_finalize_oneshot(action->irq, desc);
|
||||
}
|
||||
|
||||
wake = atomic_dec_and_test(&desc->threads_active);
|
||||
@@ -565,7 +610,7 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
|
||||
struct irqaction *old, **old_ptr;
|
||||
const char *old_name = NULL;
|
||||
unsigned long flags;
|
||||
int shared = 0;
|
||||
int nested, shared = 0;
|
||||
int ret;
|
||||
|
||||
if (!desc)
|
||||
@@ -590,10 +635,32 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
|
||||
rand_initialize_irq(irq);
|
||||
}
|
||||
|
||||
/* Oneshot interrupts are not allowed with shared */
|
||||
if ((new->flags & IRQF_ONESHOT) && (new->flags & IRQF_SHARED))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Threaded handler ?
|
||||
* Check whether the interrupt nests into another interrupt
|
||||
* thread.
|
||||
*/
|
||||
if (new->thread_fn) {
|
||||
nested = desc->status & IRQ_NESTED_THREAD;
|
||||
if (nested) {
|
||||
if (!new->thread_fn)
|
||||
return -EINVAL;
|
||||
/*
|
||||
* Replace the primary handler which was provided from
|
||||
* the driver for non nested interrupt handling by the
|
||||
* dummy function which warns when called.
|
||||
*/
|
||||
new->handler = irq_nested_primary_handler;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a handler thread when a thread function is supplied
|
||||
* and the interrupt does not nest into another interrupt
|
||||
* thread.
|
||||
*/
|
||||
if (new->thread_fn && !nested) {
|
||||
struct task_struct *t;
|
||||
|
||||
t = kthread_create(irq_thread, new, "irq/%d-%s", irq,
|
||||
@@ -662,9 +729,12 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
|
||||
desc->status |= IRQ_PER_CPU;
|
||||
#endif
|
||||
|
||||
desc->status &= ~(IRQ_AUTODETECT | IRQ_WAITING |
|
||||
desc->status &= ~(IRQ_AUTODETECT | IRQ_WAITING | IRQ_ONESHOT |
|
||||
IRQ_INPROGRESS | IRQ_SPURIOUS_DISABLED);
|
||||
|
||||
if (new->flags & IRQF_ONESHOT)
|
||||
desc->status |= IRQ_ONESHOT;
|
||||
|
||||
if (!(desc->status & IRQ_NOAUTOEN)) {
|
||||
desc->depth = 0;
|
||||
desc->status &= ~IRQ_DISABLED;
|
||||
@@ -875,7 +945,14 @@ EXPORT_SYMBOL_GPL(remove_irq);
|
||||
*/
|
||||
void free_irq(unsigned int irq, void *dev_id)
|
||||
{
|
||||
struct irq_desc *desc = irq_to_desc(irq);
|
||||
|
||||
if (!desc)
|
||||
return;
|
||||
|
||||
chip_bus_lock(irq, desc);
|
||||
kfree(__free_irq(irq, dev_id));
|
||||
chip_bus_sync_unlock(irq, desc);
|
||||
}
|
||||
EXPORT_SYMBOL(free_irq);
|
||||
|
||||
@@ -884,6 +961,8 @@ EXPORT_SYMBOL(free_irq);
|
||||
* @irq: Interrupt line to allocate
|
||||
* @handler: Function to be called when the IRQ occurs.
|
||||
* Primary handler for threaded interrupts
|
||||
* If NULL and thread_fn != NULL the default
|
||||
* primary handler is installed
|
||||
* @thread_fn: Function called from the irq handler thread
|
||||
* If NULL, no irq thread is created
|
||||
* @irqflags: Interrupt type flags
|
||||
@@ -963,8 +1042,12 @@ int request_threaded_irq(unsigned int irq, irq_handler_t handler,
|
||||
|
||||
if (desc->status & IRQ_NOREQUEST)
|
||||
return -EINVAL;
|
||||
if (!handler)
|
||||
return -EINVAL;
|
||||
|
||||
if (!handler) {
|
||||
if (!thread_fn)
|
||||
return -EINVAL;
|
||||
handler = irq_default_primary_handler;
|
||||
}
|
||||
|
||||
action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
|
||||
if (!action)
|
||||
@@ -976,7 +1059,10 @@ int request_threaded_irq(unsigned int irq, irq_handler_t handler,
|
||||
action->name = devname;
|
||||
action->dev_id = dev_id;
|
||||
|
||||
chip_bus_lock(irq, desc);
|
||||
retval = __setup_irq(irq, desc, action);
|
||||
chip_bus_sync_unlock(irq, desc);
|
||||
|
||||
if (retval)
|
||||
kfree(action);
|
||||
|
||||
|
@@ -15,10 +15,10 @@
|
||||
/**
|
||||
* suspend_device_irqs - disable all currently enabled interrupt lines
|
||||
*
|
||||
* During system-wide suspend or hibernation device interrupts need to be
|
||||
* disabled at the chip level and this function is provided for this purpose.
|
||||
* It disables all interrupt lines that are enabled at the moment and sets the
|
||||
* IRQ_SUSPENDED flag for them.
|
||||
* During system-wide suspend or hibernation device drivers need to be prevented
|
||||
* from receiving interrupts and this function is provided for this purpose.
|
||||
* It marks all interrupt lines in use, except for the timer ones, as disabled
|
||||
* and sets the IRQ_SUSPENDED flag for each of them.
|
||||
*/
|
||||
void suspend_device_irqs(void)
|
||||
{
|
||||
|
@@ -70,8 +70,7 @@ void check_irq_resend(struct irq_desc *desc, unsigned int irq)
|
||||
if ((status & (IRQ_LEVEL | IRQ_PENDING | IRQ_REPLAY)) == IRQ_PENDING) {
|
||||
desc->status = (status & ~IRQ_PENDING) | IRQ_REPLAY;
|
||||
|
||||
if (!desc->chip || !desc->chip->retrigger ||
|
||||
!desc->chip->retrigger(irq)) {
|
||||
if (!desc->chip->retrigger || !desc->chip->retrigger(irq)) {
|
||||
#ifdef CONFIG_HARDIRQS_SW_RESEND
|
||||
/* Set it pending and activate the softirq: */
|
||||
set_bit(irq, irqs_resend);
|
||||
|
@@ -297,7 +297,6 @@ static int __init irqfixup_setup(char *str)
|
||||
|
||||
__setup("irqfixup", irqfixup_setup);
|
||||
module_param(irqfixup, int, 0644);
|
||||
MODULE_PARM_DESC("irqfixup", "0: No fixup, 1: irqfixup mode, 2: irqpoll mode");
|
||||
|
||||
static int __init irqpoll_setup(char *str)
|
||||
{
|
||||
|
@@ -37,6 +37,8 @@
|
||||
#include <linux/suspend.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#include <trace/events/module.h>
|
||||
|
||||
extern int max_threads;
|
||||
|
||||
static struct workqueue_struct *khelper_wq;
|
||||
@@ -78,6 +80,10 @@ int __request_module(bool wait, const char *fmt, ...)
|
||||
#define MAX_KMOD_CONCURRENT 50 /* Completely arbitrary value - KAO */
|
||||
static int kmod_loop_msg;
|
||||
|
||||
ret = security_kernel_module_request();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
va_start(args, fmt);
|
||||
ret = vsnprintf(module_name, MODULE_NAME_LEN, fmt, args);
|
||||
va_end(args);
|
||||
@@ -108,6 +114,8 @@ int __request_module(bool wait, const char *fmt, ...)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
trace_module_request(module_name, wait, _RET_IP_);
|
||||
|
||||
ret = call_usermodehelper(modprobe_path, argv, envp,
|
||||
wait ? UMH_WAIT_PROC : UMH_WAIT_EXEC);
|
||||
atomic_dec(&kmod_concurrent);
|
||||
@@ -462,6 +470,7 @@ int call_usermodehelper_exec(struct subprocess_info *sub_info,
|
||||
int retval = 0;
|
||||
|
||||
BUG_ON(atomic_read(&sub_info->cred->usage) != 1);
|
||||
validate_creds(sub_info->cred);
|
||||
|
||||
helper_lock();
|
||||
if (sub_info->path[0] == '\0')
|
||||
|
@@ -103,7 +103,7 @@ static struct kprobe_blackpoint kprobe_blacklist[] = {
|
||||
#define INSNS_PER_PAGE (PAGE_SIZE/(MAX_INSN_SIZE * sizeof(kprobe_opcode_t)))
|
||||
|
||||
struct kprobe_insn_page {
|
||||
struct hlist_node hlist;
|
||||
struct list_head list;
|
||||
kprobe_opcode_t *insns; /* Page of instruction slots */
|
||||
char slot_used[INSNS_PER_PAGE];
|
||||
int nused;
|
||||
@@ -117,7 +117,7 @@ enum kprobe_slot_state {
|
||||
};
|
||||
|
||||
static DEFINE_MUTEX(kprobe_insn_mutex); /* Protects kprobe_insn_pages */
|
||||
static struct hlist_head kprobe_insn_pages;
|
||||
static LIST_HEAD(kprobe_insn_pages);
|
||||
static int kprobe_garbage_slots;
|
||||
static int collect_garbage_slots(void);
|
||||
|
||||
@@ -152,10 +152,9 @@ loop_end:
|
||||
static kprobe_opcode_t __kprobes *__get_insn_slot(void)
|
||||
{
|
||||
struct kprobe_insn_page *kip;
|
||||
struct hlist_node *pos;
|
||||
|
||||
retry:
|
||||
hlist_for_each_entry(kip, pos, &kprobe_insn_pages, hlist) {
|
||||
list_for_each_entry(kip, &kprobe_insn_pages, list) {
|
||||
if (kip->nused < INSNS_PER_PAGE) {
|
||||
int i;
|
||||
for (i = 0; i < INSNS_PER_PAGE; i++) {
|
||||
@@ -189,8 +188,8 @@ static kprobe_opcode_t __kprobes *__get_insn_slot(void)
|
||||
kfree(kip);
|
||||
return NULL;
|
||||
}
|
||||
INIT_HLIST_NODE(&kip->hlist);
|
||||
hlist_add_head(&kip->hlist, &kprobe_insn_pages);
|
||||
INIT_LIST_HEAD(&kip->list);
|
||||
list_add(&kip->list, &kprobe_insn_pages);
|
||||
memset(kip->slot_used, SLOT_CLEAN, INSNS_PER_PAGE);
|
||||
kip->slot_used[0] = SLOT_USED;
|
||||
kip->nused = 1;
|
||||
@@ -219,12 +218,8 @@ static int __kprobes collect_one_slot(struct kprobe_insn_page *kip, int idx)
|
||||
* so as not to have to set it up again the
|
||||
* next time somebody inserts a probe.
|
||||
*/
|
||||
hlist_del(&kip->hlist);
|
||||
if (hlist_empty(&kprobe_insn_pages)) {
|
||||
INIT_HLIST_NODE(&kip->hlist);
|
||||
hlist_add_head(&kip->hlist,
|
||||
&kprobe_insn_pages);
|
||||
} else {
|
||||
if (!list_is_singular(&kprobe_insn_pages)) {
|
||||
list_del(&kip->list);
|
||||
module_free(NULL, kip->insns);
|
||||
kfree(kip);
|
||||
}
|
||||
@@ -235,14 +230,13 @@ static int __kprobes collect_one_slot(struct kprobe_insn_page *kip, int idx)
|
||||
|
||||
static int __kprobes collect_garbage_slots(void)
|
||||
{
|
||||
struct kprobe_insn_page *kip;
|
||||
struct hlist_node *pos, *next;
|
||||
struct kprobe_insn_page *kip, *next;
|
||||
|
||||
/* Ensure no-one is preepmted on the garbages */
|
||||
if (check_safety())
|
||||
return -EAGAIN;
|
||||
|
||||
hlist_for_each_entry_safe(kip, pos, next, &kprobe_insn_pages, hlist) {
|
||||
list_for_each_entry_safe(kip, next, &kprobe_insn_pages, list) {
|
||||
int i;
|
||||
if (kip->ngarbage == 0)
|
||||
continue;
|
||||
@@ -260,19 +254,17 @@ static int __kprobes collect_garbage_slots(void)
|
||||
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) {
|
||||
list_for_each_entry(kip, &kprobe_insn_pages, list) {
|
||||
if (kip->insns <= slot &&
|
||||
slot < kip->insns + (INSNS_PER_PAGE * MAX_INSN_SIZE)) {
|
||||
int i = (slot - kip->insns) / MAX_INSN_SIZE;
|
||||
if (dirty) {
|
||||
kip->slot_used[i] = SLOT_DIRTY;
|
||||
kip->ngarbage++;
|
||||
} else {
|
||||
} else
|
||||
collect_one_slot(kip, i);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@@ -16,8 +16,6 @@
|
||||
#include <linux/mutex.h>
|
||||
#include <trace/events/sched.h>
|
||||
|
||||
#define KTHREAD_NICE_LEVEL (-5)
|
||||
|
||||
static DEFINE_SPINLOCK(kthread_create_lock);
|
||||
static LIST_HEAD(kthread_create_list);
|
||||
struct task_struct *kthreadd_task;
|
||||
@@ -145,7 +143,6 @@ struct task_struct *kthread_create(int (*threadfn)(void *data),
|
||||
* The kernel thread should not inherit these properties.
|
||||
*/
|
||||
sched_setscheduler_nocheck(create.result, SCHED_NORMAL, ¶m);
|
||||
set_user_nice(create.result, KTHREAD_NICE_LEVEL);
|
||||
set_cpus_allowed_ptr(create.result, cpu_all_mask);
|
||||
}
|
||||
return create.result;
|
||||
@@ -221,7 +218,6 @@ int kthreadd(void *unused)
|
||||
/* Setup a clean context for our children to inherit. */
|
||||
set_task_comm(tsk, "kthreadd");
|
||||
ignore_signals(tsk);
|
||||
set_user_nice(tsk, KTHREAD_NICE_LEVEL);
|
||||
set_cpus_allowed_ptr(tsk, cpu_all_mask);
|
||||
set_mems_allowed(node_possible_map);
|
||||
|
||||
|
796
kernel/lockdep.c
796
kernel/lockdep.c
File diff suppressed because it is too large
Load Diff
@@ -91,6 +91,8 @@ extern unsigned int nr_process_chains;
|
||||
extern unsigned int max_lockdep_depth;
|
||||
extern unsigned int max_recursion_depth;
|
||||
|
||||
extern unsigned int max_bfs_queue_depth;
|
||||
|
||||
#ifdef CONFIG_PROVE_LOCKING
|
||||
extern unsigned long lockdep_count_forward_deps(struct lock_class *);
|
||||
extern unsigned long lockdep_count_backward_deps(struct lock_class *);
|
||||
|
@@ -25,38 +25,12 @@
|
||||
|
||||
static void *l_next(struct seq_file *m, void *v, loff_t *pos)
|
||||
{
|
||||
struct lock_class *class;
|
||||
|
||||
(*pos)++;
|
||||
|
||||
if (v == SEQ_START_TOKEN)
|
||||
class = m->private;
|
||||
else {
|
||||
class = v;
|
||||
|
||||
if (class->lock_entry.next != &all_lock_classes)
|
||||
class = list_entry(class->lock_entry.next,
|
||||
struct lock_class, lock_entry);
|
||||
else
|
||||
class = NULL;
|
||||
}
|
||||
|
||||
return class;
|
||||
return seq_list_next(v, &all_lock_classes, pos);
|
||||
}
|
||||
|
||||
static void *l_start(struct seq_file *m, loff_t *pos)
|
||||
{
|
||||
struct lock_class *class;
|
||||
loff_t i = 0;
|
||||
|
||||
if (*pos == 0)
|
||||
return SEQ_START_TOKEN;
|
||||
|
||||
list_for_each_entry(class, &all_lock_classes, lock_entry) {
|
||||
if (++i == *pos)
|
||||
return class;
|
||||
}
|
||||
return NULL;
|
||||
return seq_list_start_head(&all_lock_classes, *pos);
|
||||
}
|
||||
|
||||
static void l_stop(struct seq_file *m, void *v)
|
||||
@@ -82,11 +56,11 @@ static void print_name(struct seq_file *m, struct lock_class *class)
|
||||
|
||||
static int l_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct lock_class *class = v;
|
||||
struct lock_class *class = list_entry(v, struct lock_class, lock_entry);
|
||||
struct lock_list *entry;
|
||||
char usage[LOCK_USAGE_CHARS];
|
||||
|
||||
if (v == SEQ_START_TOKEN) {
|
||||
if (v == &all_lock_classes) {
|
||||
seq_printf(m, "all lock classes:\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -128,17 +102,7 @@ static const struct seq_operations lockdep_ops = {
|
||||
|
||||
static int lockdep_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
int res = seq_open(file, &lockdep_ops);
|
||||
if (!res) {
|
||||
struct seq_file *m = file->private_data;
|
||||
|
||||
if (!list_empty(&all_lock_classes))
|
||||
m->private = list_entry(all_lock_classes.next,
|
||||
struct lock_class, lock_entry);
|
||||
else
|
||||
m->private = NULL;
|
||||
}
|
||||
return res;
|
||||
return seq_open(file, &lockdep_ops);
|
||||
}
|
||||
|
||||
static const struct file_operations proc_lockdep_operations = {
|
||||
@@ -149,37 +113,23 @@ static const struct file_operations proc_lockdep_operations = {
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PROVE_LOCKING
|
||||
static void *lc_next(struct seq_file *m, void *v, loff_t *pos)
|
||||
{
|
||||
struct lock_chain *chain;
|
||||
|
||||
(*pos)++;
|
||||
|
||||
if (v == SEQ_START_TOKEN)
|
||||
chain = m->private;
|
||||
else {
|
||||
chain = v;
|
||||
|
||||
if (*pos < nr_lock_chains)
|
||||
chain = lock_chains + *pos;
|
||||
else
|
||||
chain = NULL;
|
||||
}
|
||||
|
||||
return chain;
|
||||
}
|
||||
|
||||
static void *lc_start(struct seq_file *m, loff_t *pos)
|
||||
{
|
||||
if (*pos == 0)
|
||||
return SEQ_START_TOKEN;
|
||||
|
||||
if (*pos < nr_lock_chains)
|
||||
return lock_chains + *pos;
|
||||
if (*pos - 1 < nr_lock_chains)
|
||||
return lock_chains + (*pos - 1);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *lc_next(struct seq_file *m, void *v, loff_t *pos)
|
||||
{
|
||||
(*pos)++;
|
||||
return lc_start(m, pos);
|
||||
}
|
||||
|
||||
static void lc_stop(struct seq_file *m, void *v)
|
||||
{
|
||||
}
|
||||
@@ -220,16 +170,7 @@ static const struct seq_operations lockdep_chains_ops = {
|
||||
|
||||
static int lockdep_chains_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
int res = seq_open(file, &lockdep_chains_ops);
|
||||
if (!res) {
|
||||
struct seq_file *m = file->private_data;
|
||||
|
||||
if (nr_lock_chains)
|
||||
m->private = lock_chains;
|
||||
else
|
||||
m->private = NULL;
|
||||
}
|
||||
return res;
|
||||
return seq_open(file, &lockdep_chains_ops);
|
||||
}
|
||||
|
||||
static const struct file_operations proc_lockdep_chains_operations = {
|
||||
@@ -258,16 +199,10 @@ static void lockdep_stats_debug_show(struct seq_file *m)
|
||||
debug_atomic_read(&chain_lookup_hits));
|
||||
seq_printf(m, " cyclic checks: %11u\n",
|
||||
debug_atomic_read(&nr_cyclic_checks));
|
||||
seq_printf(m, " cyclic-check recursions: %11u\n",
|
||||
debug_atomic_read(&nr_cyclic_check_recursions));
|
||||
seq_printf(m, " find-mask forwards checks: %11u\n",
|
||||
debug_atomic_read(&nr_find_usage_forwards_checks));
|
||||
seq_printf(m, " find-mask forwards recursions: %11u\n",
|
||||
debug_atomic_read(&nr_find_usage_forwards_recursions));
|
||||
seq_printf(m, " find-mask backwards checks: %11u\n",
|
||||
debug_atomic_read(&nr_find_usage_backwards_checks));
|
||||
seq_printf(m, " find-mask backwards recursions:%11u\n",
|
||||
debug_atomic_read(&nr_find_usage_backwards_recursions));
|
||||
|
||||
seq_printf(m, " hardirq on events: %11u\n", hi1);
|
||||
seq_printf(m, " hardirq off events: %11u\n", hi2);
|
||||
@@ -409,8 +344,10 @@ static int lockdep_stats_show(struct seq_file *m, void *v)
|
||||
nr_unused);
|
||||
seq_printf(m, " max locking depth: %11u\n",
|
||||
max_lockdep_depth);
|
||||
seq_printf(m, " max recursion depth: %11u\n",
|
||||
max_recursion_depth);
|
||||
#ifdef CONFIG_PROVE_LOCKING
|
||||
seq_printf(m, " max bfs queue depth: %11u\n",
|
||||
max_bfs_queue_depth);
|
||||
#endif
|
||||
lockdep_stats_debug_show(m);
|
||||
seq_printf(m, " debug_locks: %11u\n",
|
||||
debug_locks);
|
||||
@@ -438,7 +375,6 @@ struct lock_stat_data {
|
||||
};
|
||||
|
||||
struct lock_stat_seq {
|
||||
struct lock_stat_data *iter;
|
||||
struct lock_stat_data *iter_end;
|
||||
struct lock_stat_data stats[MAX_LOCKDEP_KEYS];
|
||||
};
|
||||
@@ -626,34 +562,22 @@ static void seq_header(struct seq_file *m)
|
||||
static void *ls_start(struct seq_file *m, loff_t *pos)
|
||||
{
|
||||
struct lock_stat_seq *data = m->private;
|
||||
struct lock_stat_data *iter;
|
||||
|
||||
if (*pos == 0)
|
||||
return SEQ_START_TOKEN;
|
||||
|
||||
data->iter = data->stats + *pos;
|
||||
if (data->iter >= data->iter_end)
|
||||
data->iter = NULL;
|
||||
iter = data->stats + (*pos - 1);
|
||||
if (iter >= data->iter_end)
|
||||
iter = NULL;
|
||||
|
||||
return data->iter;
|
||||
return iter;
|
||||
}
|
||||
|
||||
static void *ls_next(struct seq_file *m, void *v, loff_t *pos)
|
||||
{
|
||||
struct lock_stat_seq *data = m->private;
|
||||
|
||||
(*pos)++;
|
||||
|
||||
if (v == SEQ_START_TOKEN)
|
||||
data->iter = data->stats;
|
||||
else {
|
||||
data->iter = v;
|
||||
data->iter++;
|
||||
}
|
||||
|
||||
if (data->iter == data->iter_end)
|
||||
data->iter = NULL;
|
||||
|
||||
return data->iter;
|
||||
return ls_start(m, pos);
|
||||
}
|
||||
|
||||
static void ls_stop(struct seq_file *m, void *v)
|
||||
@@ -691,7 +615,6 @@ static int lock_stat_open(struct inode *inode, struct file *file)
|
||||
struct lock_stat_data *iter = data->stats;
|
||||
struct seq_file *m = file->private_data;
|
||||
|
||||
data->iter = iter;
|
||||
list_for_each_entry(class, &all_lock_classes, lock_entry) {
|
||||
iter->class = class;
|
||||
iter->stats = lock_stats(class);
|
||||
@@ -699,7 +622,7 @@ static int lock_stat_open(struct inode *inode, struct file *file)
|
||||
}
|
||||
data->iter_end = iter;
|
||||
|
||||
sort(data->stats, data->iter_end - data->iter,
|
||||
sort(data->stats, data->iter_end - data->stats,
|
||||
sizeof(struct lock_stat_data),
|
||||
lock_stat_cmp, NULL);
|
||||
|
||||
@@ -734,7 +657,6 @@ static int lock_stat_release(struct inode *inode, struct file *file)
|
||||
struct seq_file *seq = file->private_data;
|
||||
|
||||
vfree(seq->private);
|
||||
seq->private = NULL;
|
||||
return seq_release(inode, file);
|
||||
}
|
||||
|
||||
|
@@ -55,6 +55,11 @@
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/kmemleak.h>
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include <trace/events/module.h>
|
||||
|
||||
EXPORT_TRACEPOINT_SYMBOL(module_get);
|
||||
|
||||
#if 0
|
||||
#define DEBUGP printk
|
||||
#else
|
||||
@@ -942,6 +947,8 @@ void module_put(struct module *module)
|
||||
if (module) {
|
||||
unsigned int cpu = get_cpu();
|
||||
local_dec(__module_ref_addr(module, cpu));
|
||||
trace_module_put(module, _RET_IP_,
|
||||
local_read(__module_ref_addr(module, cpu)));
|
||||
/* Maybe they're waiting for us to drop reference? */
|
||||
if (unlikely(!module_is_live(module)))
|
||||
wake_up_process(module->waiter);
|
||||
@@ -1497,6 +1504,8 @@ static int __unlink_module(void *_mod)
|
||||
/* Free a module, remove from lists, etc (must hold module_mutex). */
|
||||
static void free_module(struct module *mod)
|
||||
{
|
||||
trace_module_free(mod);
|
||||
|
||||
/* Delete from various lists */
|
||||
stop_machine(__unlink_module, mod, NULL);
|
||||
remove_notes_attrs(mod);
|
||||
@@ -2364,6 +2373,8 @@ static noinline struct module *load_module(void __user *umod,
|
||||
/* Get rid of temporary copy */
|
||||
vfree(hdr);
|
||||
|
||||
trace_module_load(mod);
|
||||
|
||||
/* Done! */
|
||||
return mod;
|
||||
|
||||
|
@@ -46,12 +46,18 @@ static atomic_t nr_task_counters __read_mostly;
|
||||
|
||||
/*
|
||||
* perf counter paranoia level:
|
||||
* 0 - not paranoid
|
||||
* 1 - disallow cpu counters to unpriv
|
||||
* 2 - disallow kernel profiling to unpriv
|
||||
* -1 - not paranoid at all
|
||||
* 0 - disallow raw tracepoint access for unpriv
|
||||
* 1 - disallow cpu counters for unpriv
|
||||
* 2 - disallow kernel profiling for unpriv
|
||||
*/
|
||||
int sysctl_perf_counter_paranoid __read_mostly = 1;
|
||||
|
||||
static inline bool perf_paranoid_tracepoint_raw(void)
|
||||
{
|
||||
return sysctl_perf_counter_paranoid > -1;
|
||||
}
|
||||
|
||||
static inline bool perf_paranoid_cpu(void)
|
||||
{
|
||||
return sysctl_perf_counter_paranoid > 0;
|
||||
@@ -469,7 +475,8 @@ static void update_counter_times(struct perf_counter *counter)
|
||||
struct perf_counter_context *ctx = counter->ctx;
|
||||
u64 run_end;
|
||||
|
||||
if (counter->state < PERF_COUNTER_STATE_INACTIVE)
|
||||
if (counter->state < PERF_COUNTER_STATE_INACTIVE ||
|
||||
counter->group_leader->state < PERF_COUNTER_STATE_INACTIVE)
|
||||
return;
|
||||
|
||||
counter->total_time_enabled = ctx->time - counter->tstamp_enabled;
|
||||
@@ -518,7 +525,7 @@ static void __perf_counter_disable(void *info)
|
||||
*/
|
||||
if (counter->state >= PERF_COUNTER_STATE_INACTIVE) {
|
||||
update_context_time(ctx);
|
||||
update_counter_times(counter);
|
||||
update_group_times(counter);
|
||||
if (counter == counter->group_leader)
|
||||
group_sched_out(counter, cpuctx, ctx);
|
||||
else
|
||||
@@ -573,7 +580,7 @@ static void perf_counter_disable(struct perf_counter *counter)
|
||||
* in, so we can change the state safely.
|
||||
*/
|
||||
if (counter->state == PERF_COUNTER_STATE_INACTIVE) {
|
||||
update_counter_times(counter);
|
||||
update_group_times(counter);
|
||||
counter->state = PERF_COUNTER_STATE_OFF;
|
||||
}
|
||||
|
||||
@@ -850,6 +857,27 @@ retry:
|
||||
spin_unlock_irq(&ctx->lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Put a counter into inactive state and update time fields.
|
||||
* Enabling the leader of a group effectively enables all
|
||||
* the group members that aren't explicitly disabled, so we
|
||||
* have to update their ->tstamp_enabled also.
|
||||
* Note: this works for group members as well as group leaders
|
||||
* since the non-leader members' sibling_lists will be empty.
|
||||
*/
|
||||
static void __perf_counter_mark_enabled(struct perf_counter *counter,
|
||||
struct perf_counter_context *ctx)
|
||||
{
|
||||
struct perf_counter *sub;
|
||||
|
||||
counter->state = PERF_COUNTER_STATE_INACTIVE;
|
||||
counter->tstamp_enabled = ctx->time - counter->total_time_enabled;
|
||||
list_for_each_entry(sub, &counter->sibling_list, list_entry)
|
||||
if (sub->state >= PERF_COUNTER_STATE_INACTIVE)
|
||||
sub->tstamp_enabled =
|
||||
ctx->time - sub->total_time_enabled;
|
||||
}
|
||||
|
||||
/*
|
||||
* Cross CPU call to enable a performance counter
|
||||
*/
|
||||
@@ -877,8 +905,7 @@ static void __perf_counter_enable(void *info)
|
||||
|
||||
if (counter->state >= PERF_COUNTER_STATE_INACTIVE)
|
||||
goto unlock;
|
||||
counter->state = PERF_COUNTER_STATE_INACTIVE;
|
||||
counter->tstamp_enabled = ctx->time - counter->total_time_enabled;
|
||||
__perf_counter_mark_enabled(counter, ctx);
|
||||
|
||||
/*
|
||||
* If the counter is in a group and isn't the group leader,
|
||||
@@ -971,11 +998,9 @@ static void perf_counter_enable(struct perf_counter *counter)
|
||||
* Since we have the lock this context can't be scheduled
|
||||
* in, so we can change the state safely.
|
||||
*/
|
||||
if (counter->state == PERF_COUNTER_STATE_OFF) {
|
||||
counter->state = PERF_COUNTER_STATE_INACTIVE;
|
||||
counter->tstamp_enabled =
|
||||
ctx->time - counter->total_time_enabled;
|
||||
}
|
||||
if (counter->state == PERF_COUNTER_STATE_OFF)
|
||||
__perf_counter_mark_enabled(counter, ctx);
|
||||
|
||||
out:
|
||||
spin_unlock_irq(&ctx->lock);
|
||||
}
|
||||
@@ -1479,9 +1504,7 @@ static void perf_counter_enable_on_exec(struct task_struct *task)
|
||||
counter->attr.enable_on_exec = 0;
|
||||
if (counter->state >= PERF_COUNTER_STATE_INACTIVE)
|
||||
continue;
|
||||
counter->state = PERF_COUNTER_STATE_INACTIVE;
|
||||
counter->tstamp_enabled =
|
||||
ctx->time - counter->total_time_enabled;
|
||||
__perf_counter_mark_enabled(counter, ctx);
|
||||
enabled = 1;
|
||||
}
|
||||
|
||||
@@ -1675,6 +1698,11 @@ static void free_counter(struct perf_counter *counter)
|
||||
atomic_dec(&nr_task_counters);
|
||||
}
|
||||
|
||||
if (counter->output) {
|
||||
fput(counter->output->filp);
|
||||
counter->output = NULL;
|
||||
}
|
||||
|
||||
if (counter->destroy)
|
||||
counter->destroy(counter);
|
||||
|
||||
@@ -1960,6 +1988,8 @@ unlock:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int perf_counter_set_output(struct perf_counter *counter, int output_fd);
|
||||
|
||||
static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct perf_counter *counter = file->private_data;
|
||||
@@ -1983,6 +2013,9 @@ static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
case PERF_COUNTER_IOC_PERIOD:
|
||||
return perf_counter_period(counter, (u64 __user *)arg);
|
||||
|
||||
case PERF_COUNTER_IOC_SET_OUTPUT:
|
||||
return perf_counter_set_output(counter, arg);
|
||||
|
||||
default:
|
||||
return -ENOTTY;
|
||||
}
|
||||
@@ -2253,6 +2286,11 @@ static int perf_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
|
||||
WARN_ON_ONCE(counter->ctx->parent_ctx);
|
||||
mutex_lock(&counter->mmap_mutex);
|
||||
if (counter->output) {
|
||||
ret = -EINVAL;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (atomic_inc_not_zero(&counter->mmap_count)) {
|
||||
if (nr_pages != counter->data->nr_pages)
|
||||
ret = -EINVAL;
|
||||
@@ -2638,6 +2676,7 @@ static int perf_output_begin(struct perf_output_handle *handle,
|
||||
struct perf_counter *counter, unsigned int size,
|
||||
int nmi, int sample)
|
||||
{
|
||||
struct perf_counter *output_counter;
|
||||
struct perf_mmap_data *data;
|
||||
unsigned int offset, head;
|
||||
int have_lost;
|
||||
@@ -2647,13 +2686,17 @@ static int perf_output_begin(struct perf_output_handle *handle,
|
||||
u64 lost;
|
||||
} lost_event;
|
||||
|
||||
rcu_read_lock();
|
||||
/*
|
||||
* For inherited counters we send all the output towards the parent.
|
||||
*/
|
||||
if (counter->parent)
|
||||
counter = counter->parent;
|
||||
|
||||
rcu_read_lock();
|
||||
output_counter = rcu_dereference(counter->output);
|
||||
if (output_counter)
|
||||
counter = output_counter;
|
||||
|
||||
data = rcu_dereference(counter->data);
|
||||
if (!data)
|
||||
goto out;
|
||||
@@ -3934,6 +3977,7 @@ static const struct pmu *tp_perf_counter_init(struct perf_counter *counter)
|
||||
* have these.
|
||||
*/
|
||||
if ((counter->attr.sample_type & PERF_SAMPLE_RAW) &&
|
||||
perf_paranoid_tracepoint_raw() &&
|
||||
!capable(CAP_SYS_ADMIN))
|
||||
return ERR_PTR(-EPERM);
|
||||
|
||||
@@ -4202,6 +4246,57 @@ err_size:
|
||||
goto out;
|
||||
}
|
||||
|
||||
int perf_counter_set_output(struct perf_counter *counter, int output_fd)
|
||||
{
|
||||
struct perf_counter *output_counter = NULL;
|
||||
struct file *output_file = NULL;
|
||||
struct perf_counter *old_output;
|
||||
int fput_needed = 0;
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (!output_fd)
|
||||
goto set;
|
||||
|
||||
output_file = fget_light(output_fd, &fput_needed);
|
||||
if (!output_file)
|
||||
return -EBADF;
|
||||
|
||||
if (output_file->f_op != &perf_fops)
|
||||
goto out;
|
||||
|
||||
output_counter = output_file->private_data;
|
||||
|
||||
/* Don't chain output fds */
|
||||
if (output_counter->output)
|
||||
goto out;
|
||||
|
||||
/* Don't set an output fd when we already have an output channel */
|
||||
if (counter->data)
|
||||
goto out;
|
||||
|
||||
atomic_long_inc(&output_file->f_count);
|
||||
|
||||
set:
|
||||
mutex_lock(&counter->mmap_mutex);
|
||||
old_output = counter->output;
|
||||
rcu_assign_pointer(counter->output, output_counter);
|
||||
mutex_unlock(&counter->mmap_mutex);
|
||||
|
||||
if (old_output) {
|
||||
/*
|
||||
* we need to make sure no existing perf_output_*()
|
||||
* is still referencing this counter.
|
||||
*/
|
||||
synchronize_rcu();
|
||||
fput(old_output->filp);
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
fput_light(output_file, fput_needed);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* sys_perf_counter_open - open a performance counter, associate it to a task/cpu
|
||||
*
|
||||
@@ -4221,15 +4316,15 @@ SYSCALL_DEFINE5(perf_counter_open,
|
||||
struct file *group_file = NULL;
|
||||
int fput_needed = 0;
|
||||
int fput_needed2 = 0;
|
||||
int ret;
|
||||
int err;
|
||||
|
||||
/* for future expandability... */
|
||||
if (flags)
|
||||
if (flags & ~(PERF_FLAG_FD_NO_GROUP | PERF_FLAG_FD_OUTPUT))
|
||||
return -EINVAL;
|
||||
|
||||
ret = perf_copy_attr(attr_uptr, &attr);
|
||||
if (ret)
|
||||
return ret;
|
||||
err = perf_copy_attr(attr_uptr, &attr);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!attr.exclude_kernel) {
|
||||
if (perf_paranoid_kernel() && !capable(CAP_SYS_ADMIN))
|
||||
@@ -4252,8 +4347,8 @@ SYSCALL_DEFINE5(perf_counter_open,
|
||||
* Look up the group leader (we will attach this counter to it):
|
||||
*/
|
||||
group_leader = NULL;
|
||||
if (group_fd != -1) {
|
||||
ret = -EINVAL;
|
||||
if (group_fd != -1 && !(flags & PERF_FLAG_FD_NO_GROUP)) {
|
||||
err = -EINVAL;
|
||||
group_file = fget_light(group_fd, &fput_needed);
|
||||
if (!group_file)
|
||||
goto err_put_context;
|
||||
@@ -4282,18 +4377,24 @@ SYSCALL_DEFINE5(perf_counter_open,
|
||||
|
||||
counter = perf_counter_alloc(&attr, cpu, ctx, group_leader,
|
||||
NULL, GFP_KERNEL);
|
||||
ret = PTR_ERR(counter);
|
||||
err = PTR_ERR(counter);
|
||||
if (IS_ERR(counter))
|
||||
goto err_put_context;
|
||||
|
||||
ret = anon_inode_getfd("[perf_counter]", &perf_fops, counter, 0);
|
||||
if (ret < 0)
|
||||
err = anon_inode_getfd("[perf_counter]", &perf_fops, counter, 0);
|
||||
if (err < 0)
|
||||
goto err_free_put_context;
|
||||
|
||||
counter_file = fget_light(ret, &fput_needed2);
|
||||
counter_file = fget_light(err, &fput_needed2);
|
||||
if (!counter_file)
|
||||
goto err_free_put_context;
|
||||
|
||||
if (flags & PERF_FLAG_FD_OUTPUT) {
|
||||
err = perf_counter_set_output(counter, group_fd);
|
||||
if (err)
|
||||
goto err_fput_free_put_context;
|
||||
}
|
||||
|
||||
counter->filp = counter_file;
|
||||
WARN_ON_ONCE(ctx->parent_ctx);
|
||||
mutex_lock(&ctx->mutex);
|
||||
@@ -4307,20 +4408,20 @@ SYSCALL_DEFINE5(perf_counter_open,
|
||||
list_add_tail(&counter->owner_entry, ¤t->perf_counter_list);
|
||||
mutex_unlock(¤t->perf_counter_mutex);
|
||||
|
||||
err_fput_free_put_context:
|
||||
fput_light(counter_file, fput_needed2);
|
||||
|
||||
out_fput:
|
||||
fput_light(group_file, fput_needed);
|
||||
|
||||
return ret;
|
||||
|
||||
err_free_put_context:
|
||||
kfree(counter);
|
||||
if (err < 0)
|
||||
kfree(counter);
|
||||
|
||||
err_put_context:
|
||||
put_ctx(ctx);
|
||||
if (err < 0)
|
||||
put_ctx(ctx);
|
||||
|
||||
goto out_fput;
|
||||
fput_light(group_file, fput_needed);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
|
175
kernel/printk.c
175
kernel/printk.c
@@ -36,6 +36,12 @@
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
/*
|
||||
* for_each_console() allows you to iterate on each console
|
||||
*/
|
||||
#define for_each_console(con) \
|
||||
for (con = console_drivers; con != NULL; con = con->next)
|
||||
|
||||
/*
|
||||
* Architectures can override it:
|
||||
*/
|
||||
@@ -61,6 +67,8 @@ int console_printk[4] = {
|
||||
DEFAULT_CONSOLE_LOGLEVEL, /* default_console_loglevel */
|
||||
};
|
||||
|
||||
static int saved_console_loglevel = -1;
|
||||
|
||||
/*
|
||||
* Low level drivers may need that to know if they can schedule in
|
||||
* their unblank() callback or not. So let's export it.
|
||||
@@ -372,10 +380,15 @@ int do_syslog(int type, char __user *buf, int len)
|
||||
logged_chars = 0;
|
||||
break;
|
||||
case 6: /* Disable logging to console */
|
||||
if (saved_console_loglevel == -1)
|
||||
saved_console_loglevel = console_loglevel;
|
||||
console_loglevel = minimum_console_loglevel;
|
||||
break;
|
||||
case 7: /* Enable logging to console */
|
||||
console_loglevel = default_console_loglevel;
|
||||
if (saved_console_loglevel != -1) {
|
||||
console_loglevel = saved_console_loglevel;
|
||||
saved_console_loglevel = -1;
|
||||
}
|
||||
break;
|
||||
case 8: /* Set level of messages printed to console */
|
||||
error = -EINVAL;
|
||||
@@ -384,6 +397,8 @@ int do_syslog(int type, char __user *buf, int len)
|
||||
if (len < minimum_console_loglevel)
|
||||
len = minimum_console_loglevel;
|
||||
console_loglevel = len;
|
||||
/* Implicitly re-enable logging to console */
|
||||
saved_console_loglevel = -1;
|
||||
error = 0;
|
||||
break;
|
||||
case 9: /* Number of chars in the log buffer */
|
||||
@@ -412,7 +427,7 @@ static void __call_console_drivers(unsigned start, unsigned end)
|
||||
{
|
||||
struct console *con;
|
||||
|
||||
for (con = console_drivers; con; con = con->next) {
|
||||
for_each_console(con) {
|
||||
if ((con->flags & CON_ENABLED) && con->write &&
|
||||
(cpu_online(smp_processor_id()) ||
|
||||
(con->flags & CON_ANYTIME)))
|
||||
@@ -544,7 +559,7 @@ static int have_callable_console(void)
|
||||
{
|
||||
struct console *con;
|
||||
|
||||
for (con = console_drivers; con; con = con->next)
|
||||
for_each_console(con)
|
||||
if (con->flags & CON_ANYTIME)
|
||||
return 1;
|
||||
|
||||
@@ -1082,7 +1097,7 @@ void console_unblank(void)
|
||||
|
||||
console_locked = 1;
|
||||
console_may_schedule = 0;
|
||||
for (c = console_drivers; c != NULL; c = c->next)
|
||||
for_each_console(c)
|
||||
if ((c->flags & CON_ENABLED) && c->unblank)
|
||||
c->unblank();
|
||||
release_console_sem();
|
||||
@@ -1097,7 +1112,7 @@ struct tty_driver *console_device(int *index)
|
||||
struct tty_driver *driver = NULL;
|
||||
|
||||
acquire_console_sem();
|
||||
for (c = console_drivers; c != NULL; c = c->next) {
|
||||
for_each_console(c) {
|
||||
if (!c->device)
|
||||
continue;
|
||||
driver = c->device(c, index);
|
||||
@@ -1134,25 +1149,49 @@ EXPORT_SYMBOL(console_start);
|
||||
* to register the console printing procedure with printk() and to
|
||||
* print any messages that were printed by the kernel before the
|
||||
* console driver was initialized.
|
||||
*
|
||||
* This can happen pretty early during the boot process (because of
|
||||
* early_printk) - sometimes before setup_arch() completes - be careful
|
||||
* of what kernel features are used - they may not be initialised yet.
|
||||
*
|
||||
* There are two types of consoles - bootconsoles (early_printk) and
|
||||
* "real" consoles (everything which is not a bootconsole) which are
|
||||
* handled differently.
|
||||
* - Any number of bootconsoles can be registered at any time.
|
||||
* - As soon as a "real" console is registered, all bootconsoles
|
||||
* will be unregistered automatically.
|
||||
* - Once a "real" console is registered, any attempt to register a
|
||||
* bootconsoles will be rejected
|
||||
*/
|
||||
void register_console(struct console *console)
|
||||
void register_console(struct console *newcon)
|
||||
{
|
||||
int i;
|
||||
unsigned long flags;
|
||||
struct console *bootconsole = NULL;
|
||||
struct console *bcon = NULL;
|
||||
|
||||
if (console_drivers) {
|
||||
if (console->flags & CON_BOOT)
|
||||
return;
|
||||
if (console_drivers->flags & CON_BOOT)
|
||||
bootconsole = console_drivers;
|
||||
/*
|
||||
* before we register a new CON_BOOT console, make sure we don't
|
||||
* already have a valid console
|
||||
*/
|
||||
if (console_drivers && newcon->flags & CON_BOOT) {
|
||||
/* find the last or real console */
|
||||
for_each_console(bcon) {
|
||||
if (!(bcon->flags & CON_BOOT)) {
|
||||
printk(KERN_INFO "Too late to register bootconsole %s%d\n",
|
||||
newcon->name, newcon->index);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (preferred_console < 0 || bootconsole || !console_drivers)
|
||||
if (console_drivers && console_drivers->flags & CON_BOOT)
|
||||
bcon = console_drivers;
|
||||
|
||||
if (preferred_console < 0 || bcon || !console_drivers)
|
||||
preferred_console = selected_console;
|
||||
|
||||
if (console->early_setup)
|
||||
console->early_setup();
|
||||
if (newcon->early_setup)
|
||||
newcon->early_setup();
|
||||
|
||||
/*
|
||||
* See if we want to use this console driver. If we
|
||||
@@ -1160,13 +1199,13 @@ void register_console(struct console *console)
|
||||
* that registers here.
|
||||
*/
|
||||
if (preferred_console < 0) {
|
||||
if (console->index < 0)
|
||||
console->index = 0;
|
||||
if (console->setup == NULL ||
|
||||
console->setup(console, NULL) == 0) {
|
||||
console->flags |= CON_ENABLED;
|
||||
if (console->device) {
|
||||
console->flags |= CON_CONSDEV;
|
||||
if (newcon->index < 0)
|
||||
newcon->index = 0;
|
||||
if (newcon->setup == NULL ||
|
||||
newcon->setup(newcon, NULL) == 0) {
|
||||
newcon->flags |= CON_ENABLED;
|
||||
if (newcon->device) {
|
||||
newcon->flags |= CON_CONSDEV;
|
||||
preferred_console = 0;
|
||||
}
|
||||
}
|
||||
@@ -1178,64 +1217,62 @@ void register_console(struct console *console)
|
||||
*/
|
||||
for (i = 0; i < MAX_CMDLINECONSOLES && console_cmdline[i].name[0];
|
||||
i++) {
|
||||
if (strcmp(console_cmdline[i].name, console->name) != 0)
|
||||
if (strcmp(console_cmdline[i].name, newcon->name) != 0)
|
||||
continue;
|
||||
if (console->index >= 0 &&
|
||||
console->index != console_cmdline[i].index)
|
||||
if (newcon->index >= 0 &&
|
||||
newcon->index != console_cmdline[i].index)
|
||||
continue;
|
||||
if (console->index < 0)
|
||||
console->index = console_cmdline[i].index;
|
||||
if (newcon->index < 0)
|
||||
newcon->index = console_cmdline[i].index;
|
||||
#ifdef CONFIG_A11Y_BRAILLE_CONSOLE
|
||||
if (console_cmdline[i].brl_options) {
|
||||
console->flags |= CON_BRL;
|
||||
braille_register_console(console,
|
||||
newcon->flags |= CON_BRL;
|
||||
braille_register_console(newcon,
|
||||
console_cmdline[i].index,
|
||||
console_cmdline[i].options,
|
||||
console_cmdline[i].brl_options);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (console->setup &&
|
||||
console->setup(console, console_cmdline[i].options) != 0)
|
||||
if (newcon->setup &&
|
||||
newcon->setup(newcon, console_cmdline[i].options) != 0)
|
||||
break;
|
||||
console->flags |= CON_ENABLED;
|
||||
console->index = console_cmdline[i].index;
|
||||
newcon->flags |= CON_ENABLED;
|
||||
newcon->index = console_cmdline[i].index;
|
||||
if (i == selected_console) {
|
||||
console->flags |= CON_CONSDEV;
|
||||
newcon->flags |= CON_CONSDEV;
|
||||
preferred_console = selected_console;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!(console->flags & CON_ENABLED))
|
||||
if (!(newcon->flags & CON_ENABLED))
|
||||
return;
|
||||
|
||||
if (bootconsole && (console->flags & CON_CONSDEV)) {
|
||||
printk(KERN_INFO "console handover: boot [%s%d] -> real [%s%d]\n",
|
||||
bootconsole->name, bootconsole->index,
|
||||
console->name, console->index);
|
||||
unregister_console(bootconsole);
|
||||
console->flags &= ~CON_PRINTBUFFER;
|
||||
} else {
|
||||
printk(KERN_INFO "console [%s%d] enabled\n",
|
||||
console->name, console->index);
|
||||
}
|
||||
/*
|
||||
* If we have a bootconsole, and are switching to a real console,
|
||||
* don't print everything out again, since when the boot console, and
|
||||
* the real console are the same physical device, it's annoying to
|
||||
* see the beginning boot messages twice
|
||||
*/
|
||||
if (bcon && ((newcon->flags & (CON_CONSDEV | CON_BOOT)) == CON_CONSDEV))
|
||||
newcon->flags &= ~CON_PRINTBUFFER;
|
||||
|
||||
/*
|
||||
* Put this console in the list - keep the
|
||||
* preferred driver at the head of the list.
|
||||
*/
|
||||
acquire_console_sem();
|
||||
if ((console->flags & CON_CONSDEV) || console_drivers == NULL) {
|
||||
console->next = console_drivers;
|
||||
console_drivers = console;
|
||||
if (console->next)
|
||||
console->next->flags &= ~CON_CONSDEV;
|
||||
if ((newcon->flags & CON_CONSDEV) || console_drivers == NULL) {
|
||||
newcon->next = console_drivers;
|
||||
console_drivers = newcon;
|
||||
if (newcon->next)
|
||||
newcon->next->flags &= ~CON_CONSDEV;
|
||||
} else {
|
||||
console->next = console_drivers->next;
|
||||
console_drivers->next = console;
|
||||
newcon->next = console_drivers->next;
|
||||
console_drivers->next = newcon;
|
||||
}
|
||||
if (console->flags & CON_PRINTBUFFER) {
|
||||
if (newcon->flags & CON_PRINTBUFFER) {
|
||||
/*
|
||||
* release_console_sem() will print out the buffered messages
|
||||
* for us.
|
||||
@@ -1245,6 +1282,28 @@ void register_console(struct console *console)
|
||||
spin_unlock_irqrestore(&logbuf_lock, flags);
|
||||
}
|
||||
release_console_sem();
|
||||
|
||||
/*
|
||||
* By unregistering the bootconsoles after we enable the real console
|
||||
* we get the "console xxx enabled" message on all the consoles -
|
||||
* boot consoles, real consoles, etc - this is to ensure that end
|
||||
* users know there might be something in the kernel's log buffer that
|
||||
* went to the bootconsole (that they do not see on the real console)
|
||||
*/
|
||||
if (bcon && ((newcon->flags & (CON_CONSDEV | CON_BOOT)) == CON_CONSDEV)) {
|
||||
/* we need to iterate through twice, to make sure we print
|
||||
* everything out, before we unregister the console(s)
|
||||
*/
|
||||
printk(KERN_INFO "console [%s%d] enabled, bootconsole disabled\n",
|
||||
newcon->name, newcon->index);
|
||||
for_each_console(bcon)
|
||||
if (bcon->flags & CON_BOOT)
|
||||
unregister_console(bcon);
|
||||
} else {
|
||||
printk(KERN_INFO "%sconsole [%s%d] enabled\n",
|
||||
(newcon->flags & CON_BOOT) ? "boot" : "" ,
|
||||
newcon->name, newcon->index);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(register_console);
|
||||
|
||||
@@ -1287,11 +1346,13 @@ EXPORT_SYMBOL(unregister_console);
|
||||
|
||||
static int __init disable_boot_consoles(void)
|
||||
{
|
||||
if (console_drivers != NULL) {
|
||||
if (console_drivers->flags & CON_BOOT) {
|
||||
struct console *con;
|
||||
|
||||
for_each_console(con) {
|
||||
if (con->flags & CON_BOOT) {
|
||||
printk(KERN_INFO "turn off boot console %s%d\n",
|
||||
console_drivers->name, console_drivers->index);
|
||||
return unregister_console(console_drivers);
|
||||
con->name, con->index);
|
||||
unregister_console(con);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
@@ -152,7 +152,7 @@ int __ptrace_may_access(struct task_struct *task, unsigned int mode)
|
||||
if (!dumpable && !capable(CAP_SYS_PTRACE))
|
||||
return -EPERM;
|
||||
|
||||
return security_ptrace_may_access(task, mode);
|
||||
return security_ptrace_access_check(task, mode);
|
||||
}
|
||||
|
||||
bool ptrace_may_access(struct task_struct *task, unsigned int mode)
|
||||
|
@@ -1,807 +0,0 @@
|
||||
/*
|
||||
* Read-Copy Update mechanism for mutual exclusion
|
||||
*
|
||||
* 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, 2001
|
||||
*
|
||||
* Authors: Dipankar Sarma <dipankar@in.ibm.com>
|
||||
* Manfred Spraul <manfred@colorfullife.com>
|
||||
*
|
||||
* Based on the original work by Paul McKenney <paulmck@us.ibm.com>
|
||||
* and inputs from Rusty Russell, Andrea Arcangeli and Andi Kleen.
|
||||
* Papers:
|
||||
* http://www.rdrop.com/users/paulmck/paper/rclockpdcsproof.pdf
|
||||
* http://lse.sourceforge.net/locking/rclock_OLS.2001.05.01c.sc.pdf (OLS2001)
|
||||
*
|
||||
* 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/time.h>
|
||||
|
||||
#ifdef CONFIG_DEBUG_LOCK_ALLOC
|
||||
static struct lock_class_key rcu_lock_key;
|
||||
struct lockdep_map rcu_lock_map =
|
||||
STATIC_LOCKDEP_MAP_INIT("rcu_read_lock", &rcu_lock_key);
|
||||
EXPORT_SYMBOL_GPL(rcu_lock_map);
|
||||
#endif
|
||||
|
||||
|
||||
/* Definition for rcupdate control block. */
|
||||
static struct rcu_ctrlblk rcu_ctrlblk = {
|
||||
.cur = -300,
|
||||
.completed = -300,
|
||||
.pending = -300,
|
||||
.lock = __SPIN_LOCK_UNLOCKED(&rcu_ctrlblk.lock),
|
||||
.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_BITS_NONE,
|
||||
};
|
||||
|
||||
static DEFINE_PER_CPU(struct rcu_data, rcu_data);
|
||||
static DEFINE_PER_CPU(struct rcu_data, rcu_bh_data);
|
||||
|
||||
/*
|
||||
* Increment the quiescent state counter.
|
||||
* The counter is a bit degenerated: We do not need to know
|
||||
* how many quiescent states passed, just if there was at least
|
||||
* one since the start of the grace period. Thus just a flag.
|
||||
*/
|
||||
void rcu_qsctr_inc(int cpu)
|
||||
{
|
||||
struct rcu_data *rdp = &per_cpu(rcu_data, cpu);
|
||||
rdp->passed_quiesc = 1;
|
||||
}
|
||||
|
||||
void rcu_bh_qsctr_inc(int cpu)
|
||||
{
|
||||
struct rcu_data *rdp = &per_cpu(rcu_bh_data, cpu);
|
||||
rdp->passed_quiesc = 1;
|
||||
}
|
||||
|
||||
static int blimit = 10;
|
||||
static int qhimark = 10000;
|
||||
static int qlowmark = 100;
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
static void force_quiescent_state(struct rcu_data *rdp,
|
||||
struct rcu_ctrlblk *rcp)
|
||||
{
|
||||
int cpu;
|
||||
unsigned long flags;
|
||||
|
||||
set_need_resched();
|
||||
spin_lock_irqsave(&rcp->lock, flags);
|
||||
if (unlikely(!rcp->signaled)) {
|
||||
rcp->signaled = 1;
|
||||
/*
|
||||
* Don't send IPI to itself. With irqs disabled,
|
||||
* rdp->cpu is the current cpu.
|
||||
*
|
||||
* 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_mask is stable.
|
||||
*
|
||||
* However, a cpu might have been offlined _just_ before
|
||||
* we disabled irqs while entering here.
|
||||
* And rcu subsystem might not yet have handled the CPU_DEAD
|
||||
* notification, leading to the offlined cpu's bit
|
||||
* being set in the rcp->cpumask.
|
||||
*
|
||||
* Hence cpumask = (rcp->cpumask & cpu_online_mask) to prevent
|
||||
* sending smp_reschedule() to an offlined 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);
|
||||
}
|
||||
#else
|
||||
static inline void force_quiescent_state(struct rcu_data *rdp,
|
||||
struct rcu_ctrlblk *rcp)
|
||||
{
|
||||
set_need_resched();
|
||||
}
|
||||
#endif
|
||||
|
||||
static void __call_rcu(struct rcu_head *head, struct rcu_ctrlblk *rcp,
|
||||
struct rcu_data *rdp)
|
||||
{
|
||||
long batch;
|
||||
|
||||
head->next = NULL;
|
||||
smp_mb(); /* Read of rcu->cur must happen after any change by caller. */
|
||||
|
||||
/*
|
||||
* Determine the batch number of this callback.
|
||||
*
|
||||
* Using ACCESS_ONCE to avoid the following error when gcc eliminates
|
||||
* local variable "batch" and emits codes like this:
|
||||
* 1) rdp->batch = rcp->cur + 1 # gets old value
|
||||
* ......
|
||||
* 2)rcu_batch_after(rcp->cur + 1, rdp->batch) # gets new value
|
||||
* then [*nxttail[0], *nxttail[1]) may contain callbacks
|
||||
* that batch# = rdp->batch, see the comment of struct rcu_data.
|
||||
*/
|
||||
batch = ACCESS_ONCE(rcp->cur) + 1;
|
||||
|
||||
if (rdp->nxtlist && rcu_batch_after(batch, rdp->batch)) {
|
||||
/* process callbacks */
|
||||
rdp->nxttail[0] = rdp->nxttail[1];
|
||||
rdp->nxttail[1] = rdp->nxttail[2];
|
||||
if (rcu_batch_after(batch - 1, rdp->batch))
|
||||
rdp->nxttail[0] = rdp->nxttail[2];
|
||||
}
|
||||
|
||||
rdp->batch = batch;
|
||||
*rdp->nxttail[2] = head;
|
||||
rdp->nxttail[2] = &head->next;
|
||||
|
||||
if (unlikely(++rdp->qlen > qhimark)) {
|
||||
rdp->blimit = INT_MAX;
|
||||
force_quiescent_state(rdp, &rcu_ctrlblk);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_RCU_CPU_STALL_DETECTOR
|
||||
|
||||
static void record_gp_stall_check_time(struct rcu_ctrlblk *rcp)
|
||||
{
|
||||
rcp->gp_start = jiffies;
|
||||
rcp->jiffies_stall = jiffies + RCU_SECONDS_TILL_STALL_CHECK;
|
||||
}
|
||||
|
||||
static void print_other_cpu_stall(struct rcu_ctrlblk *rcp)
|
||||
{
|
||||
int cpu;
|
||||
long delta;
|
||||
unsigned long flags;
|
||||
|
||||
/* Only let one CPU complain about others per time interval. */
|
||||
|
||||
spin_lock_irqsave(&rcp->lock, flags);
|
||||
delta = jiffies - rcp->jiffies_stall;
|
||||
if (delta < 2 || rcp->cur != rcp->completed) {
|
||||
spin_unlock_irqrestore(&rcp->lock, flags);
|
||||
return;
|
||||
}
|
||||
rcp->jiffies_stall = jiffies + RCU_SECONDS_TILL_STALL_RECHECK;
|
||||
spin_unlock_irqrestore(&rcp->lock, flags);
|
||||
|
||||
/* OK, time to rat on our buddy... */
|
||||
|
||||
printk(KERN_ERR "INFO: RCU detected CPU stalls:");
|
||||
for_each_possible_cpu(cpu) {
|
||||
if (cpumask_test_cpu(cpu, to_cpumask(rcp->cpumask)))
|
||||
printk(" %d", cpu);
|
||||
}
|
||||
printk(" (detected by %d, t=%ld jiffies)\n",
|
||||
smp_processor_id(), (long)(jiffies - rcp->gp_start));
|
||||
}
|
||||
|
||||
static void print_cpu_stall(struct rcu_ctrlblk *rcp)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
printk(KERN_ERR "INFO: RCU detected CPU %d stall (t=%lu/%lu jiffies)\n",
|
||||
smp_processor_id(), jiffies,
|
||||
jiffies - rcp->gp_start);
|
||||
dump_stack();
|
||||
spin_lock_irqsave(&rcp->lock, flags);
|
||||
if ((long)(jiffies - rcp->jiffies_stall) >= 0)
|
||||
rcp->jiffies_stall =
|
||||
jiffies + RCU_SECONDS_TILL_STALL_RECHECK;
|
||||
spin_unlock_irqrestore(&rcp->lock, flags);
|
||||
set_need_resched(); /* kick ourselves to get things going. */
|
||||
}
|
||||
|
||||
static void check_cpu_stall(struct rcu_ctrlblk *rcp)
|
||||
{
|
||||
long delta;
|
||||
|
||||
delta = jiffies - rcp->jiffies_stall;
|
||||
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);
|
||||
|
||||
} else if (rcp->cur != rcp->completed && delta >= 2) {
|
||||
|
||||
/* They had two seconds to dump stack, so complain. */
|
||||
print_other_cpu_stall(rcp);
|
||||
}
|
||||
}
|
||||
|
||||
#else /* #ifdef CONFIG_RCU_CPU_STALL_DETECTOR */
|
||||
|
||||
static void record_gp_stall_check_time(struct rcu_ctrlblk *rcp)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void check_cpu_stall(struct rcu_ctrlblk *rcp)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* #else #ifdef CONFIG_RCU_CPU_STALL_DETECTOR */
|
||||
|
||||
/**
|
||||
* call_rcu - Queue an RCU callback for invocation after a grace period.
|
||||
* @head: structure to be used for queueing the RCU updates.
|
||||
* @func: actual update function to be invoked after the grace period
|
||||
*
|
||||
* The update function will be invoked some time after a full grace
|
||||
* period elapses, in other words after all currently executing RCU
|
||||
* read-side critical sections have completed. RCU read-side critical
|
||||
* sections are delimited by rcu_read_lock() and rcu_read_unlock(),
|
||||
* and may be nested.
|
||||
*/
|
||||
void call_rcu(struct rcu_head *head,
|
||||
void (*func)(struct rcu_head *rcu))
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
head->func = func;
|
||||
local_irq_save(flags);
|
||||
__call_rcu(head, &rcu_ctrlblk, &__get_cpu_var(rcu_data));
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(call_rcu);
|
||||
|
||||
/**
|
||||
* call_rcu_bh - Queue an RCU for invocation after a quicker grace period.
|
||||
* @head: structure to be used for queueing the RCU updates.
|
||||
* @func: actual update function to be invoked after the grace period
|
||||
*
|
||||
* The update function will be invoked some time after a full grace
|
||||
* period elapses, in other words after all currently executing RCU
|
||||
* read-side critical sections have completed. call_rcu_bh() assumes
|
||||
* that the read-side critical sections end on completion of a softirq
|
||||
* handler. This means that read-side critical sections in process
|
||||
* context must not be interrupted by softirqs. This interface is to be
|
||||
* used when most of the read-side critical sections are in softirq context.
|
||||
* RCU read-side critical sections are delimited by rcu_read_lock() and
|
||||
* rcu_read_unlock(), * if in interrupt context or rcu_read_lock_bh()
|
||||
* and rcu_read_unlock_bh(), if in process context. These may be nested.
|
||||
*/
|
||||
void call_rcu_bh(struct rcu_head *head,
|
||||
void (*func)(struct rcu_head *rcu))
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
head->func = func;
|
||||
local_irq_save(flags);
|
||||
__call_rcu(head, &rcu_bh_ctrlblk, &__get_cpu_var(rcu_bh_data));
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(call_rcu_bh);
|
||||
|
||||
/*
|
||||
* Return the number of RCU batches processed thus far. Useful
|
||||
* for debug and statistics.
|
||||
*/
|
||||
long rcu_batches_completed(void)
|
||||
{
|
||||
return rcu_ctrlblk.completed;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rcu_batches_completed);
|
||||
|
||||
/*
|
||||
* Return the number of RCU batches processed thus far. Useful
|
||||
* for debug and statistics.
|
||||
*/
|
||||
long rcu_batches_completed_bh(void)
|
||||
{
|
||||
return rcu_bh_ctrlblk.completed;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rcu_batches_completed_bh);
|
||||
|
||||
/* Raises the softirq for processing rcu_callbacks. */
|
||||
static inline void raise_rcu_softirq(void)
|
||||
{
|
||||
raise_softirq(RCU_SOFTIRQ);
|
||||
}
|
||||
|
||||
/*
|
||||
* Invoke the completed RCU callbacks. They are expected to be in
|
||||
* a per-cpu list.
|
||||
*/
|
||||
static void rcu_do_batch(struct rcu_data *rdp)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct rcu_head *next, *list;
|
||||
int count = 0;
|
||||
|
||||
list = rdp->donelist;
|
||||
while (list) {
|
||||
next = list->next;
|
||||
prefetch(next);
|
||||
list->func(list);
|
||||
list = next;
|
||||
if (++count >= rdp->blimit)
|
||||
break;
|
||||
}
|
||||
rdp->donelist = list;
|
||||
|
||||
local_irq_save(flags);
|
||||
rdp->qlen -= count;
|
||||
local_irq_restore(flags);
|
||||
if (rdp->blimit == INT_MAX && rdp->qlen <= qlowmark)
|
||||
rdp->blimit = blimit;
|
||||
|
||||
if (!rdp->donelist)
|
||||
rdp->donetail = &rdp->donelist;
|
||||
else
|
||||
raise_rcu_softirq();
|
||||
}
|
||||
|
||||
/*
|
||||
* Grace period handling:
|
||||
* The grace period handling consists out of two steps:
|
||||
* - A new grace period is started.
|
||||
* This is done by rcu_start_batch. The start is not broadcasted to
|
||||
* all cpus, they must pick this up by comparing rcp->cur with
|
||||
* rdp->quiescbatch. All cpus are recorded in the
|
||||
* rcu_ctrlblk.cpumask bitmap.
|
||||
* - All cpus must go through a quiescent state.
|
||||
* Since the start of the grace period is not broadcasted, at least two
|
||||
* calls to rcu_check_quiescent_state are required:
|
||||
* The first call just notices that a new grace period is running. The
|
||||
* following calls check if there was a quiescent state since the beginning
|
||||
* of the grace period. If so, it updates rcu_ctrlblk.cpumask. If
|
||||
* the bitmap is empty, then the grace period is completed.
|
||||
* rcu_check_quiescent_state calls rcu_start_batch(0) to start the next grace
|
||||
* period (if necessary).
|
||||
*/
|
||||
|
||||
/*
|
||||
* Register a new batch of callbacks, and start it up if there is currently no
|
||||
* active batch and the batch to be registered has not already occurred.
|
||||
* Caller must hold rcu_ctrlblk.lock.
|
||||
*/
|
||||
static void rcu_start_batch(struct rcu_ctrlblk *rcp)
|
||||
{
|
||||
if (rcp->cur != rcp->pending &&
|
||||
rcp->completed == rcp->cur) {
|
||||
rcp->cur++;
|
||||
record_gp_stall_check_time(rcp);
|
||||
|
||||
/*
|
||||
* Accessing nohz_cpu_mask before incrementing rcp->cur needs a
|
||||
* Barrier Otherwise it can cause tickless idle CPUs to be
|
||||
* included in rcp->cpumask, which will extend graceperiods
|
||||
* unnecessarily.
|
||||
*/
|
||||
smp_mb();
|
||||
cpumask_andnot(to_cpumask(rcp->cpumask),
|
||||
cpu_online_mask, nohz_cpu_mask);
|
||||
|
||||
rcp->signaled = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* cpu went through a quiescent state since the beginning of the grace period.
|
||||
* Clear it from the cpu mask and complete the grace period if it was the last
|
||||
* cpu. Start another grace period if someone has further entries pending
|
||||
*/
|
||||
static void cpu_quiet(int cpu, struct rcu_ctrlblk *rcp)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the cpu has gone through a quiescent state (say context
|
||||
* switch). If so and if it already hasn't done so in this RCU
|
||||
* quiescent cycle, then indicate that it has done so.
|
||||
*/
|
||||
static void rcu_check_quiescent_state(struct rcu_ctrlblk *rcp,
|
||||
struct rcu_data *rdp)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (rdp->quiescbatch != rcp->cur) {
|
||||
/* start new grace period: */
|
||||
rdp->qs_pending = 1;
|
||||
rdp->passed_quiesc = 0;
|
||||
rdp->quiescbatch = rcp->cur;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Grace period already completed for this cpu?
|
||||
* qs_pending is checked instead of the actual bitmap to avoid
|
||||
* cacheline trashing.
|
||||
*/
|
||||
if (!rdp->qs_pending)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Was there a quiescent state since the beginning of the grace
|
||||
* period? If no, then exit and wait for the next call.
|
||||
*/
|
||||
if (!rdp->passed_quiesc)
|
||||
return;
|
||||
rdp->qs_pending = 0;
|
||||
|
||||
spin_lock_irqsave(&rcp->lock, flags);
|
||||
/*
|
||||
* rdp->quiescbatch/rcp->cur and the cpu bitmap can come out of sync
|
||||
* during cpu startup. Ignore the quiescent state.
|
||||
*/
|
||||
if (likely(rdp->quiescbatch == rcp->cur))
|
||||
cpu_quiet(rdp->cpu, rcp);
|
||||
|
||||
spin_unlock_irqrestore(&rcp->lock, flags);
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
|
||||
/* warning! helper for rcu_offline_cpu. do not use elsewhere without reviewing
|
||||
* locking requirements, the list it's pulling from has to belong to a cpu
|
||||
* which is dead and hence not processing interrupts.
|
||||
*/
|
||||
static void rcu_move_batch(struct rcu_data *this_rdp, struct rcu_head *list,
|
||||
struct rcu_head **tail, long batch)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (list) {
|
||||
local_irq_save(flags);
|
||||
this_rdp->batch = batch;
|
||||
*this_rdp->nxttail[2] = list;
|
||||
this_rdp->nxttail[2] = tail;
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
}
|
||||
|
||||
static void __rcu_offline_cpu(struct rcu_data *this_rdp,
|
||||
struct rcu_ctrlblk *rcp, struct rcu_data *rdp)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
/*
|
||||
* if the cpu going offline owns the grace period
|
||||
* we can block indefinitely waiting for it, so flush
|
||||
* it here
|
||||
*/
|
||||
spin_lock_irqsave(&rcp->lock, flags);
|
||||
if (rcp->cur != rcp->completed)
|
||||
cpu_quiet(rdp->cpu, rcp);
|
||||
rcu_move_batch(this_rdp, rdp->donelist, rdp->donetail, rcp->cur + 1);
|
||||
rcu_move_batch(this_rdp, rdp->nxtlist, rdp->nxttail[2], rcp->cur + 1);
|
||||
spin_unlock(&rcp->lock);
|
||||
|
||||
this_rdp->qlen += rdp->qlen;
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
static void rcu_offline_cpu(int cpu)
|
||||
{
|
||||
struct rcu_data *this_rdp = &get_cpu_var(rcu_data);
|
||||
struct rcu_data *this_bh_rdp = &get_cpu_var(rcu_bh_data);
|
||||
|
||||
__rcu_offline_cpu(this_rdp, &rcu_ctrlblk,
|
||||
&per_cpu(rcu_data, cpu));
|
||||
__rcu_offline_cpu(this_bh_rdp, &rcu_bh_ctrlblk,
|
||||
&per_cpu(rcu_bh_data, cpu));
|
||||
put_cpu_var(rcu_data);
|
||||
put_cpu_var(rcu_bh_data);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void rcu_offline_cpu(int cpu)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This does the RCU processing work from softirq context.
|
||||
*/
|
||||
static void __rcu_process_callbacks(struct rcu_ctrlblk *rcp,
|
||||
struct rcu_data *rdp)
|
||||
{
|
||||
unsigned long flags;
|
||||
long completed_snap;
|
||||
|
||||
if (rdp->nxtlist) {
|
||||
local_irq_save(flags);
|
||||
completed_snap = ACCESS_ONCE(rcp->completed);
|
||||
|
||||
/*
|
||||
* move the other grace-period-completed entries to
|
||||
* [rdp->nxtlist, *rdp->nxttail[0]) temporarily
|
||||
*/
|
||||
if (!rcu_batch_before(completed_snap, rdp->batch))
|
||||
rdp->nxttail[0] = rdp->nxttail[1] = rdp->nxttail[2];
|
||||
else if (!rcu_batch_before(completed_snap, rdp->batch - 1))
|
||||
rdp->nxttail[0] = rdp->nxttail[1];
|
||||
|
||||
/*
|
||||
* the grace period for entries in
|
||||
* [rdp->nxtlist, *rdp->nxttail[0]) has completed and
|
||||
* move these entries to donelist
|
||||
*/
|
||||
if (rdp->nxttail[0] != &rdp->nxtlist) {
|
||||
*rdp->donetail = rdp->nxtlist;
|
||||
rdp->donetail = rdp->nxttail[0];
|
||||
rdp->nxtlist = *rdp->nxttail[0];
|
||||
*rdp->donetail = NULL;
|
||||
|
||||
if (rdp->nxttail[1] == rdp->nxttail[0])
|
||||
rdp->nxttail[1] = &rdp->nxtlist;
|
||||
if (rdp->nxttail[2] == rdp->nxttail[0])
|
||||
rdp->nxttail[2] = &rdp->nxtlist;
|
||||
rdp->nxttail[0] = &rdp->nxtlist;
|
||||
}
|
||||
|
||||
local_irq_restore(flags);
|
||||
|
||||
if (rcu_batch_after(rdp->batch, rcp->pending)) {
|
||||
unsigned long flags2;
|
||||
|
||||
/* and start it/schedule start if it's a new batch */
|
||||
spin_lock_irqsave(&rcp->lock, flags2);
|
||||
if (rcu_batch_after(rdp->batch, rcp->pending)) {
|
||||
rcp->pending = rdp->batch;
|
||||
rcu_start_batch(rcp);
|
||||
}
|
||||
spin_unlock_irqrestore(&rcp->lock, flags2);
|
||||
}
|
||||
}
|
||||
|
||||
rcu_check_quiescent_state(rcp, rdp);
|
||||
if (rdp->donelist)
|
||||
rcu_do_batch(rdp);
|
||||
}
|
||||
|
||||
static void rcu_process_callbacks(struct softirq_action *unused)
|
||||
{
|
||||
/*
|
||||
* Memory references from any prior RCU read-side critical sections
|
||||
* executed by the interrupted code must be see before any RCU
|
||||
* grace-period manupulations below.
|
||||
*/
|
||||
|
||||
smp_mb(); /* See above block comment. */
|
||||
|
||||
__rcu_process_callbacks(&rcu_ctrlblk, &__get_cpu_var(rcu_data));
|
||||
__rcu_process_callbacks(&rcu_bh_ctrlblk, &__get_cpu_var(rcu_bh_data));
|
||||
|
||||
/*
|
||||
* Memory references from any later RCU read-side critical sections
|
||||
* executed by the interrupted code must be see after any RCU
|
||||
* grace-period manupulations above.
|
||||
*/
|
||||
|
||||
smp_mb(); /* See above block comment. */
|
||||
}
|
||||
|
||||
static int __rcu_pending(struct rcu_ctrlblk *rcp, struct rcu_data *rdp)
|
||||
{
|
||||
/* Check for CPU stalls, if enabled. */
|
||||
check_cpu_stall(rcp);
|
||||
|
||||
if (rdp->nxtlist) {
|
||||
long completed_snap = ACCESS_ONCE(rcp->completed);
|
||||
|
||||
/*
|
||||
* This cpu has pending rcu entries and the grace period
|
||||
* for them has completed.
|
||||
*/
|
||||
if (!rcu_batch_before(completed_snap, rdp->batch))
|
||||
return 1;
|
||||
if (!rcu_batch_before(completed_snap, rdp->batch - 1) &&
|
||||
rdp->nxttail[0] != rdp->nxttail[1])
|
||||
return 1;
|
||||
if (rdp->nxttail[0] != &rdp->nxtlist)
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* This cpu has pending rcu entries and the new batch
|
||||
* for then hasn't been started nor scheduled start
|
||||
*/
|
||||
if (rcu_batch_after(rdp->batch, rcp->pending))
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* This cpu has finished callbacks to invoke */
|
||||
if (rdp->donelist)
|
||||
return 1;
|
||||
|
||||
/* The rcu core waits for a quiescent state from the cpu */
|
||||
if (rdp->quiescbatch != rcp->cur || rdp->qs_pending)
|
||||
return 1;
|
||||
|
||||
/* nothing to do */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check to see if there is any immediate RCU-related work to be done
|
||||
* by the current CPU, returning 1 if so. This function is part of the
|
||||
* RCU implementation; it is -not- an exported member of the RCU API.
|
||||
*/
|
||||
int rcu_pending(int cpu)
|
||||
{
|
||||
return __rcu_pending(&rcu_ctrlblk, &per_cpu(rcu_data, cpu)) ||
|
||||
__rcu_pending(&rcu_bh_ctrlblk, &per_cpu(rcu_bh_data, cpu));
|
||||
}
|
||||
|
||||
/*
|
||||
* Check to see if any future RCU-related work will need to be done
|
||||
* by the current CPU, even if none need be done immediately, returning
|
||||
* 1 if so. This function is part of the RCU implementation; it is -not-
|
||||
* an exported member of the RCU API.
|
||||
*/
|
||||
int rcu_needs_cpu(int cpu)
|
||||
{
|
||||
struct rcu_data *rdp = &per_cpu(rcu_data, cpu);
|
||||
struct rcu_data *rdp_bh = &per_cpu(rcu_bh_data, cpu);
|
||||
|
||||
return !!rdp->nxtlist || !!rdp_bh->nxtlist || rcu_pending(cpu);
|
||||
}
|
||||
|
||||
/*
|
||||
* Top-level function driving RCU grace-period detection, normally
|
||||
* invoked from the scheduler-clock interrupt. This function simply
|
||||
* increments counters that are read only from softirq by this same
|
||||
* CPU, so there are no memory barriers required.
|
||||
*/
|
||||
void rcu_check_callbacks(int cpu, int user)
|
||||
{
|
||||
if (user ||
|
||||
(idle_cpu(cpu) && rcu_scheduler_active &&
|
||||
!in_softirq() && hardirq_count() <= (1 << HARDIRQ_SHIFT))) {
|
||||
|
||||
/*
|
||||
* Get here if this CPU took its interrupt from user
|
||||
* mode or from the idle loop, and if this is not a
|
||||
* nested interrupt. In this case, the CPU is in
|
||||
* a quiescent state, so count it.
|
||||
*
|
||||
* Also do a memory barrier. This is needed to handle
|
||||
* the case where writes from a preempt-disable section
|
||||
* of code get reordered into schedule() by this CPU's
|
||||
* write buffer. The memory barrier makes sure that
|
||||
* the rcu_qsctr_inc() and rcu_bh_qsctr_inc() are see
|
||||
* by other CPUs to happen after any such write.
|
||||
*/
|
||||
|
||||
smp_mb(); /* See above block comment. */
|
||||
rcu_qsctr_inc(cpu);
|
||||
rcu_bh_qsctr_inc(cpu);
|
||||
|
||||
} else if (!in_softirq()) {
|
||||
|
||||
/*
|
||||
* Get here if this CPU did not take its interrupt from
|
||||
* softirq, in other words, if it is not interrupting
|
||||
* a rcu_bh read-side critical section. This is an _bh
|
||||
* critical section, so count it. The memory barrier
|
||||
* is needed for the same reason as is the above one.
|
||||
*/
|
||||
|
||||
smp_mb(); /* See above block comment. */
|
||||
rcu_bh_qsctr_inc(cpu);
|
||||
}
|
||||
raise_rcu_softirq();
|
||||
}
|
||||
|
||||
static void __cpuinit rcu_init_percpu_data(int cpu, struct rcu_ctrlblk *rcp,
|
||||
struct rcu_data *rdp)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&rcp->lock, flags);
|
||||
memset(rdp, 0, sizeof(*rdp));
|
||||
rdp->nxttail[0] = rdp->nxttail[1] = rdp->nxttail[2] = &rdp->nxtlist;
|
||||
rdp->donetail = &rdp->donelist;
|
||||
rdp->quiescbatch = rcp->completed;
|
||||
rdp->qs_pending = 0;
|
||||
rdp->cpu = cpu;
|
||||
rdp->blimit = blimit;
|
||||
spin_unlock_irqrestore(&rcp->lock, flags);
|
||||
}
|
||||
|
||||
static void __cpuinit rcu_online_cpu(int cpu)
|
||||
{
|
||||
struct rcu_data *rdp = &per_cpu(rcu_data, cpu);
|
||||
struct rcu_data *bh_rdp = &per_cpu(rcu_bh_data, cpu);
|
||||
|
||||
rcu_init_percpu_data(cpu, &rcu_ctrlblk, rdp);
|
||||
rcu_init_percpu_data(cpu, &rcu_bh_ctrlblk, bh_rdp);
|
||||
open_softirq(RCU_SOFTIRQ, rcu_process_callbacks);
|
||||
}
|
||||
|
||||
static int __cpuinit rcu_cpu_notify(struct notifier_block *self,
|
||||
unsigned long action, void *hcpu)
|
||||
{
|
||||
long cpu = (long)hcpu;
|
||||
|
||||
switch (action) {
|
||||
case CPU_UP_PREPARE:
|
||||
case CPU_UP_PREPARE_FROZEN:
|
||||
rcu_online_cpu(cpu);
|
||||
break;
|
||||
case CPU_DEAD:
|
||||
case CPU_DEAD_FROZEN:
|
||||
rcu_offline_cpu(cpu);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static struct notifier_block __cpuinitdata rcu_nb = {
|
||||
.notifier_call = rcu_cpu_notify,
|
||||
};
|
||||
|
||||
/*
|
||||
* Initializes rcu mechanism. Assumed to be called early.
|
||||
* That is before local timer(SMP) or jiffie timer (uniproc) is setup.
|
||||
* Note that rcu_qsctr and friends are implicitly
|
||||
* initialized due to the choice of ``0'' for RCU_CTR_INVALID.
|
||||
*/
|
||||
void __init __rcu_init(void)
|
||||
{
|
||||
#ifdef CONFIG_RCU_CPU_STALL_DETECTOR
|
||||
printk(KERN_INFO "RCU-based detection of stalled CPUs is enabled.\n");
|
||||
#endif /* #ifdef CONFIG_RCU_CPU_STALL_DETECTOR */
|
||||
rcu_cpu_notify(&rcu_nb, CPU_UP_PREPARE,
|
||||
(void *)(long)smp_processor_id());
|
||||
/* Register notifier for non-boot CPUs */
|
||||
register_cpu_notifier(&rcu_nb);
|
||||
}
|
||||
|
||||
module_param(blimit, int, 0);
|
||||
module_param(qhimark, int, 0);
|
||||
module_param(qlowmark, int, 0);
|
@@ -98,6 +98,30 @@ void synchronize_rcu(void)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(synchronize_rcu);
|
||||
|
||||
/**
|
||||
* synchronize_rcu_bh - wait until an rcu_bh grace period has elapsed.
|
||||
*
|
||||
* Control will return to the caller some time after a full rcu_bh grace
|
||||
* period has elapsed, in other words after all currently executing rcu_bh
|
||||
* read-side critical sections have completed. RCU read-side critical
|
||||
* sections are delimited by rcu_read_lock_bh() and rcu_read_unlock_bh(),
|
||||
* and may be nested.
|
||||
*/
|
||||
void synchronize_rcu_bh(void)
|
||||
{
|
||||
struct rcu_synchronize rcu;
|
||||
|
||||
if (rcu_blocking_is_gp())
|
||||
return;
|
||||
|
||||
init_completion(&rcu.completion);
|
||||
/* Will wake me after RCU finished. */
|
||||
call_rcu_bh(&rcu.head, wakeme_after_rcu);
|
||||
/* Wait for it. */
|
||||
wait_for_completion(&rcu.completion);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(synchronize_rcu_bh);
|
||||
|
||||
static void rcu_barrier_callback(struct rcu_head *notused)
|
||||
{
|
||||
if (atomic_dec_and_test(&rcu_barrier_cpu_count))
|
||||
@@ -129,6 +153,7 @@ static void rcu_barrier_func(void *type)
|
||||
static inline void wait_migrated_callbacks(void)
|
||||
{
|
||||
wait_event(rcu_migrate_wq, !atomic_read(&rcu_migrate_type_count));
|
||||
smp_mb(); /* In case we didn't sleep. */
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -192,9 +217,13 @@ static void rcu_migrate_callback(struct rcu_head *notused)
|
||||
wake_up(&rcu_migrate_wq);
|
||||
}
|
||||
|
||||
extern int rcu_cpu_notify(struct notifier_block *self,
|
||||
unsigned long action, void *hcpu);
|
||||
|
||||
static int __cpuinit rcu_barrier_cpu_hotplug(struct notifier_block *self,
|
||||
unsigned long action, void *hcpu)
|
||||
{
|
||||
rcu_cpu_notify(self, action, hcpu);
|
||||
if (action == CPU_DYING) {
|
||||
/*
|
||||
* preempt_disable() in on_each_cpu() prevents stop_machine(),
|
||||
@@ -209,7 +238,8 @@ static int __cpuinit rcu_barrier_cpu_hotplug(struct notifier_block *self,
|
||||
call_rcu_bh(rcu_migrate_head, rcu_migrate_callback);
|
||||
call_rcu_sched(rcu_migrate_head + 1, rcu_migrate_callback);
|
||||
call_rcu(rcu_migrate_head + 2, rcu_migrate_callback);
|
||||
} else if (action == CPU_POST_DEAD) {
|
||||
} else if (action == CPU_DOWN_PREPARE) {
|
||||
/* Don't need to wait until next removal operation. */
|
||||
/* rcu_migrate_head is protected by cpu_add_remove_lock */
|
||||
wait_migrated_callbacks();
|
||||
}
|
||||
@@ -219,8 +249,18 @@ static int __cpuinit rcu_barrier_cpu_hotplug(struct notifier_block *self,
|
||||
|
||||
void __init rcu_init(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
__rcu_init();
|
||||
hotcpu_notifier(rcu_barrier_cpu_hotplug, 0);
|
||||
cpu_notifier(rcu_barrier_cpu_hotplug, 0);
|
||||
|
||||
/*
|
||||
* We don't need protection against CPU-hotplug here because
|
||||
* this is called early in boot, before either interrupts
|
||||
* or the scheduler are operational.
|
||||
*/
|
||||
for_each_online_cpu(i)
|
||||
rcu_barrier_cpu_hotplug(NULL, CPU_UP_PREPARE, (void *)(long)i);
|
||||
}
|
||||
|
||||
void rcu_scheduler_starting(void)
|
||||
|
1539
kernel/rcupreempt.c
1539
kernel/rcupreempt.c
File diff suppressed because it is too large
Load Diff
@@ -1,334 +0,0 @@
|
||||
/*
|
||||
* Read-Copy Update tracing for realtime 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, 2006
|
||||
*
|
||||
* Papers: http://www.rdrop.com/users/paulmck/RCU
|
||||
*
|
||||
* For detailed explanation of Read-Copy Update mechanism see -
|
||||
* Documentation/RCU/ *.txt
|
||||
*
|
||||
*/
|
||||
#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/rcupreempt_trace.h>
|
||||
#include <linux/debugfs.h>
|
||||
|
||||
static struct mutex rcupreempt_trace_mutex;
|
||||
static char *rcupreempt_trace_buf;
|
||||
#define RCUPREEMPT_TRACE_BUF_SIZE 4096
|
||||
|
||||
void rcupreempt_trace_move2done(struct rcupreempt_trace *trace)
|
||||
{
|
||||
trace->done_length += trace->wait_length;
|
||||
trace->done_add += trace->wait_length;
|
||||
trace->wait_length = 0;
|
||||
}
|
||||
void rcupreempt_trace_move2wait(struct rcupreempt_trace *trace)
|
||||
{
|
||||
trace->wait_length += trace->next_length;
|
||||
trace->wait_add += trace->next_length;
|
||||
trace->next_length = 0;
|
||||
}
|
||||
void rcupreempt_trace_try_flip_1(struct rcupreempt_trace *trace)
|
||||
{
|
||||
atomic_inc(&trace->rcu_try_flip_1);
|
||||
}
|
||||
void rcupreempt_trace_try_flip_e1(struct rcupreempt_trace *trace)
|
||||
{
|
||||
atomic_inc(&trace->rcu_try_flip_e1);
|
||||
}
|
||||
void rcupreempt_trace_try_flip_i1(struct rcupreempt_trace *trace)
|
||||
{
|
||||
trace->rcu_try_flip_i1++;
|
||||
}
|
||||
void rcupreempt_trace_try_flip_ie1(struct rcupreempt_trace *trace)
|
||||
{
|
||||
trace->rcu_try_flip_ie1++;
|
||||
}
|
||||
void rcupreempt_trace_try_flip_g1(struct rcupreempt_trace *trace)
|
||||
{
|
||||
trace->rcu_try_flip_g1++;
|
||||
}
|
||||
void rcupreempt_trace_try_flip_a1(struct rcupreempt_trace *trace)
|
||||
{
|
||||
trace->rcu_try_flip_a1++;
|
||||
}
|
||||
void rcupreempt_trace_try_flip_ae1(struct rcupreempt_trace *trace)
|
||||
{
|
||||
trace->rcu_try_flip_ae1++;
|
||||
}
|
||||
void rcupreempt_trace_try_flip_a2(struct rcupreempt_trace *trace)
|
||||
{
|
||||
trace->rcu_try_flip_a2++;
|
||||
}
|
||||
void rcupreempt_trace_try_flip_z1(struct rcupreempt_trace *trace)
|
||||
{
|
||||
trace->rcu_try_flip_z1++;
|
||||
}
|
||||
void rcupreempt_trace_try_flip_ze1(struct rcupreempt_trace *trace)
|
||||
{
|
||||
trace->rcu_try_flip_ze1++;
|
||||
}
|
||||
void rcupreempt_trace_try_flip_z2(struct rcupreempt_trace *trace)
|
||||
{
|
||||
trace->rcu_try_flip_z2++;
|
||||
}
|
||||
void rcupreempt_trace_try_flip_m1(struct rcupreempt_trace *trace)
|
||||
{
|
||||
trace->rcu_try_flip_m1++;
|
||||
}
|
||||
void rcupreempt_trace_try_flip_me1(struct rcupreempt_trace *trace)
|
||||
{
|
||||
trace->rcu_try_flip_me1++;
|
||||
}
|
||||
void rcupreempt_trace_try_flip_m2(struct rcupreempt_trace *trace)
|
||||
{
|
||||
trace->rcu_try_flip_m2++;
|
||||
}
|
||||
void rcupreempt_trace_check_callbacks(struct rcupreempt_trace *trace)
|
||||
{
|
||||
trace->rcu_check_callbacks++;
|
||||
}
|
||||
void rcupreempt_trace_done_remove(struct rcupreempt_trace *trace)
|
||||
{
|
||||
trace->done_remove += trace->done_length;
|
||||
trace->done_length = 0;
|
||||
}
|
||||
void rcupreempt_trace_invoke(struct rcupreempt_trace *trace)
|
||||
{
|
||||
atomic_inc(&trace->done_invoked);
|
||||
}
|
||||
void rcupreempt_trace_next_add(struct rcupreempt_trace *trace)
|
||||
{
|
||||
trace->next_add++;
|
||||
trace->next_length++;
|
||||
}
|
||||
|
||||
static void rcupreempt_trace_sum(struct rcupreempt_trace *sp)
|
||||
{
|
||||
struct rcupreempt_trace *cp;
|
||||
int cpu;
|
||||
|
||||
memset(sp, 0, sizeof(*sp));
|
||||
for_each_possible_cpu(cpu) {
|
||||
cp = rcupreempt_trace_cpu(cpu);
|
||||
sp->next_length += cp->next_length;
|
||||
sp->next_add += cp->next_add;
|
||||
sp->wait_length += cp->wait_length;
|
||||
sp->wait_add += cp->wait_add;
|
||||
sp->done_length += cp->done_length;
|
||||
sp->done_add += cp->done_add;
|
||||
sp->done_remove += cp->done_remove;
|
||||
atomic_add(atomic_read(&cp->done_invoked), &sp->done_invoked);
|
||||
sp->rcu_check_callbacks += cp->rcu_check_callbacks;
|
||||
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;
|
||||
sp->rcu_try_flip_a1 += cp->rcu_try_flip_a1;
|
||||
sp->rcu_try_flip_ae1 += cp->rcu_try_flip_ae1;
|
||||
sp->rcu_try_flip_a2 += cp->rcu_try_flip_a2;
|
||||
sp->rcu_try_flip_z1 += cp->rcu_try_flip_z1;
|
||||
sp->rcu_try_flip_ze1 += cp->rcu_try_flip_ze1;
|
||||
sp->rcu_try_flip_z2 += cp->rcu_try_flip_z2;
|
||||
sp->rcu_try_flip_m1 += cp->rcu_try_flip_m1;
|
||||
sp->rcu_try_flip_me1 += cp->rcu_try_flip_me1;
|
||||
sp->rcu_try_flip_m2 += cp->rcu_try_flip_m2;
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t rcustats_read(struct file *filp, char __user *buffer,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct rcupreempt_trace trace;
|
||||
ssize_t bcount;
|
||||
int cnt = 0;
|
||||
|
||||
rcupreempt_trace_sum(&trace);
|
||||
mutex_lock(&rcupreempt_trace_mutex);
|
||||
snprintf(&rcupreempt_trace_buf[cnt], RCUPREEMPT_TRACE_BUF_SIZE - cnt,
|
||||
"ggp=%ld rcc=%ld\n",
|
||||
rcu_batches_completed(),
|
||||
trace.rcu_check_callbacks);
|
||||
snprintf(&rcupreempt_trace_buf[cnt], RCUPREEMPT_TRACE_BUF_SIZE - cnt,
|
||||
"na=%ld nl=%ld wa=%ld wl=%ld da=%ld dl=%ld dr=%ld di=%d\n"
|
||||
"1=%d e1=%d i1=%ld ie1=%ld g1=%ld a1=%ld ae1=%ld a2=%ld\n"
|
||||
"z1=%ld ze1=%ld z2=%ld m1=%ld me1=%ld m2=%ld\n",
|
||||
|
||||
trace.next_add, trace.next_length,
|
||||
trace.wait_add, trace.wait_length,
|
||||
trace.done_add, trace.done_length,
|
||||
trace.done_remove, atomic_read(&trace.done_invoked),
|
||||
atomic_read(&trace.rcu_try_flip_1),
|
||||
atomic_read(&trace.rcu_try_flip_e1),
|
||||
trace.rcu_try_flip_i1, trace.rcu_try_flip_ie1,
|
||||
trace.rcu_try_flip_g1,
|
||||
trace.rcu_try_flip_a1, trace.rcu_try_flip_ae1,
|
||||
trace.rcu_try_flip_a2,
|
||||
trace.rcu_try_flip_z1, trace.rcu_try_flip_ze1,
|
||||
trace.rcu_try_flip_z2,
|
||||
trace.rcu_try_flip_m1, trace.rcu_try_flip_me1,
|
||||
trace.rcu_try_flip_m2);
|
||||
bcount = simple_read_from_buffer(buffer, count, ppos,
|
||||
rcupreempt_trace_buf, strlen(rcupreempt_trace_buf));
|
||||
mutex_unlock(&rcupreempt_trace_mutex);
|
||||
return bcount;
|
||||
}
|
||||
|
||||
static ssize_t rcugp_read(struct file *filp, char __user *buffer,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
long oldgp = rcu_batches_completed();
|
||||
ssize_t bcount;
|
||||
|
||||
mutex_lock(&rcupreempt_trace_mutex);
|
||||
synchronize_rcu();
|
||||
snprintf(rcupreempt_trace_buf, RCUPREEMPT_TRACE_BUF_SIZE,
|
||||
"oldggp=%ld newggp=%ld\n", oldgp, rcu_batches_completed());
|
||||
bcount = simple_read_from_buffer(buffer, count, ppos,
|
||||
rcupreempt_trace_buf, strlen(rcupreempt_trace_buf));
|
||||
mutex_unlock(&rcupreempt_trace_mutex);
|
||||
return bcount;
|
||||
}
|
||||
|
||||
static ssize_t rcuctrs_read(struct file *filp, char __user *buffer,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
int cnt = 0;
|
||||
int cpu;
|
||||
int f = rcu_batches_completed() & 0x1;
|
||||
ssize_t bcount;
|
||||
|
||||
mutex_lock(&rcupreempt_trace_mutex);
|
||||
|
||||
cnt += snprintf(&rcupreempt_trace_buf[cnt], RCUPREEMPT_TRACE_BUF_SIZE,
|
||||
"CPU last cur F M\n");
|
||||
for_each_online_cpu(cpu) {
|
||||
long *flipctr = rcupreempt_flipctr(cpu);
|
||||
cnt += snprintf(&rcupreempt_trace_buf[cnt],
|
||||
RCUPREEMPT_TRACE_BUF_SIZE - cnt,
|
||||
"%3d %4ld %3ld %d %d\n",
|
||||
cpu,
|
||||
flipctr[!f],
|
||||
flipctr[f],
|
||||
rcupreempt_flip_flag(cpu),
|
||||
rcupreempt_mb_flag(cpu));
|
||||
}
|
||||
cnt += snprintf(&rcupreempt_trace_buf[cnt],
|
||||
RCUPREEMPT_TRACE_BUF_SIZE - cnt,
|
||||
"ggp = %ld, state = %s\n",
|
||||
rcu_batches_completed(),
|
||||
rcupreempt_try_flip_state_name());
|
||||
cnt += snprintf(&rcupreempt_trace_buf[cnt],
|
||||
RCUPREEMPT_TRACE_BUF_SIZE - cnt,
|
||||
"\n");
|
||||
bcount = simple_read_from_buffer(buffer, count, ppos,
|
||||
rcupreempt_trace_buf, strlen(rcupreempt_trace_buf));
|
||||
mutex_unlock(&rcupreempt_trace_mutex);
|
||||
return bcount;
|
||||
}
|
||||
|
||||
static struct file_operations rcustats_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.read = rcustats_read,
|
||||
};
|
||||
|
||||
static struct file_operations rcugp_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.read = rcugp_read,
|
||||
};
|
||||
|
||||
static struct file_operations rcuctrs_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.read = rcuctrs_read,
|
||||
};
|
||||
|
||||
static struct dentry *rcudir, *statdir, *ctrsdir, *gpdir;
|
||||
static int rcupreempt_debugfs_init(void)
|
||||
{
|
||||
rcudir = debugfs_create_dir("rcu", NULL);
|
||||
if (!rcudir)
|
||||
goto out;
|
||||
statdir = debugfs_create_file("rcustats", 0444, rcudir,
|
||||
NULL, &rcustats_fops);
|
||||
if (!statdir)
|
||||
goto free_out;
|
||||
|
||||
gpdir = debugfs_create_file("rcugp", 0444, rcudir, NULL, &rcugp_fops);
|
||||
if (!gpdir)
|
||||
goto free_out;
|
||||
|
||||
ctrsdir = debugfs_create_file("rcuctrs", 0444, rcudir,
|
||||
NULL, &rcuctrs_fops);
|
||||
if (!ctrsdir)
|
||||
goto free_out;
|
||||
return 0;
|
||||
free_out:
|
||||
if (statdir)
|
||||
debugfs_remove(statdir);
|
||||
if (gpdir)
|
||||
debugfs_remove(gpdir);
|
||||
debugfs_remove(rcudir);
|
||||
out:
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __init rcupreempt_trace_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_init(&rcupreempt_trace_mutex);
|
||||
rcupreempt_trace_buf = kmalloc(RCUPREEMPT_TRACE_BUF_SIZE, GFP_KERNEL);
|
||||
if (!rcupreempt_trace_buf)
|
||||
return 1;
|
||||
ret = rcupreempt_debugfs_init();
|
||||
if (ret)
|
||||
kfree(rcupreempt_trace_buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit rcupreempt_trace_cleanup(void)
|
||||
{
|
||||
debugfs_remove(statdir);
|
||||
debugfs_remove(gpdir);
|
||||
debugfs_remove(ctrsdir);
|
||||
debugfs_remove(rcudir);
|
||||
kfree(rcupreempt_trace_buf);
|
||||
}
|
||||
|
||||
|
||||
module_init(rcupreempt_trace_init);
|
||||
module_exit(rcupreempt_trace_cleanup);
|
@@ -257,14 +257,14 @@ struct rcu_torture_ops {
|
||||
void (*init)(void);
|
||||
void (*cleanup)(void);
|
||||
int (*readlock)(void);
|
||||
void (*readdelay)(struct rcu_random_state *rrsp);
|
||||
void (*read_delay)(struct rcu_random_state *rrsp);
|
||||
void (*readunlock)(int idx);
|
||||
int (*completed)(void);
|
||||
void (*deferredfree)(struct rcu_torture *p);
|
||||
void (*deferred_free)(struct rcu_torture *p);
|
||||
void (*sync)(void);
|
||||
void (*cb_barrier)(void);
|
||||
int (*stats)(char *page);
|
||||
int irqcapable;
|
||||
int irq_capable;
|
||||
char *name;
|
||||
};
|
||||
static struct rcu_torture_ops *cur_ops = NULL;
|
||||
@@ -320,7 +320,7 @@ rcu_torture_cb(struct rcu_head *p)
|
||||
rp->rtort_mbtest = 0;
|
||||
rcu_torture_free(rp);
|
||||
} else
|
||||
cur_ops->deferredfree(rp);
|
||||
cur_ops->deferred_free(rp);
|
||||
}
|
||||
|
||||
static void rcu_torture_deferred_free(struct rcu_torture *p)
|
||||
@@ -329,18 +329,18 @@ static void rcu_torture_deferred_free(struct rcu_torture *p)
|
||||
}
|
||||
|
||||
static struct rcu_torture_ops rcu_ops = {
|
||||
.init = NULL,
|
||||
.cleanup = NULL,
|
||||
.readlock = rcu_torture_read_lock,
|
||||
.readdelay = rcu_read_delay,
|
||||
.readunlock = rcu_torture_read_unlock,
|
||||
.completed = rcu_torture_completed,
|
||||
.deferredfree = rcu_torture_deferred_free,
|
||||
.sync = synchronize_rcu,
|
||||
.cb_barrier = rcu_barrier,
|
||||
.stats = NULL,
|
||||
.irqcapable = 1,
|
||||
.name = "rcu"
|
||||
.init = NULL,
|
||||
.cleanup = NULL,
|
||||
.readlock = rcu_torture_read_lock,
|
||||
.read_delay = rcu_read_delay,
|
||||
.readunlock = rcu_torture_read_unlock,
|
||||
.completed = rcu_torture_completed,
|
||||
.deferred_free = rcu_torture_deferred_free,
|
||||
.sync = synchronize_rcu,
|
||||
.cb_barrier = rcu_barrier,
|
||||
.stats = NULL,
|
||||
.irq_capable = 1,
|
||||
.name = "rcu"
|
||||
};
|
||||
|
||||
static void rcu_sync_torture_deferred_free(struct rcu_torture *p)
|
||||
@@ -370,18 +370,18 @@ static void rcu_sync_torture_init(void)
|
||||
}
|
||||
|
||||
static struct rcu_torture_ops rcu_sync_ops = {
|
||||
.init = rcu_sync_torture_init,
|
||||
.cleanup = NULL,
|
||||
.readlock = rcu_torture_read_lock,
|
||||
.readdelay = rcu_read_delay,
|
||||
.readunlock = rcu_torture_read_unlock,
|
||||
.completed = rcu_torture_completed,
|
||||
.deferredfree = rcu_sync_torture_deferred_free,
|
||||
.sync = synchronize_rcu,
|
||||
.cb_barrier = NULL,
|
||||
.stats = NULL,
|
||||
.irqcapable = 1,
|
||||
.name = "rcu_sync"
|
||||
.init = rcu_sync_torture_init,
|
||||
.cleanup = NULL,
|
||||
.readlock = rcu_torture_read_lock,
|
||||
.read_delay = rcu_read_delay,
|
||||
.readunlock = rcu_torture_read_unlock,
|
||||
.completed = rcu_torture_completed,
|
||||
.deferred_free = rcu_sync_torture_deferred_free,
|
||||
.sync = synchronize_rcu,
|
||||
.cb_barrier = NULL,
|
||||
.stats = NULL,
|
||||
.irq_capable = 1,
|
||||
.name = "rcu_sync"
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -432,33 +432,33 @@ static void rcu_bh_torture_synchronize(void)
|
||||
}
|
||||
|
||||
static struct rcu_torture_ops rcu_bh_ops = {
|
||||
.init = NULL,
|
||||
.cleanup = NULL,
|
||||
.readlock = rcu_bh_torture_read_lock,
|
||||
.readdelay = rcu_read_delay, /* just reuse rcu's version. */
|
||||
.readunlock = rcu_bh_torture_read_unlock,
|
||||
.completed = rcu_bh_torture_completed,
|
||||
.deferredfree = rcu_bh_torture_deferred_free,
|
||||
.sync = rcu_bh_torture_synchronize,
|
||||
.cb_barrier = rcu_barrier_bh,
|
||||
.stats = NULL,
|
||||
.irqcapable = 1,
|
||||
.name = "rcu_bh"
|
||||
.init = NULL,
|
||||
.cleanup = NULL,
|
||||
.readlock = rcu_bh_torture_read_lock,
|
||||
.read_delay = rcu_read_delay, /* just reuse rcu's version. */
|
||||
.readunlock = rcu_bh_torture_read_unlock,
|
||||
.completed = rcu_bh_torture_completed,
|
||||
.deferred_free = rcu_bh_torture_deferred_free,
|
||||
.sync = rcu_bh_torture_synchronize,
|
||||
.cb_barrier = rcu_barrier_bh,
|
||||
.stats = NULL,
|
||||
.irq_capable = 1,
|
||||
.name = "rcu_bh"
|
||||
};
|
||||
|
||||
static struct rcu_torture_ops rcu_bh_sync_ops = {
|
||||
.init = rcu_sync_torture_init,
|
||||
.cleanup = NULL,
|
||||
.readlock = rcu_bh_torture_read_lock,
|
||||
.readdelay = rcu_read_delay, /* just reuse rcu's version. */
|
||||
.readunlock = rcu_bh_torture_read_unlock,
|
||||
.completed = rcu_bh_torture_completed,
|
||||
.deferredfree = rcu_sync_torture_deferred_free,
|
||||
.sync = rcu_bh_torture_synchronize,
|
||||
.cb_barrier = NULL,
|
||||
.stats = NULL,
|
||||
.irqcapable = 1,
|
||||
.name = "rcu_bh_sync"
|
||||
.init = rcu_sync_torture_init,
|
||||
.cleanup = NULL,
|
||||
.readlock = rcu_bh_torture_read_lock,
|
||||
.read_delay = rcu_read_delay, /* just reuse rcu's version. */
|
||||
.readunlock = rcu_bh_torture_read_unlock,
|
||||
.completed = rcu_bh_torture_completed,
|
||||
.deferred_free = rcu_sync_torture_deferred_free,
|
||||
.sync = rcu_bh_torture_synchronize,
|
||||
.cb_barrier = NULL,
|
||||
.stats = NULL,
|
||||
.irq_capable = 1,
|
||||
.name = "rcu_bh_sync"
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -530,17 +530,17 @@ static int srcu_torture_stats(char *page)
|
||||
}
|
||||
|
||||
static struct rcu_torture_ops srcu_ops = {
|
||||
.init = srcu_torture_init,
|
||||
.cleanup = srcu_torture_cleanup,
|
||||
.readlock = srcu_torture_read_lock,
|
||||
.readdelay = srcu_read_delay,
|
||||
.readunlock = srcu_torture_read_unlock,
|
||||
.completed = srcu_torture_completed,
|
||||
.deferredfree = rcu_sync_torture_deferred_free,
|
||||
.sync = srcu_torture_synchronize,
|
||||
.cb_barrier = NULL,
|
||||
.stats = srcu_torture_stats,
|
||||
.name = "srcu"
|
||||
.init = srcu_torture_init,
|
||||
.cleanup = srcu_torture_cleanup,
|
||||
.readlock = srcu_torture_read_lock,
|
||||
.read_delay = srcu_read_delay,
|
||||
.readunlock = srcu_torture_read_unlock,
|
||||
.completed = srcu_torture_completed,
|
||||
.deferred_free = rcu_sync_torture_deferred_free,
|
||||
.sync = srcu_torture_synchronize,
|
||||
.cb_barrier = NULL,
|
||||
.stats = srcu_torture_stats,
|
||||
.name = "srcu"
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -574,32 +574,49 @@ static void sched_torture_synchronize(void)
|
||||
}
|
||||
|
||||
static struct rcu_torture_ops sched_ops = {
|
||||
.init = rcu_sync_torture_init,
|
||||
.cleanup = NULL,
|
||||
.readlock = sched_torture_read_lock,
|
||||
.readdelay = rcu_read_delay, /* just reuse rcu's version. */
|
||||
.readunlock = sched_torture_read_unlock,
|
||||
.completed = sched_torture_completed,
|
||||
.deferredfree = rcu_sched_torture_deferred_free,
|
||||
.sync = sched_torture_synchronize,
|
||||
.cb_barrier = rcu_barrier_sched,
|
||||
.stats = NULL,
|
||||
.irqcapable = 1,
|
||||
.name = "sched"
|
||||
.init = rcu_sync_torture_init,
|
||||
.cleanup = NULL,
|
||||
.readlock = sched_torture_read_lock,
|
||||
.read_delay = rcu_read_delay, /* just reuse rcu's version. */
|
||||
.readunlock = sched_torture_read_unlock,
|
||||
.completed = sched_torture_completed,
|
||||
.deferred_free = rcu_sched_torture_deferred_free,
|
||||
.sync = sched_torture_synchronize,
|
||||
.cb_barrier = rcu_barrier_sched,
|
||||
.stats = NULL,
|
||||
.irq_capable = 1,
|
||||
.name = "sched"
|
||||
};
|
||||
|
||||
static struct rcu_torture_ops sched_ops_sync = {
|
||||
.init = rcu_sync_torture_init,
|
||||
.cleanup = NULL,
|
||||
.readlock = sched_torture_read_lock,
|
||||
.readdelay = rcu_read_delay, /* just reuse rcu's version. */
|
||||
.readunlock = sched_torture_read_unlock,
|
||||
.completed = sched_torture_completed,
|
||||
.deferredfree = rcu_sync_torture_deferred_free,
|
||||
.sync = sched_torture_synchronize,
|
||||
.cb_barrier = NULL,
|
||||
.stats = NULL,
|
||||
.name = "sched_sync"
|
||||
.init = rcu_sync_torture_init,
|
||||
.cleanup = NULL,
|
||||
.readlock = sched_torture_read_lock,
|
||||
.read_delay = rcu_read_delay, /* just reuse rcu's version. */
|
||||
.readunlock = sched_torture_read_unlock,
|
||||
.completed = sched_torture_completed,
|
||||
.deferred_free = rcu_sync_torture_deferred_free,
|
||||
.sync = sched_torture_synchronize,
|
||||
.cb_barrier = NULL,
|
||||
.stats = NULL,
|
||||
.name = "sched_sync"
|
||||
};
|
||||
|
||||
extern int rcu_expedited_torture_stats(char *page);
|
||||
|
||||
static struct rcu_torture_ops sched_expedited_ops = {
|
||||
.init = rcu_sync_torture_init,
|
||||
.cleanup = NULL,
|
||||
.readlock = sched_torture_read_lock,
|
||||
.read_delay = rcu_read_delay, /* just reuse rcu's version. */
|
||||
.readunlock = sched_torture_read_unlock,
|
||||
.completed = sched_torture_completed,
|
||||
.deferred_free = rcu_sync_torture_deferred_free,
|
||||
.sync = synchronize_sched_expedited,
|
||||
.cb_barrier = NULL,
|
||||
.stats = rcu_expedited_torture_stats,
|
||||
.irq_capable = 1,
|
||||
.name = "sched_expedited"
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -635,7 +652,7 @@ rcu_torture_writer(void *arg)
|
||||
i = RCU_TORTURE_PIPE_LEN;
|
||||
atomic_inc(&rcu_torture_wcount[i]);
|
||||
old_rp->rtort_pipe_count++;
|
||||
cur_ops->deferredfree(old_rp);
|
||||
cur_ops->deferred_free(old_rp);
|
||||
}
|
||||
rcu_torture_current_version++;
|
||||
oldbatch = cur_ops->completed();
|
||||
@@ -700,7 +717,7 @@ static void rcu_torture_timer(unsigned long unused)
|
||||
if (p->rtort_mbtest == 0)
|
||||
atomic_inc(&n_rcu_torture_mberror);
|
||||
spin_lock(&rand_lock);
|
||||
cur_ops->readdelay(&rand);
|
||||
cur_ops->read_delay(&rand);
|
||||
n_rcu_torture_timers++;
|
||||
spin_unlock(&rand_lock);
|
||||
preempt_disable();
|
||||
@@ -738,11 +755,11 @@ rcu_torture_reader(void *arg)
|
||||
|
||||
VERBOSE_PRINTK_STRING("rcu_torture_reader task started");
|
||||
set_user_nice(current, 19);
|
||||
if (irqreader && cur_ops->irqcapable)
|
||||
if (irqreader && cur_ops->irq_capable)
|
||||
setup_timer_on_stack(&t, rcu_torture_timer, 0);
|
||||
|
||||
do {
|
||||
if (irqreader && cur_ops->irqcapable) {
|
||||
if (irqreader && cur_ops->irq_capable) {
|
||||
if (!timer_pending(&t))
|
||||
mod_timer(&t, 1);
|
||||
}
|
||||
@@ -757,7 +774,7 @@ rcu_torture_reader(void *arg)
|
||||
}
|
||||
if (p->rtort_mbtest == 0)
|
||||
atomic_inc(&n_rcu_torture_mberror);
|
||||
cur_ops->readdelay(&rand);
|
||||
cur_ops->read_delay(&rand);
|
||||
preempt_disable();
|
||||
pipe_count = p->rtort_pipe_count;
|
||||
if (pipe_count > RCU_TORTURE_PIPE_LEN) {
|
||||
@@ -778,7 +795,7 @@ rcu_torture_reader(void *arg)
|
||||
} 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)
|
||||
if (irqreader && cur_ops->irq_capable)
|
||||
del_timer_sync(&t);
|
||||
while (!kthread_should_stop())
|
||||
schedule_timeout_uninterruptible(1);
|
||||
@@ -1078,6 +1095,7 @@ rcu_torture_init(void)
|
||||
int firsterr = 0;
|
||||
static struct rcu_torture_ops *torture_ops[] =
|
||||
{ &rcu_ops, &rcu_sync_ops, &rcu_bh_ops, &rcu_bh_sync_ops,
|
||||
&sched_expedited_ops,
|
||||
&srcu_ops, &sched_ops, &sched_ops_sync, };
|
||||
|
||||
mutex_lock(&fullstop_mutex);
|
||||
|
282
kernel/rcutree.c
282
kernel/rcutree.c
@@ -35,6 +35,7 @@
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/nmi.h>
|
||||
#include <asm/atomic.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/module.h>
|
||||
@@ -46,6 +47,8 @@
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/time.h>
|
||||
|
||||
#include "rcutree.h"
|
||||
|
||||
#ifdef CONFIG_DEBUG_LOCK_ALLOC
|
||||
static struct lock_class_key rcu_lock_key;
|
||||
struct lockdep_map rcu_lock_map =
|
||||
@@ -72,30 +75,59 @@ EXPORT_SYMBOL_GPL(rcu_lock_map);
|
||||
.n_force_qs_ngp = 0, \
|
||||
}
|
||||
|
||||
struct rcu_state rcu_state = RCU_STATE_INITIALIZER(rcu_state);
|
||||
DEFINE_PER_CPU(struct rcu_data, rcu_data);
|
||||
struct rcu_state rcu_sched_state = RCU_STATE_INITIALIZER(rcu_sched_state);
|
||||
DEFINE_PER_CPU(struct rcu_data, rcu_sched_data);
|
||||
|
||||
struct rcu_state rcu_bh_state = RCU_STATE_INITIALIZER(rcu_bh_state);
|
||||
DEFINE_PER_CPU(struct rcu_data, rcu_bh_data);
|
||||
|
||||
extern long rcu_batches_completed_sched(void);
|
||||
static struct rcu_node *rcu_get_root(struct rcu_state *rsp);
|
||||
static void cpu_quiet_msk(unsigned long mask, struct rcu_state *rsp,
|
||||
struct rcu_node *rnp, unsigned long flags);
|
||||
static void cpu_quiet_msk_finish(struct rcu_state *rsp, unsigned long flags);
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
static void __rcu_offline_cpu(int cpu, struct rcu_state *rsp);
|
||||
#endif /* #ifdef CONFIG_HOTPLUG_CPU */
|
||||
static void __rcu_process_callbacks(struct rcu_state *rsp,
|
||||
struct rcu_data *rdp);
|
||||
static void __call_rcu(struct rcu_head *head,
|
||||
void (*func)(struct rcu_head *rcu),
|
||||
struct rcu_state *rsp);
|
||||
static int __rcu_pending(struct rcu_state *rsp, struct rcu_data *rdp);
|
||||
static void __cpuinit rcu_init_percpu_data(int cpu, struct rcu_state *rsp,
|
||||
int preemptable);
|
||||
|
||||
#include "rcutree_plugin.h"
|
||||
|
||||
/*
|
||||
* Increment the quiescent state counter.
|
||||
* The counter is a bit degenerated: We do not need to know
|
||||
* Note a quiescent state. Because we do not need to know
|
||||
* how many quiescent states passed, just if there was at least
|
||||
* one since the start of the grace period. Thus just a flag.
|
||||
* one since the start of the grace period, this just sets a flag.
|
||||
*/
|
||||
void rcu_qsctr_inc(int cpu)
|
||||
void rcu_sched_qs(int cpu)
|
||||
{
|
||||
struct rcu_data *rdp = &per_cpu(rcu_data, cpu);
|
||||
unsigned long flags;
|
||||
struct rcu_data *rdp;
|
||||
|
||||
local_irq_save(flags);
|
||||
rdp = &per_cpu(rcu_sched_data, cpu);
|
||||
rdp->passed_quiesc = 1;
|
||||
rdp->passed_quiesc_completed = rdp->completed;
|
||||
rcu_preempt_qs(cpu);
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
void rcu_bh_qsctr_inc(int cpu)
|
||||
void rcu_bh_qs(int cpu)
|
||||
{
|
||||
struct rcu_data *rdp = &per_cpu(rcu_bh_data, cpu);
|
||||
unsigned long flags;
|
||||
struct rcu_data *rdp;
|
||||
|
||||
local_irq_save(flags);
|
||||
rdp = &per_cpu(rcu_bh_data, cpu);
|
||||
rdp->passed_quiesc = 1;
|
||||
rdp->passed_quiesc_completed = rdp->completed;
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NO_HZ
|
||||
@@ -110,15 +142,16 @@ static int qhimark = 10000; /* If this many pending, ignore blimit. */
|
||||
static int qlowmark = 100; /* Once only this many pending, use blimit. */
|
||||
|
||||
static void force_quiescent_state(struct rcu_state *rsp, int relaxed);
|
||||
static int rcu_pending(int cpu);
|
||||
|
||||
/*
|
||||
* Return the number of RCU batches processed thus far for debug & stats.
|
||||
* Return the number of RCU-sched batches processed thus far for debug & stats.
|
||||
*/
|
||||
long rcu_batches_completed(void)
|
||||
long rcu_batches_completed_sched(void)
|
||||
{
|
||||
return rcu_state.completed;
|
||||
return rcu_sched_state.completed;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rcu_batches_completed);
|
||||
EXPORT_SYMBOL_GPL(rcu_batches_completed_sched);
|
||||
|
||||
/*
|
||||
* Return the number of RCU BH batches processed thus far for debug & stats.
|
||||
@@ -181,6 +214,10 @@ static int rcu_implicit_offline_qs(struct rcu_data *rdp)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* If preemptable RCU, no point in sending reschedule IPI. */
|
||||
if (rdp->preemptable)
|
||||
return 0;
|
||||
|
||||
/* The CPU is online, so send it a reschedule IPI. */
|
||||
if (rdp->cpu != smp_processor_id())
|
||||
smp_send_reschedule(rdp->cpu);
|
||||
@@ -193,7 +230,6 @@ static int rcu_implicit_offline_qs(struct rcu_data *rdp)
|
||||
#endif /* #ifdef CONFIG_SMP */
|
||||
|
||||
#ifdef CONFIG_NO_HZ
|
||||
static DEFINE_RATELIMIT_STATE(rcu_rs, 10 * HZ, 5);
|
||||
|
||||
/**
|
||||
* rcu_enter_nohz - inform RCU that current CPU is entering nohz
|
||||
@@ -213,7 +249,7 @@ void rcu_enter_nohz(void)
|
||||
rdtp = &__get_cpu_var(rcu_dynticks);
|
||||
rdtp->dynticks++;
|
||||
rdtp->dynticks_nesting--;
|
||||
WARN_ON_RATELIMIT(rdtp->dynticks & 0x1, &rcu_rs);
|
||||
WARN_ON_ONCE(rdtp->dynticks & 0x1);
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
@@ -232,7 +268,7 @@ void rcu_exit_nohz(void)
|
||||
rdtp = &__get_cpu_var(rcu_dynticks);
|
||||
rdtp->dynticks++;
|
||||
rdtp->dynticks_nesting++;
|
||||
WARN_ON_RATELIMIT(!(rdtp->dynticks & 0x1), &rcu_rs);
|
||||
WARN_ON_ONCE(!(rdtp->dynticks & 0x1));
|
||||
local_irq_restore(flags);
|
||||
smp_mb(); /* CPUs seeing ++ must see later RCU read-side crit sects */
|
||||
}
|
||||
@@ -251,7 +287,7 @@ void rcu_nmi_enter(void)
|
||||
if (rdtp->dynticks & 0x1)
|
||||
return;
|
||||
rdtp->dynticks_nmi++;
|
||||
WARN_ON_RATELIMIT(!(rdtp->dynticks_nmi & 0x1), &rcu_rs);
|
||||
WARN_ON_ONCE(!(rdtp->dynticks_nmi & 0x1));
|
||||
smp_mb(); /* CPUs seeing ++ must see later RCU read-side crit sects */
|
||||
}
|
||||
|
||||
@@ -270,7 +306,7 @@ void rcu_nmi_exit(void)
|
||||
return;
|
||||
smp_mb(); /* CPUs seeing ++ must see prior RCU read-side crit sects */
|
||||
rdtp->dynticks_nmi++;
|
||||
WARN_ON_RATELIMIT(rdtp->dynticks_nmi & 0x1, &rcu_rs);
|
||||
WARN_ON_ONCE(rdtp->dynticks_nmi & 0x1);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -286,7 +322,7 @@ void rcu_irq_enter(void)
|
||||
if (rdtp->dynticks_nesting++)
|
||||
return;
|
||||
rdtp->dynticks++;
|
||||
WARN_ON_RATELIMIT(!(rdtp->dynticks & 0x1), &rcu_rs);
|
||||
WARN_ON_ONCE(!(rdtp->dynticks & 0x1));
|
||||
smp_mb(); /* CPUs seeing ++ must see later RCU read-side crit sects */
|
||||
}
|
||||
|
||||
@@ -305,10 +341,10 @@ void rcu_irq_exit(void)
|
||||
return;
|
||||
smp_mb(); /* CPUs seeing ++ must see prior RCU read-side crit sects */
|
||||
rdtp->dynticks++;
|
||||
WARN_ON_RATELIMIT(rdtp->dynticks & 0x1, &rcu_rs);
|
||||
WARN_ON_ONCE(rdtp->dynticks & 0x1);
|
||||
|
||||
/* If the interrupt queued a callback, get out of dyntick mode. */
|
||||
if (__get_cpu_var(rcu_data).nxtlist ||
|
||||
if (__get_cpu_var(rcu_sched_data).nxtlist ||
|
||||
__get_cpu_var(rcu_bh_data).nxtlist)
|
||||
set_need_resched();
|
||||
}
|
||||
@@ -461,6 +497,7 @@ static void print_other_cpu_stall(struct rcu_state *rsp)
|
||||
|
||||
printk(KERN_ERR "INFO: RCU detected CPU stalls:");
|
||||
for (; rnp_cur < rnp_end; rnp_cur++) {
|
||||
rcu_print_task_stall(rnp);
|
||||
if (rnp_cur->qsmask == 0)
|
||||
continue;
|
||||
for (cpu = 0; cpu <= rnp_cur->grphi - rnp_cur->grplo; cpu++)
|
||||
@@ -469,6 +506,8 @@ static void print_other_cpu_stall(struct rcu_state *rsp)
|
||||
}
|
||||
printk(" (detected by %d, t=%ld jiffies)\n",
|
||||
smp_processor_id(), (long)(jiffies - rsp->gp_start));
|
||||
trigger_all_cpu_backtrace();
|
||||
|
||||
force_quiescent_state(rsp, 0); /* Kick them all. */
|
||||
}
|
||||
|
||||
@@ -479,12 +518,14 @@ static void print_cpu_stall(struct rcu_state *rsp)
|
||||
|
||||
printk(KERN_ERR "INFO: RCU detected CPU %d stall (t=%lu jiffies)\n",
|
||||
smp_processor_id(), jiffies - rsp->gp_start);
|
||||
dump_stack();
|
||||
trigger_all_cpu_backtrace();
|
||||
|
||||
spin_lock_irqsave(&rnp->lock, flags);
|
||||
if ((long)(jiffies - rsp->jiffies_stall) >= 0)
|
||||
rsp->jiffies_stall =
|
||||
jiffies + RCU_SECONDS_TILL_STALL_RECHECK;
|
||||
spin_unlock_irqrestore(&rnp->lock, flags);
|
||||
|
||||
set_need_resched(); /* kick ourselves to get things going. */
|
||||
}
|
||||
|
||||
@@ -673,6 +714,19 @@ rcu_process_gp_end(struct rcu_state *rsp, struct rcu_data *rdp)
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Clean up after the prior grace period and let rcu_start_gp() start up
|
||||
* the next grace period if one is needed. Note that the caller must
|
||||
* hold rnp->lock, as required by rcu_start_gp(), which will release it.
|
||||
*/
|
||||
static void cpu_quiet_msk_finish(struct rcu_state *rsp, unsigned long flags)
|
||||
__releases(rnp->lock)
|
||||
{
|
||||
rsp->completed = rsp->gpnum;
|
||||
rcu_process_gp_end(rsp, rsp->rda[smp_processor_id()]);
|
||||
rcu_start_gp(rsp, flags); /* releases root node's rnp->lock. */
|
||||
}
|
||||
|
||||
/*
|
||||
* Similar to cpu_quiet(), for which it is a helper function. Allows
|
||||
* a group of CPUs to be quieted at one go, though all the CPUs in the
|
||||
@@ -694,7 +748,7 @@ cpu_quiet_msk(unsigned long mask, struct rcu_state *rsp, struct rcu_node *rnp,
|
||||
return;
|
||||
}
|
||||
rnp->qsmask &= ~mask;
|
||||
if (rnp->qsmask != 0) {
|
||||
if (rnp->qsmask != 0 || rcu_preempted_readers(rnp)) {
|
||||
|
||||
/* Other bits still set at this level, so done. */
|
||||
spin_unlock_irqrestore(&rnp->lock, flags);
|
||||
@@ -714,14 +768,10 @@ cpu_quiet_msk(unsigned long mask, struct rcu_state *rsp, struct rcu_node *rnp,
|
||||
|
||||
/*
|
||||
* Get here if we are the last CPU to pass through a quiescent
|
||||
* state for this grace period. Clean up and let rcu_start_gp()
|
||||
* start up the next grace period if one is needed. Note that
|
||||
* we still hold rnp->lock, as required by rcu_start_gp(), which
|
||||
* will release it.
|
||||
* state for this grace period. Invoke cpu_quiet_msk_finish()
|
||||
* to clean up and start the next grace period if one is needed.
|
||||
*/
|
||||
rsp->completed = rsp->gpnum;
|
||||
rcu_process_gp_end(rsp, rsp->rda[smp_processor_id()]);
|
||||
rcu_start_gp(rsp, flags); /* releases rnp->lock. */
|
||||
cpu_quiet_msk_finish(rsp, flags); /* releases rnp->lock. */
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -828,11 +878,12 @@ static void __rcu_offline_cpu(int cpu, struct rcu_state *rsp)
|
||||
spin_lock(&rnp->lock); /* irqs already disabled. */
|
||||
rnp->qsmaskinit &= ~mask;
|
||||
if (rnp->qsmaskinit != 0) {
|
||||
spin_unlock(&rnp->lock); /* irqs already disabled. */
|
||||
spin_unlock(&rnp->lock); /* irqs remain disabled. */
|
||||
break;
|
||||
}
|
||||
rcu_preempt_offline_tasks(rsp, rnp);
|
||||
mask = rnp->grpmask;
|
||||
spin_unlock(&rnp->lock); /* irqs already disabled. */
|
||||
spin_unlock(&rnp->lock); /* irqs remain disabled. */
|
||||
rnp = rnp->parent;
|
||||
} while (rnp != NULL);
|
||||
lastcomp = rsp->completed;
|
||||
@@ -845,7 +896,7 @@ static void __rcu_offline_cpu(int cpu, struct rcu_state *rsp)
|
||||
/*
|
||||
* Move callbacks from the outgoing CPU to the running CPU.
|
||||
* Note that the outgoing CPU is now quiscent, so it is now
|
||||
* (uncharacteristically) safe to access it rcu_data structure.
|
||||
* (uncharacteristically) safe to access its rcu_data structure.
|
||||
* Note also that we must carefully retain the order of the
|
||||
* outgoing CPU's callbacks in order for rcu_barrier() to work
|
||||
* correctly. Finally, note that we start all the callbacks
|
||||
@@ -876,8 +927,9 @@ static void __rcu_offline_cpu(int cpu, struct rcu_state *rsp)
|
||||
*/
|
||||
static void rcu_offline_cpu(int cpu)
|
||||
{
|
||||
__rcu_offline_cpu(cpu, &rcu_state);
|
||||
__rcu_offline_cpu(cpu, &rcu_sched_state);
|
||||
__rcu_offline_cpu(cpu, &rcu_bh_state);
|
||||
rcu_preempt_offline_cpu(cpu);
|
||||
}
|
||||
|
||||
#else /* #ifdef CONFIG_HOTPLUG_CPU */
|
||||
@@ -963,6 +1015,8 @@ static void rcu_do_batch(struct rcu_data *rdp)
|
||||
*/
|
||||
void rcu_check_callbacks(int cpu, int user)
|
||||
{
|
||||
if (!rcu_pending(cpu))
|
||||
return; /* if nothing for RCU to do. */
|
||||
if (user ||
|
||||
(idle_cpu(cpu) && rcu_scheduler_active &&
|
||||
!in_softirq() && hardirq_count() <= (1 << HARDIRQ_SHIFT))) {
|
||||
@@ -971,17 +1025,16 @@ void rcu_check_callbacks(int cpu, int user)
|
||||
* Get here if this CPU took its interrupt from user
|
||||
* mode or from the idle loop, and if this is not a
|
||||
* nested interrupt. In this case, the CPU is in
|
||||
* a quiescent state, so count it.
|
||||
* a quiescent state, so note it.
|
||||
*
|
||||
* No memory barrier is required here because both
|
||||
* rcu_qsctr_inc() and rcu_bh_qsctr_inc() reference
|
||||
* only CPU-local variables that other CPUs neither
|
||||
* access nor modify, at least not while the corresponding
|
||||
* CPU is online.
|
||||
* rcu_sched_qs() and rcu_bh_qs() reference only CPU-local
|
||||
* variables that other CPUs neither access nor modify,
|
||||
* at least not while the corresponding CPU is online.
|
||||
*/
|
||||
|
||||
rcu_qsctr_inc(cpu);
|
||||
rcu_bh_qsctr_inc(cpu);
|
||||
rcu_sched_qs(cpu);
|
||||
rcu_bh_qs(cpu);
|
||||
|
||||
} else if (!in_softirq()) {
|
||||
|
||||
@@ -989,11 +1042,12 @@ void rcu_check_callbacks(int cpu, int user)
|
||||
* Get here if this CPU did not take its interrupt from
|
||||
* softirq, in other words, if it is not interrupting
|
||||
* a rcu_bh read-side critical section. This is an _bh
|
||||
* critical section, so count it.
|
||||
* critical section, so note it.
|
||||
*/
|
||||
|
||||
rcu_bh_qsctr_inc(cpu);
|
||||
rcu_bh_qs(cpu);
|
||||
}
|
||||
rcu_preempt_check_callbacks(cpu);
|
||||
raise_softirq(RCU_SOFTIRQ);
|
||||
}
|
||||
|
||||
@@ -1132,6 +1186,8 @@ __rcu_process_callbacks(struct rcu_state *rsp, struct rcu_data *rdp)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
WARN_ON_ONCE(rdp->beenonline == 0);
|
||||
|
||||
/*
|
||||
* If an RCU GP has gone long enough, go check for dyntick
|
||||
* idle CPUs and, if needed, send resched IPIs.
|
||||
@@ -1170,8 +1226,10 @@ static void rcu_process_callbacks(struct softirq_action *unused)
|
||||
*/
|
||||
smp_mb(); /* See above block comment. */
|
||||
|
||||
__rcu_process_callbacks(&rcu_state, &__get_cpu_var(rcu_data));
|
||||
__rcu_process_callbacks(&rcu_sched_state,
|
||||
&__get_cpu_var(rcu_sched_data));
|
||||
__rcu_process_callbacks(&rcu_bh_state, &__get_cpu_var(rcu_bh_data));
|
||||
rcu_preempt_process_callbacks();
|
||||
|
||||
/*
|
||||
* Memory references from any later RCU read-side critical sections
|
||||
@@ -1227,13 +1285,13 @@ __call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu),
|
||||
}
|
||||
|
||||
/*
|
||||
* Queue an RCU callback for invocation after a grace period.
|
||||
* Queue an RCU-sched callback for invocation after a grace period.
|
||||
*/
|
||||
void call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu))
|
||||
void call_rcu_sched(struct rcu_head *head, void (*func)(struct rcu_head *rcu))
|
||||
{
|
||||
__call_rcu(head, func, &rcu_state);
|
||||
__call_rcu(head, func, &rcu_sched_state);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(call_rcu);
|
||||
EXPORT_SYMBOL_GPL(call_rcu_sched);
|
||||
|
||||
/*
|
||||
* Queue an RCU for invocation after a quicker grace period.
|
||||
@@ -1305,10 +1363,11 @@ static int __rcu_pending(struct rcu_state *rsp, struct rcu_data *rdp)
|
||||
* by the current CPU, returning 1 if so. This function is part of the
|
||||
* RCU implementation; it is -not- an exported member of the RCU API.
|
||||
*/
|
||||
int rcu_pending(int cpu)
|
||||
static int rcu_pending(int cpu)
|
||||
{
|
||||
return __rcu_pending(&rcu_state, &per_cpu(rcu_data, cpu)) ||
|
||||
__rcu_pending(&rcu_bh_state, &per_cpu(rcu_bh_data, cpu));
|
||||
return __rcu_pending(&rcu_sched_state, &per_cpu(rcu_sched_data, cpu)) ||
|
||||
__rcu_pending(&rcu_bh_state, &per_cpu(rcu_bh_data, cpu)) ||
|
||||
rcu_preempt_pending(cpu);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1320,27 +1379,46 @@ int rcu_pending(int cpu)
|
||||
int rcu_needs_cpu(int cpu)
|
||||
{
|
||||
/* RCU callbacks either ready or pending? */
|
||||
return per_cpu(rcu_data, cpu).nxtlist ||
|
||||
per_cpu(rcu_bh_data, cpu).nxtlist;
|
||||
return per_cpu(rcu_sched_data, cpu).nxtlist ||
|
||||
per_cpu(rcu_bh_data, cpu).nxtlist ||
|
||||
rcu_preempt_needs_cpu(cpu);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize a CPU's per-CPU RCU data. We take this "scorched earth"
|
||||
* approach so that we don't have to worry about how long the CPU has
|
||||
* been gone, or whether it ever was online previously. We do trust the
|
||||
* ->mynode field, as it is constant for a given struct rcu_data and
|
||||
* initialized during early boot.
|
||||
*
|
||||
* Note that only one online or offline event can be happening at a given
|
||||
* time. Note also that we can accept some slop in the rsp->completed
|
||||
* access due to the fact that this CPU cannot possibly have any RCU
|
||||
* callbacks in flight yet.
|
||||
* Do boot-time initialization of a CPU's per-CPU RCU data.
|
||||
*/
|
||||
static void __cpuinit
|
||||
rcu_init_percpu_data(int cpu, struct rcu_state *rsp)
|
||||
static void __init
|
||||
rcu_boot_init_percpu_data(int cpu, struct rcu_state *rsp)
|
||||
{
|
||||
unsigned long flags;
|
||||
int i;
|
||||
struct rcu_data *rdp = rsp->rda[cpu];
|
||||
struct rcu_node *rnp = rcu_get_root(rsp);
|
||||
|
||||
/* Set up local state, ensuring consistent view of global state. */
|
||||
spin_lock_irqsave(&rnp->lock, flags);
|
||||
rdp->grpmask = 1UL << (cpu - rdp->mynode->grplo);
|
||||
rdp->nxtlist = NULL;
|
||||
for (i = 0; i < RCU_NEXT_SIZE; i++)
|
||||
rdp->nxttail[i] = &rdp->nxtlist;
|
||||
rdp->qlen = 0;
|
||||
#ifdef CONFIG_NO_HZ
|
||||
rdp->dynticks = &per_cpu(rcu_dynticks, cpu);
|
||||
#endif /* #ifdef CONFIG_NO_HZ */
|
||||
rdp->cpu = cpu;
|
||||
spin_unlock_irqrestore(&rnp->lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize a CPU's per-CPU RCU data. Note that only one online or
|
||||
* offline event can be happening at a given time. Note also that we
|
||||
* can accept some slop in the rsp->completed access due to the fact
|
||||
* that this CPU cannot possibly have any RCU callbacks in flight yet.
|
||||
*/
|
||||
static void __cpuinit
|
||||
rcu_init_percpu_data(int cpu, struct rcu_state *rsp, int preemptable)
|
||||
{
|
||||
unsigned long flags;
|
||||
long lastcomp;
|
||||
unsigned long mask;
|
||||
struct rcu_data *rdp = rsp->rda[cpu];
|
||||
@@ -1354,17 +1432,9 @@ rcu_init_percpu_data(int cpu, struct rcu_state *rsp)
|
||||
rdp->passed_quiesc = 0; /* We could be racing with new GP, */
|
||||
rdp->qs_pending = 1; /* so set up to respond to current GP. */
|
||||
rdp->beenonline = 1; /* We have now been online. */
|
||||
rdp->preemptable = preemptable;
|
||||
rdp->passed_quiesc_completed = lastcomp - 1;
|
||||
rdp->grpmask = 1UL << (cpu - rdp->mynode->grplo);
|
||||
rdp->nxtlist = NULL;
|
||||
for (i = 0; i < RCU_NEXT_SIZE; i++)
|
||||
rdp->nxttail[i] = &rdp->nxtlist;
|
||||
rdp->qlen = 0;
|
||||
rdp->blimit = blimit;
|
||||
#ifdef CONFIG_NO_HZ
|
||||
rdp->dynticks = &per_cpu(rcu_dynticks, cpu);
|
||||
#endif /* #ifdef CONFIG_NO_HZ */
|
||||
rdp->cpu = cpu;
|
||||
spin_unlock(&rnp->lock); /* irqs remain disabled. */
|
||||
|
||||
/*
|
||||
@@ -1405,16 +1475,16 @@ rcu_init_percpu_data(int cpu, struct rcu_state *rsp)
|
||||
|
||||
static void __cpuinit rcu_online_cpu(int cpu)
|
||||
{
|
||||
rcu_init_percpu_data(cpu, &rcu_state);
|
||||
rcu_init_percpu_data(cpu, &rcu_bh_state);
|
||||
open_softirq(RCU_SOFTIRQ, rcu_process_callbacks);
|
||||
rcu_init_percpu_data(cpu, &rcu_sched_state, 0);
|
||||
rcu_init_percpu_data(cpu, &rcu_bh_state, 0);
|
||||
rcu_preempt_init_percpu_data(cpu);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle CPU online/offline notifcation events.
|
||||
* Handle CPU online/offline notification events.
|
||||
*/
|
||||
static int __cpuinit rcu_cpu_notify(struct notifier_block *self,
|
||||
unsigned long action, void *hcpu)
|
||||
int __cpuinit rcu_cpu_notify(struct notifier_block *self,
|
||||
unsigned long action, void *hcpu)
|
||||
{
|
||||
long cpu = (long)hcpu;
|
||||
|
||||
@@ -1486,6 +1556,7 @@ static void __init rcu_init_one(struct rcu_state *rsp)
|
||||
rnp = rsp->level[i];
|
||||
for (j = 0; j < rsp->levelcnt[i]; j++, rnp++) {
|
||||
spin_lock_init(&rnp->lock);
|
||||
rnp->gpnum = 0;
|
||||
rnp->qsmask = 0;
|
||||
rnp->qsmaskinit = 0;
|
||||
rnp->grplo = j * cpustride;
|
||||
@@ -1503,16 +1574,20 @@ static void __init rcu_init_one(struct rcu_state *rsp)
|
||||
j / rsp->levelspread[i - 1];
|
||||
}
|
||||
rnp->level = i;
|
||||
INIT_LIST_HEAD(&rnp->blocked_tasks[0]);
|
||||
INIT_LIST_HEAD(&rnp->blocked_tasks[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper macro for __rcu_init(). To be used nowhere else!
|
||||
* Assigns leaf node pointers into each CPU's rcu_data structure.
|
||||
* Helper macro for __rcu_init() and __rcu_init_preempt(). To be used
|
||||
* nowhere else! Assigns leaf node pointers into each CPU's rcu_data
|
||||
* structure.
|
||||
*/
|
||||
#define RCU_DATA_PTR_INIT(rsp, rcu_data) \
|
||||
#define RCU_INIT_FLAVOR(rsp, rcu_data) \
|
||||
do { \
|
||||
rcu_init_one(rsp); \
|
||||
rnp = (rsp)->level[NUM_RCU_LVLS - 1]; \
|
||||
j = 0; \
|
||||
for_each_possible_cpu(i) { \
|
||||
@@ -1520,32 +1595,43 @@ do { \
|
||||
j++; \
|
||||
per_cpu(rcu_data, i).mynode = &rnp[j]; \
|
||||
(rsp)->rda[i] = &per_cpu(rcu_data, i); \
|
||||
rcu_boot_init_percpu_data(i, rsp); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static struct notifier_block __cpuinitdata rcu_nb = {
|
||||
.notifier_call = rcu_cpu_notify,
|
||||
};
|
||||
#ifdef CONFIG_TREE_PREEMPT_RCU
|
||||
|
||||
void __init __rcu_init(void)
|
||||
void __init __rcu_init_preempt(void)
|
||||
{
|
||||
int i; /* All used by RCU_DATA_PTR_INIT(). */
|
||||
int i; /* All used by RCU_INIT_FLAVOR(). */
|
||||
int j;
|
||||
struct rcu_node *rnp;
|
||||
|
||||
printk(KERN_INFO "Hierarchical RCU implementation.\n");
|
||||
RCU_INIT_FLAVOR(&rcu_preempt_state, rcu_preempt_data);
|
||||
}
|
||||
|
||||
#else /* #ifdef CONFIG_TREE_PREEMPT_RCU */
|
||||
|
||||
void __init __rcu_init_preempt(void)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* #else #ifdef CONFIG_TREE_PREEMPT_RCU */
|
||||
|
||||
void __init __rcu_init(void)
|
||||
{
|
||||
int i; /* All used by RCU_INIT_FLAVOR(). */
|
||||
int j;
|
||||
struct rcu_node *rnp;
|
||||
|
||||
rcu_bootup_announce();
|
||||
#ifdef CONFIG_RCU_CPU_STALL_DETECTOR
|
||||
printk(KERN_INFO "RCU-based detection of stalled CPUs is enabled.\n");
|
||||
#endif /* #ifdef CONFIG_RCU_CPU_STALL_DETECTOR */
|
||||
rcu_init_one(&rcu_state);
|
||||
RCU_DATA_PTR_INIT(&rcu_state, rcu_data);
|
||||
rcu_init_one(&rcu_bh_state);
|
||||
RCU_DATA_PTR_INIT(&rcu_bh_state, rcu_bh_data);
|
||||
|
||||
for_each_online_cpu(i)
|
||||
rcu_cpu_notify(&rcu_nb, CPU_UP_PREPARE, (void *)(long)i);
|
||||
/* Register notifier for non-boot CPUs */
|
||||
register_cpu_notifier(&rcu_nb);
|
||||
RCU_INIT_FLAVOR(&rcu_sched_state, rcu_sched_data);
|
||||
RCU_INIT_FLAVOR(&rcu_bh_state, rcu_bh_data);
|
||||
__rcu_init_preempt();
|
||||
open_softirq(RCU_SOFTIRQ, rcu_process_callbacks);
|
||||
}
|
||||
|
||||
module_param(blimit, int, 0);
|
||||
|
253
kernel/rcutree.h
253
kernel/rcutree.h
@@ -1,10 +1,259 @@
|
||||
/*
|
||||
* Read-Copy Update mechanism for mutual exclusion (tree-based version)
|
||||
* Internal non-public definitions.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* Author: Ingo Molnar <mingo@elte.hu>
|
||||
* Paul E. McKenney <paulmck@linux.vnet.ibm.com>
|
||||
*/
|
||||
|
||||
#include <linux/cache.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/threads.h>
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/seqlock.h>
|
||||
|
||||
/*
|
||||
* Define shape of hierarchy based on NR_CPUS and CONFIG_RCU_FANOUT.
|
||||
* In theory, it should be possible to add more levels straightforwardly.
|
||||
* In practice, this has not been tested, so there is probably some
|
||||
* bug somewhere.
|
||||
*/
|
||||
#define MAX_RCU_LVLS 3
|
||||
#define RCU_FANOUT (CONFIG_RCU_FANOUT)
|
||||
#define RCU_FANOUT_SQ (RCU_FANOUT * RCU_FANOUT)
|
||||
#define RCU_FANOUT_CUBE (RCU_FANOUT_SQ * RCU_FANOUT)
|
||||
|
||||
#if NR_CPUS <= RCU_FANOUT
|
||||
# define NUM_RCU_LVLS 1
|
||||
# define NUM_RCU_LVL_0 1
|
||||
# define NUM_RCU_LVL_1 (NR_CPUS)
|
||||
# define NUM_RCU_LVL_2 0
|
||||
# define NUM_RCU_LVL_3 0
|
||||
#elif NR_CPUS <= RCU_FANOUT_SQ
|
||||
# define NUM_RCU_LVLS 2
|
||||
# define NUM_RCU_LVL_0 1
|
||||
# define NUM_RCU_LVL_1 (((NR_CPUS) + RCU_FANOUT - 1) / RCU_FANOUT)
|
||||
# define NUM_RCU_LVL_2 (NR_CPUS)
|
||||
# define NUM_RCU_LVL_3 0
|
||||
#elif NR_CPUS <= RCU_FANOUT_CUBE
|
||||
# define NUM_RCU_LVLS 3
|
||||
# define NUM_RCU_LVL_0 1
|
||||
# define NUM_RCU_LVL_1 (((NR_CPUS) + RCU_FANOUT_SQ - 1) / RCU_FANOUT_SQ)
|
||||
# define NUM_RCU_LVL_2 (((NR_CPUS) + (RCU_FANOUT) - 1) / (RCU_FANOUT))
|
||||
# define NUM_RCU_LVL_3 NR_CPUS
|
||||
#else
|
||||
# error "CONFIG_RCU_FANOUT insufficient for NR_CPUS"
|
||||
#endif /* #if (NR_CPUS) <= RCU_FANOUT */
|
||||
|
||||
#define RCU_SUM (NUM_RCU_LVL_0 + NUM_RCU_LVL_1 + NUM_RCU_LVL_2 + NUM_RCU_LVL_3)
|
||||
#define NUM_RCU_NODES (RCU_SUM - NR_CPUS)
|
||||
|
||||
/*
|
||||
* Dynticks per-CPU state.
|
||||
*/
|
||||
struct rcu_dynticks {
|
||||
int dynticks_nesting; /* Track nesting level, sort of. */
|
||||
int dynticks; /* Even value for dynticks-idle, else odd. */
|
||||
int dynticks_nmi; /* Even value for either dynticks-idle or */
|
||||
/* not in nmi handler, else odd. So this */
|
||||
/* remains even for nmi from irq handler. */
|
||||
};
|
||||
|
||||
/*
|
||||
* Definition for node within the RCU grace-period-detection hierarchy.
|
||||
*/
|
||||
struct rcu_node {
|
||||
spinlock_t lock;
|
||||
long gpnum; /* Current grace period for this node. */
|
||||
/* This will either be equal to or one */
|
||||
/* behind the root rcu_node's gpnum. */
|
||||
unsigned long qsmask; /* CPUs or groups that need to switch in */
|
||||
/* order for current grace period to proceed.*/
|
||||
unsigned long qsmaskinit;
|
||||
/* Per-GP initialization for qsmask. */
|
||||
unsigned long grpmask; /* Mask to apply to parent qsmask. */
|
||||
int grplo; /* lowest-numbered CPU or group here. */
|
||||
int grphi; /* highest-numbered CPU or group here. */
|
||||
u8 grpnum; /* CPU/group number for next level up. */
|
||||
u8 level; /* root is at level 0. */
|
||||
struct rcu_node *parent;
|
||||
struct list_head blocked_tasks[2];
|
||||
/* Tasks blocked in RCU read-side critsect. */
|
||||
} ____cacheline_internodealigned_in_smp;
|
||||
|
||||
/* Index values for nxttail array in struct rcu_data. */
|
||||
#define RCU_DONE_TAIL 0 /* Also RCU_WAIT head. */
|
||||
#define RCU_WAIT_TAIL 1 /* Also RCU_NEXT_READY head. */
|
||||
#define RCU_NEXT_READY_TAIL 2 /* Also RCU_NEXT head. */
|
||||
#define RCU_NEXT_TAIL 3
|
||||
#define RCU_NEXT_SIZE 4
|
||||
|
||||
/* Per-CPU data for read-copy update. */
|
||||
struct rcu_data {
|
||||
/* 1) quiescent-state and grace-period handling : */
|
||||
long completed; /* Track rsp->completed gp number */
|
||||
/* in order to detect GP end. */
|
||||
long gpnum; /* Highest gp number that this CPU */
|
||||
/* is aware of having started. */
|
||||
long passed_quiesc_completed;
|
||||
/* Value of completed at time of qs. */
|
||||
bool passed_quiesc; /* User-mode/idle loop etc. */
|
||||
bool qs_pending; /* Core waits for quiesc state. */
|
||||
bool beenonline; /* CPU online at least once. */
|
||||
bool preemptable; /* Preemptable RCU? */
|
||||
struct rcu_node *mynode; /* This CPU's leaf of hierarchy */
|
||||
unsigned long grpmask; /* Mask to apply to leaf qsmask. */
|
||||
|
||||
/* 2) batch handling */
|
||||
/*
|
||||
* If nxtlist is not NULL, it is partitioned as follows.
|
||||
* Any of the partitions might be empty, in which case the
|
||||
* pointer to that partition will be equal to the pointer for
|
||||
* the following partition. When the list is empty, all of
|
||||
* the nxttail elements point to nxtlist, which is NULL.
|
||||
*
|
||||
* [*nxttail[RCU_NEXT_READY_TAIL], NULL = *nxttail[RCU_NEXT_TAIL]):
|
||||
* Entries that might have arrived after current GP ended
|
||||
* [*nxttail[RCU_WAIT_TAIL], *nxttail[RCU_NEXT_READY_TAIL]):
|
||||
* Entries known to have arrived before current GP ended
|
||||
* [*nxttail[RCU_DONE_TAIL], *nxttail[RCU_WAIT_TAIL]):
|
||||
* Entries that batch # <= ->completed - 1: waiting for current GP
|
||||
* [nxtlist, *nxttail[RCU_DONE_TAIL]):
|
||||
* Entries that batch # <= ->completed
|
||||
* The grace period for these entries has completed, and
|
||||
* the other grace-period-completed entries may be moved
|
||||
* here temporarily in rcu_process_callbacks().
|
||||
*/
|
||||
struct rcu_head *nxtlist;
|
||||
struct rcu_head **nxttail[RCU_NEXT_SIZE];
|
||||
long qlen; /* # of queued callbacks */
|
||||
long blimit; /* Upper limit on a processed batch */
|
||||
|
||||
#ifdef CONFIG_NO_HZ
|
||||
/* 3) dynticks interface. */
|
||||
struct rcu_dynticks *dynticks; /* Shared per-CPU dynticks state. */
|
||||
int dynticks_snap; /* Per-GP tracking for dynticks. */
|
||||
int dynticks_nmi_snap; /* Per-GP tracking for dynticks_nmi. */
|
||||
#endif /* #ifdef CONFIG_NO_HZ */
|
||||
|
||||
/* 4) reasons this CPU needed to be kicked by force_quiescent_state */
|
||||
#ifdef CONFIG_NO_HZ
|
||||
unsigned long dynticks_fqs; /* Kicked due to dynticks idle. */
|
||||
#endif /* #ifdef CONFIG_NO_HZ */
|
||||
unsigned long offline_fqs; /* Kicked due to being offline. */
|
||||
unsigned long resched_ipi; /* Sent a resched IPI. */
|
||||
|
||||
/* 5) __rcu_pending() statistics. */
|
||||
long n_rcu_pending; /* rcu_pending() calls since boot. */
|
||||
long n_rp_qs_pending;
|
||||
long n_rp_cb_ready;
|
||||
long n_rp_cpu_needs_gp;
|
||||
long n_rp_gp_completed;
|
||||
long n_rp_gp_started;
|
||||
long n_rp_need_fqs;
|
||||
long n_rp_need_nothing;
|
||||
|
||||
int cpu;
|
||||
};
|
||||
|
||||
/* Values for signaled field in struct rcu_state. */
|
||||
#define RCU_GP_INIT 0 /* Grace period being initialized. */
|
||||
#define RCU_SAVE_DYNTICK 1 /* Need to scan dyntick state. */
|
||||
#define RCU_FORCE_QS 2 /* Need to force quiescent state. */
|
||||
#ifdef CONFIG_NO_HZ
|
||||
#define RCU_SIGNAL_INIT RCU_SAVE_DYNTICK
|
||||
#else /* #ifdef CONFIG_NO_HZ */
|
||||
#define RCU_SIGNAL_INIT RCU_FORCE_QS
|
||||
#endif /* #else #ifdef CONFIG_NO_HZ */
|
||||
|
||||
#define RCU_JIFFIES_TILL_FORCE_QS 3 /* for rsp->jiffies_force_qs */
|
||||
#ifdef CONFIG_RCU_CPU_STALL_DETECTOR
|
||||
#define RCU_SECONDS_TILL_STALL_CHECK (10 * HZ) /* for rsp->jiffies_stall */
|
||||
#define RCU_SECONDS_TILL_STALL_RECHECK (30 * HZ) /* for rsp->jiffies_stall */
|
||||
#define RCU_STALL_RAT_DELAY 2 /* Allow other CPUs time */
|
||||
/* to take at least one */
|
||||
/* scheduling clock irq */
|
||||
/* before ratting on them. */
|
||||
|
||||
#endif /* #ifdef CONFIG_RCU_CPU_STALL_DETECTOR */
|
||||
|
||||
/*
|
||||
* RCU global state, including node hierarchy. This hierarchy is
|
||||
* represented in "heap" form in a dense array. The root (first level)
|
||||
* of the hierarchy is in ->node[0] (referenced by ->level[0]), the second
|
||||
* level in ->node[1] through ->node[m] (->node[1] referenced by ->level[1]),
|
||||
* and the third level in ->node[m+1] and following (->node[m+1] referenced
|
||||
* by ->level[2]). The number of levels is determined by the number of
|
||||
* CPUs and by CONFIG_RCU_FANOUT. Small systems will have a "hierarchy"
|
||||
* consisting of a single rcu_node.
|
||||
*/
|
||||
struct rcu_state {
|
||||
struct rcu_node node[NUM_RCU_NODES]; /* Hierarchy. */
|
||||
struct rcu_node *level[NUM_RCU_LVLS]; /* Hierarchy levels. */
|
||||
u32 levelcnt[MAX_RCU_LVLS + 1]; /* # nodes in each level. */
|
||||
u8 levelspread[NUM_RCU_LVLS]; /* kids/node in each level. */
|
||||
struct rcu_data *rda[NR_CPUS]; /* array of rdp pointers. */
|
||||
|
||||
/* The following fields are guarded by the root rcu_node's lock. */
|
||||
|
||||
u8 signaled ____cacheline_internodealigned_in_smp;
|
||||
/* Force QS state. */
|
||||
long gpnum; /* Current gp number. */
|
||||
long completed; /* # of last completed gp. */
|
||||
spinlock_t onofflock; /* exclude on/offline and */
|
||||
/* starting new GP. */
|
||||
spinlock_t fqslock; /* Only one task forcing */
|
||||
/* quiescent states. */
|
||||
unsigned long jiffies_force_qs; /* Time at which to invoke */
|
||||
/* force_quiescent_state(). */
|
||||
unsigned long n_force_qs; /* Number of calls to */
|
||||
/* force_quiescent_state(). */
|
||||
unsigned long n_force_qs_lh; /* ~Number of calls leaving */
|
||||
/* due to lock unavailable. */
|
||||
unsigned long n_force_qs_ngp; /* Number of calls leaving */
|
||||
/* due to no GP active. */
|
||||
#ifdef CONFIG_RCU_CPU_STALL_DETECTOR
|
||||
unsigned long gp_start; /* Time at which GP started, */
|
||||
/* but in jiffies. */
|
||||
unsigned long jiffies_stall; /* Time at which to check */
|
||||
/* for CPU stalls. */
|
||||
#endif /* #ifdef CONFIG_RCU_CPU_STALL_DETECTOR */
|
||||
#ifdef CONFIG_NO_HZ
|
||||
long dynticks_completed; /* Value of completed @ snap. */
|
||||
#endif /* #ifdef CONFIG_NO_HZ */
|
||||
};
|
||||
|
||||
#ifdef RCU_TREE_NONCORE
|
||||
|
||||
/*
|
||||
* RCU implementation internal declarations:
|
||||
*/
|
||||
extern struct rcu_state rcu_state;
|
||||
DECLARE_PER_CPU(struct rcu_data, rcu_data);
|
||||
extern struct rcu_state rcu_sched_state;
|
||||
DECLARE_PER_CPU(struct rcu_data, rcu_sched_data);
|
||||
|
||||
extern struct rcu_state rcu_bh_state;
|
||||
DECLARE_PER_CPU(struct rcu_data, rcu_bh_data);
|
||||
|
||||
#ifdef CONFIG_TREE_PREEMPT_RCU
|
||||
extern struct rcu_state rcu_preempt_state;
|
||||
DECLARE_PER_CPU(struct rcu_data, rcu_preempt_data);
|
||||
#endif /* #ifdef CONFIG_TREE_PREEMPT_RCU */
|
||||
|
||||
#endif /* #ifdef RCU_TREE_NONCORE */
|
||||
|
||||
|
532
kernel/rcutree_plugin.h
Normal file
532
kernel/rcutree_plugin.h
Normal file
@@ -0,0 +1,532 @@
|
||||
/*
|
||||
* Read-Copy Update mechanism for mutual exclusion (tree-based version)
|
||||
* Internal non-public definitions that provide either classic
|
||||
* or preemptable semantics.
|
||||
*
|
||||
* 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 Red Hat, 2009
|
||||
* Copyright IBM Corporation, 2009
|
||||
*
|
||||
* Author: Ingo Molnar <mingo@elte.hu>
|
||||
* Paul E. McKenney <paulmck@linux.vnet.ibm.com>
|
||||
*/
|
||||
|
||||
|
||||
#ifdef CONFIG_TREE_PREEMPT_RCU
|
||||
|
||||
struct rcu_state rcu_preempt_state = RCU_STATE_INITIALIZER(rcu_preempt_state);
|
||||
DEFINE_PER_CPU(struct rcu_data, rcu_preempt_data);
|
||||
|
||||
/*
|
||||
* Tell them what RCU they are running.
|
||||
*/
|
||||
static inline void rcu_bootup_announce(void)
|
||||
{
|
||||
printk(KERN_INFO
|
||||
"Experimental preemptable hierarchical RCU implementation.\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the number of RCU-preempt batches processed thus far
|
||||
* for debug and statistics.
|
||||
*/
|
||||
long rcu_batches_completed_preempt(void)
|
||||
{
|
||||
return rcu_preempt_state.completed;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rcu_batches_completed_preempt);
|
||||
|
||||
/*
|
||||
* Return the number of RCU batches processed thus far for debug & stats.
|
||||
*/
|
||||
long rcu_batches_completed(void)
|
||||
{
|
||||
return rcu_batches_completed_preempt();
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rcu_batches_completed);
|
||||
|
||||
/*
|
||||
* Record a preemptable-RCU quiescent state for the specified CPU. Note
|
||||
* that this just means that the task currently running on the CPU is
|
||||
* not in a quiescent state. There might be any number of tasks blocked
|
||||
* while in an RCU read-side critical section.
|
||||
*/
|
||||
static void rcu_preempt_qs_record(int cpu)
|
||||
{
|
||||
struct rcu_data *rdp = &per_cpu(rcu_preempt_data, cpu);
|
||||
rdp->passed_quiesc = 1;
|
||||
rdp->passed_quiesc_completed = rdp->completed;
|
||||
}
|
||||
|
||||
/*
|
||||
* We have entered the scheduler or are between softirqs in ksoftirqd.
|
||||
* If we are in an RCU read-side critical section, we need to reflect
|
||||
* that in the state of the rcu_node structure corresponding to this CPU.
|
||||
* Caller must disable hardirqs.
|
||||
*/
|
||||
static void rcu_preempt_qs(int cpu)
|
||||
{
|
||||
struct task_struct *t = current;
|
||||
int phase;
|
||||
struct rcu_data *rdp;
|
||||
struct rcu_node *rnp;
|
||||
|
||||
if (t->rcu_read_lock_nesting &&
|
||||
(t->rcu_read_unlock_special & RCU_READ_UNLOCK_BLOCKED) == 0) {
|
||||
|
||||
/* Possibly blocking in an RCU read-side critical section. */
|
||||
rdp = rcu_preempt_state.rda[cpu];
|
||||
rnp = rdp->mynode;
|
||||
spin_lock(&rnp->lock);
|
||||
t->rcu_read_unlock_special |= RCU_READ_UNLOCK_BLOCKED;
|
||||
t->rcu_blocked_node = rnp;
|
||||
|
||||
/*
|
||||
* If this CPU has already checked in, then this task
|
||||
* will hold up the next grace period rather than the
|
||||
* current grace period. Queue the task accordingly.
|
||||
* If the task is queued for the current grace period
|
||||
* (i.e., this CPU has not yet passed through a quiescent
|
||||
* state for the current grace period), then as long
|
||||
* as that task remains queued, the current grace period
|
||||
* cannot end.
|
||||
*/
|
||||
phase = !(rnp->qsmask & rdp->grpmask) ^ (rnp->gpnum & 0x1);
|
||||
list_add(&t->rcu_node_entry, &rnp->blocked_tasks[phase]);
|
||||
smp_mb(); /* Ensure later ctxt swtch seen after above. */
|
||||
spin_unlock(&rnp->lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Either we were not in an RCU read-side critical section to
|
||||
* begin with, or we have now recorded that critical section
|
||||
* globally. Either way, we can now note a quiescent state
|
||||
* for this CPU. Again, if we were in an RCU read-side critical
|
||||
* section, and if that critical section was blocking the current
|
||||
* grace period, then the fact that the task has been enqueued
|
||||
* means that we continue to block the current grace period.
|
||||
*/
|
||||
rcu_preempt_qs_record(cpu);
|
||||
t->rcu_read_unlock_special &= ~(RCU_READ_UNLOCK_NEED_QS |
|
||||
RCU_READ_UNLOCK_GOT_QS);
|
||||
}
|
||||
|
||||
/*
|
||||
* Tree-preemptable RCU implementation for rcu_read_lock().
|
||||
* Just increment ->rcu_read_lock_nesting, shared state will be updated
|
||||
* if we block.
|
||||
*/
|
||||
void __rcu_read_lock(void)
|
||||
{
|
||||
ACCESS_ONCE(current->rcu_read_lock_nesting)++;
|
||||
barrier(); /* needed if we ever invoke rcu_read_lock in rcutree.c */
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__rcu_read_lock);
|
||||
|
||||
static void rcu_read_unlock_special(struct task_struct *t)
|
||||
{
|
||||
int empty;
|
||||
unsigned long flags;
|
||||
unsigned long mask;
|
||||
struct rcu_node *rnp;
|
||||
int special;
|
||||
|
||||
/* NMI handlers cannot block and cannot safely manipulate state. */
|
||||
if (in_nmi())
|
||||
return;
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
/*
|
||||
* If RCU core is waiting for this CPU to exit critical section,
|
||||
* let it know that we have done so.
|
||||
*/
|
||||
special = t->rcu_read_unlock_special;
|
||||
if (special & RCU_READ_UNLOCK_NEED_QS) {
|
||||
t->rcu_read_unlock_special &= ~RCU_READ_UNLOCK_NEED_QS;
|
||||
t->rcu_read_unlock_special |= RCU_READ_UNLOCK_GOT_QS;
|
||||
}
|
||||
|
||||
/* Hardware IRQ handlers cannot block. */
|
||||
if (in_irq()) {
|
||||
local_irq_restore(flags);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Clean up if blocked during RCU read-side critical section. */
|
||||
if (special & RCU_READ_UNLOCK_BLOCKED) {
|
||||
t->rcu_read_unlock_special &= ~RCU_READ_UNLOCK_BLOCKED;
|
||||
|
||||
/*
|
||||
* Remove this task from the list it blocked on. The
|
||||
* task can migrate while we acquire the lock, but at
|
||||
* most one time. So at most two passes through loop.
|
||||
*/
|
||||
for (;;) {
|
||||
rnp = t->rcu_blocked_node;
|
||||
spin_lock(&rnp->lock);
|
||||
if (rnp == t->rcu_blocked_node)
|
||||
break;
|
||||
spin_unlock(&rnp->lock);
|
||||
}
|
||||
empty = list_empty(&rnp->blocked_tasks[rnp->gpnum & 0x1]);
|
||||
list_del_init(&t->rcu_node_entry);
|
||||
t->rcu_blocked_node = NULL;
|
||||
|
||||
/*
|
||||
* If this was the last task on the current list, and if
|
||||
* we aren't waiting on any CPUs, report the quiescent state.
|
||||
* Note that both cpu_quiet_msk_finish() and cpu_quiet_msk()
|
||||
* drop rnp->lock and restore irq.
|
||||
*/
|
||||
if (!empty && rnp->qsmask == 0 &&
|
||||
list_empty(&rnp->blocked_tasks[rnp->gpnum & 0x1])) {
|
||||
t->rcu_read_unlock_special &=
|
||||
~(RCU_READ_UNLOCK_NEED_QS |
|
||||
RCU_READ_UNLOCK_GOT_QS);
|
||||
if (rnp->parent == NULL) {
|
||||
/* Only one rcu_node in the tree. */
|
||||
cpu_quiet_msk_finish(&rcu_preempt_state, flags);
|
||||
return;
|
||||
}
|
||||
/* Report up the rest of the hierarchy. */
|
||||
mask = rnp->grpmask;
|
||||
spin_unlock_irqrestore(&rnp->lock, flags);
|
||||
rnp = rnp->parent;
|
||||
spin_lock_irqsave(&rnp->lock, flags);
|
||||
cpu_quiet_msk(mask, &rcu_preempt_state, rnp, flags);
|
||||
return;
|
||||
}
|
||||
spin_unlock(&rnp->lock);
|
||||
}
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Tree-preemptable RCU implementation for rcu_read_unlock().
|
||||
* Decrement ->rcu_read_lock_nesting. If the result is zero (outermost
|
||||
* rcu_read_unlock()) and ->rcu_read_unlock_special is non-zero, then
|
||||
* invoke rcu_read_unlock_special() to clean up after a context switch
|
||||
* in an RCU read-side critical section and other special cases.
|
||||
*/
|
||||
void __rcu_read_unlock(void)
|
||||
{
|
||||
struct task_struct *t = current;
|
||||
|
||||
barrier(); /* needed if we ever invoke rcu_read_unlock in rcutree.c */
|
||||
if (--ACCESS_ONCE(t->rcu_read_lock_nesting) == 0 &&
|
||||
unlikely(ACCESS_ONCE(t->rcu_read_unlock_special)))
|
||||
rcu_read_unlock_special(t);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__rcu_read_unlock);
|
||||
|
||||
#ifdef CONFIG_RCU_CPU_STALL_DETECTOR
|
||||
|
||||
/*
|
||||
* Scan the current list of tasks blocked within RCU read-side critical
|
||||
* sections, printing out the tid of each.
|
||||
*/
|
||||
static void rcu_print_task_stall(struct rcu_node *rnp)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct list_head *lp;
|
||||
int phase = rnp->gpnum & 0x1;
|
||||
struct task_struct *t;
|
||||
|
||||
if (!list_empty(&rnp->blocked_tasks[phase])) {
|
||||
spin_lock_irqsave(&rnp->lock, flags);
|
||||
phase = rnp->gpnum & 0x1; /* re-read under lock. */
|
||||
lp = &rnp->blocked_tasks[phase];
|
||||
list_for_each_entry(t, lp, rcu_node_entry)
|
||||
printk(" P%d", t->pid);
|
||||
spin_unlock_irqrestore(&rnp->lock, flags);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* #ifdef CONFIG_RCU_CPU_STALL_DETECTOR */
|
||||
|
||||
/*
|
||||
* Check for preempted RCU readers for the specified rcu_node structure.
|
||||
* If the caller needs a reliable answer, it must hold the rcu_node's
|
||||
* >lock.
|
||||
*/
|
||||
static int rcu_preempted_readers(struct rcu_node *rnp)
|
||||
{
|
||||
return !list_empty(&rnp->blocked_tasks[rnp->gpnum & 0x1]);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
|
||||
/*
|
||||
* Handle tasklist migration for case in which all CPUs covered by the
|
||||
* specified rcu_node have gone offline. Move them up to the root
|
||||
* rcu_node. The reason for not just moving them to the immediate
|
||||
* parent is to remove the need for rcu_read_unlock_special() to
|
||||
* make more than two attempts to acquire the target rcu_node's lock.
|
||||
*
|
||||
* The caller must hold rnp->lock with irqs disabled.
|
||||
*/
|
||||
static void rcu_preempt_offline_tasks(struct rcu_state *rsp,
|
||||
struct rcu_node *rnp)
|
||||
{
|
||||
int i;
|
||||
struct list_head *lp;
|
||||
struct list_head *lp_root;
|
||||
struct rcu_node *rnp_root = rcu_get_root(rsp);
|
||||
struct task_struct *tp;
|
||||
|
||||
if (rnp == rnp_root) {
|
||||
WARN_ONCE(1, "Last CPU thought to be offlined?");
|
||||
return; /* Shouldn't happen: at least one CPU online. */
|
||||
}
|
||||
|
||||
/*
|
||||
* Move tasks up to root rcu_node. Rely on the fact that the
|
||||
* root rcu_node can be at most one ahead of the rest of the
|
||||
* rcu_nodes in terms of gp_num value. This fact allows us to
|
||||
* move the blocked_tasks[] array directly, element by element.
|
||||
*/
|
||||
for (i = 0; i < 2; i++) {
|
||||
lp = &rnp->blocked_tasks[i];
|
||||
lp_root = &rnp_root->blocked_tasks[i];
|
||||
while (!list_empty(lp)) {
|
||||
tp = list_entry(lp->next, typeof(*tp), rcu_node_entry);
|
||||
spin_lock(&rnp_root->lock); /* irqs already disabled */
|
||||
list_del(&tp->rcu_node_entry);
|
||||
tp->rcu_blocked_node = rnp_root;
|
||||
list_add(&tp->rcu_node_entry, lp_root);
|
||||
spin_unlock(&rnp_root->lock); /* irqs remain disabled */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Do CPU-offline processing for preemptable RCU.
|
||||
*/
|
||||
static void rcu_preempt_offline_cpu(int cpu)
|
||||
{
|
||||
__rcu_offline_cpu(cpu, &rcu_preempt_state);
|
||||
}
|
||||
|
||||
#endif /* #ifdef CONFIG_HOTPLUG_CPU */
|
||||
|
||||
/*
|
||||
* Check for a quiescent state from the current CPU. When a task blocks,
|
||||
* the task is recorded in the corresponding CPU's rcu_node structure,
|
||||
* which is checked elsewhere.
|
||||
*
|
||||
* Caller must disable hard irqs.
|
||||
*/
|
||||
static void rcu_preempt_check_callbacks(int cpu)
|
||||
{
|
||||
struct task_struct *t = current;
|
||||
|
||||
if (t->rcu_read_lock_nesting == 0) {
|
||||
t->rcu_read_unlock_special &=
|
||||
~(RCU_READ_UNLOCK_NEED_QS | RCU_READ_UNLOCK_GOT_QS);
|
||||
rcu_preempt_qs_record(cpu);
|
||||
return;
|
||||
}
|
||||
if (per_cpu(rcu_preempt_data, cpu).qs_pending) {
|
||||
if (t->rcu_read_unlock_special & RCU_READ_UNLOCK_GOT_QS) {
|
||||
rcu_preempt_qs_record(cpu);
|
||||
t->rcu_read_unlock_special &= ~RCU_READ_UNLOCK_GOT_QS;
|
||||
} else if (!(t->rcu_read_unlock_special &
|
||||
RCU_READ_UNLOCK_NEED_QS)) {
|
||||
t->rcu_read_unlock_special |= RCU_READ_UNLOCK_NEED_QS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Process callbacks for preemptable RCU.
|
||||
*/
|
||||
static void rcu_preempt_process_callbacks(void)
|
||||
{
|
||||
__rcu_process_callbacks(&rcu_preempt_state,
|
||||
&__get_cpu_var(rcu_preempt_data));
|
||||
}
|
||||
|
||||
/*
|
||||
* Queue a preemptable-RCU callback for invocation after a grace period.
|
||||
*/
|
||||
void call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu))
|
||||
{
|
||||
__call_rcu(head, func, &rcu_preempt_state);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(call_rcu);
|
||||
|
||||
/*
|
||||
* Check to see if there is any immediate preemptable-RCU-related work
|
||||
* to be done.
|
||||
*/
|
||||
static int rcu_preempt_pending(int cpu)
|
||||
{
|
||||
return __rcu_pending(&rcu_preempt_state,
|
||||
&per_cpu(rcu_preempt_data, cpu));
|
||||
}
|
||||
|
||||
/*
|
||||
* Does preemptable RCU need the CPU to stay out of dynticks mode?
|
||||
*/
|
||||
static int rcu_preempt_needs_cpu(int cpu)
|
||||
{
|
||||
return !!per_cpu(rcu_preempt_data, cpu).nxtlist;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize preemptable RCU's per-CPU data.
|
||||
*/
|
||||
static void __cpuinit rcu_preempt_init_percpu_data(int cpu)
|
||||
{
|
||||
rcu_init_percpu_data(cpu, &rcu_preempt_state, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for a task exiting while in a preemptable-RCU read-side
|
||||
* critical section, clean up if so. No need to issue warnings,
|
||||
* as debug_check_no_locks_held() already does this if lockdep
|
||||
* is enabled.
|
||||
*/
|
||||
void exit_rcu(void)
|
||||
{
|
||||
struct task_struct *t = current;
|
||||
|
||||
if (t->rcu_read_lock_nesting == 0)
|
||||
return;
|
||||
t->rcu_read_lock_nesting = 1;
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
#else /* #ifdef CONFIG_TREE_PREEMPT_RCU */
|
||||
|
||||
/*
|
||||
* Tell them what RCU they are running.
|
||||
*/
|
||||
static inline void rcu_bootup_announce(void)
|
||||
{
|
||||
printk(KERN_INFO "Hierarchical RCU implementation.\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the number of RCU batches processed thus far for debug & stats.
|
||||
*/
|
||||
long rcu_batches_completed(void)
|
||||
{
|
||||
return rcu_batches_completed_sched();
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rcu_batches_completed);
|
||||
|
||||
/*
|
||||
* Because preemptable RCU does not exist, we never have to check for
|
||||
* CPUs being in quiescent states.
|
||||
*/
|
||||
static void rcu_preempt_qs(int cpu)
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef CONFIG_RCU_CPU_STALL_DETECTOR
|
||||
|
||||
/*
|
||||
* Because preemptable RCU does not exist, we never have to check for
|
||||
* tasks blocked within RCU read-side critical sections.
|
||||
*/
|
||||
static void rcu_print_task_stall(struct rcu_node *rnp)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* #ifdef CONFIG_RCU_CPU_STALL_DETECTOR */
|
||||
|
||||
/*
|
||||
* Because preemptable RCU does not exist, there are never any preempted
|
||||
* RCU readers.
|
||||
*/
|
||||
static int rcu_preempted_readers(struct rcu_node *rnp)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
|
||||
/*
|
||||
* Because preemptable RCU does not exist, it never needs to migrate
|
||||
* tasks that were blocked within RCU read-side critical sections.
|
||||
*/
|
||||
static void rcu_preempt_offline_tasks(struct rcu_state *rsp,
|
||||
struct rcu_node *rnp)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* Because preemptable RCU does not exist, it never needs CPU-offline
|
||||
* processing.
|
||||
*/
|
||||
static void rcu_preempt_offline_cpu(int cpu)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* #ifdef CONFIG_HOTPLUG_CPU */
|
||||
|
||||
/*
|
||||
* Because preemptable RCU does not exist, it never has any callbacks
|
||||
* to check.
|
||||
*/
|
||||
void rcu_preempt_check_callbacks(int cpu)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* Because preemptable RCU does not exist, it never has any callbacks
|
||||
* to process.
|
||||
*/
|
||||
void rcu_preempt_process_callbacks(void)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* In classic RCU, call_rcu() is just call_rcu_sched().
|
||||
*/
|
||||
void call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu))
|
||||
{
|
||||
call_rcu_sched(head, func);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(call_rcu);
|
||||
|
||||
/*
|
||||
* Because preemptable RCU does not exist, it never has any work to do.
|
||||
*/
|
||||
static int rcu_preempt_pending(int cpu)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Because preemptable RCU does not exist, it never needs any CPU.
|
||||
*/
|
||||
static int rcu_preempt_needs_cpu(int cpu)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Because preemptable RCU does not exist, there is no per-CPU
|
||||
* data to initialize.
|
||||
*/
|
||||
static void __cpuinit rcu_preempt_init_percpu_data(int cpu)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* #else #ifdef CONFIG_TREE_PREEMPT_RCU */
|
@@ -43,6 +43,7 @@
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/seq_file.h>
|
||||
|
||||
#define RCU_TREE_NONCORE
|
||||
#include "rcutree.h"
|
||||
|
||||
static void print_one_rcu_data(struct seq_file *m, struct rcu_data *rdp)
|
||||
@@ -76,8 +77,12 @@ static void print_one_rcu_data(struct seq_file *m, struct rcu_data *rdp)
|
||||
|
||||
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);
|
||||
#ifdef CONFIG_TREE_PREEMPT_RCU
|
||||
seq_puts(m, "rcu_preempt:\n");
|
||||
PRINT_RCU_DATA(rcu_preempt_data, print_one_rcu_data, m);
|
||||
#endif /* #ifdef CONFIG_TREE_PREEMPT_RCU */
|
||||
seq_puts(m, "rcu_sched:\n");
|
||||
PRINT_RCU_DATA(rcu_sched_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;
|
||||
@@ -102,7 +107,7 @@ static void print_one_rcu_data_csv(struct seq_file *m, struct rcu_data *rdp)
|
||||
return;
|
||||
seq_printf(m, "%d,%s,%ld,%ld,%d,%ld,%d",
|
||||
rdp->cpu,
|
||||
cpu_is_offline(rdp->cpu) ? "\"Y\"" : "\"N\"",
|
||||
cpu_is_offline(rdp->cpu) ? "\"N\"" : "\"Y\"",
|
||||
rdp->completed, rdp->gpnum,
|
||||
rdp->passed_quiesc, rdp->passed_quiesc_completed,
|
||||
rdp->qs_pending);
|
||||
@@ -124,8 +129,12 @@ static int show_rcudata_csv(struct seq_file *m, void *unused)
|
||||
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);
|
||||
#ifdef CONFIG_TREE_PREEMPT_RCU
|
||||
seq_puts(m, "\"rcu_preempt:\"\n");
|
||||
PRINT_RCU_DATA(rcu_preempt_data, print_one_rcu_data_csv, m);
|
||||
#endif /* #ifdef CONFIG_TREE_PREEMPT_RCU */
|
||||
seq_puts(m, "\"rcu_sched:\"\n");
|
||||
PRINT_RCU_DATA(rcu_sched_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;
|
||||
@@ -171,8 +180,12 @@ static void print_one_rcu_state(struct seq_file *m, struct rcu_state *rsp)
|
||||
|
||||
static int show_rcuhier(struct seq_file *m, void *unused)
|
||||
{
|
||||
seq_puts(m, "rcu:\n");
|
||||
print_one_rcu_state(m, &rcu_state);
|
||||
#ifdef CONFIG_TREE_PREEMPT_RCU
|
||||
seq_puts(m, "rcu_preempt:\n");
|
||||
print_one_rcu_state(m, &rcu_preempt_state);
|
||||
#endif /* #ifdef CONFIG_TREE_PREEMPT_RCU */
|
||||
seq_puts(m, "rcu_sched:\n");
|
||||
print_one_rcu_state(m, &rcu_sched_state);
|
||||
seq_puts(m, "rcu_bh:\n");
|
||||
print_one_rcu_state(m, &rcu_bh_state);
|
||||
return 0;
|
||||
@@ -193,8 +206,12 @@ static struct file_operations rcuhier_fops = {
|
||||
|
||||
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);
|
||||
#ifdef CONFIG_TREE_PREEMPT_RCU
|
||||
seq_printf(m, "rcu_preempt: completed=%ld gpnum=%ld\n",
|
||||
rcu_preempt_state.completed, rcu_preempt_state.gpnum);
|
||||
#endif /* #ifdef CONFIG_TREE_PREEMPT_RCU */
|
||||
seq_printf(m, "rcu_sched: completed=%ld gpnum=%ld\n",
|
||||
rcu_sched_state.completed, rcu_sched_state.gpnum);
|
||||
seq_printf(m, "rcu_bh: completed=%ld gpnum=%ld\n",
|
||||
rcu_bh_state.completed, rcu_bh_state.gpnum);
|
||||
return 0;
|
||||
@@ -243,8 +260,12 @@ static void print_rcu_pendings(struct seq_file *m, struct rcu_state *rsp)
|
||||
|
||||
static int show_rcu_pending(struct seq_file *m, void *unused)
|
||||
{
|
||||
seq_puts(m, "rcu:\n");
|
||||
print_rcu_pendings(m, &rcu_state);
|
||||
#ifdef CONFIG_TREE_PREEMPT_RCU
|
||||
seq_puts(m, "rcu_preempt:\n");
|
||||
print_rcu_pendings(m, &rcu_preempt_state);
|
||||
#endif /* #ifdef CONFIG_TREE_PREEMPT_RCU */
|
||||
seq_puts(m, "rcu_sched:\n");
|
||||
print_rcu_pendings(m, &rcu_sched_state);
|
||||
seq_puts(m, "rcu_bh:\n");
|
||||
print_rcu_pendings(m, &rcu_bh_state);
|
||||
return 0;
|
||||
@@ -264,62 +285,47 @@ static struct file_operations rcu_pending_fops = {
|
||||
};
|
||||
|
||||
static struct dentry *rcudir;
|
||||
static struct dentry *datadir;
|
||||
static struct dentry *datadir_csv;
|
||||
static struct dentry *gpdir;
|
||||
static struct dentry *hierdir;
|
||||
static struct dentry *rcu_pendingdir;
|
||||
|
||||
static int __init rcuclassic_trace_init(void)
|
||||
{
|
||||
struct dentry *retval;
|
||||
|
||||
rcudir = debugfs_create_dir("rcu", NULL);
|
||||
if (!rcudir)
|
||||
goto out;
|
||||
goto free_out;
|
||||
|
||||
datadir = debugfs_create_file("rcudata", 0444, rcudir,
|
||||
retval = debugfs_create_file("rcudata", 0444, rcudir,
|
||||
NULL, &rcudata_fops);
|
||||
if (!datadir)
|
||||
if (!retval)
|
||||
goto free_out;
|
||||
|
||||
datadir_csv = debugfs_create_file("rcudata.csv", 0444, rcudir,
|
||||
retval = debugfs_create_file("rcudata.csv", 0444, rcudir,
|
||||
NULL, &rcudata_csv_fops);
|
||||
if (!datadir_csv)
|
||||
if (!retval)
|
||||
goto free_out;
|
||||
|
||||
gpdir = debugfs_create_file("rcugp", 0444, rcudir, NULL, &rcugp_fops);
|
||||
if (!gpdir)
|
||||
retval = debugfs_create_file("rcugp", 0444, rcudir, NULL, &rcugp_fops);
|
||||
if (!retval)
|
||||
goto free_out;
|
||||
|
||||
hierdir = debugfs_create_file("rcuhier", 0444, rcudir,
|
||||
retval = debugfs_create_file("rcuhier", 0444, rcudir,
|
||||
NULL, &rcuhier_fops);
|
||||
if (!hierdir)
|
||||
if (!retval)
|
||||
goto free_out;
|
||||
|
||||
rcu_pendingdir = debugfs_create_file("rcu_pending", 0444, rcudir,
|
||||
retval = debugfs_create_file("rcu_pending", 0444, rcudir,
|
||||
NULL, &rcu_pending_fops);
|
||||
if (!rcu_pendingdir)
|
||||
if (!retval)
|
||||
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:
|
||||
debugfs_remove_recursive(rcudir);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void __exit rcuclassic_trace_cleanup(void)
|
||||
{
|
||||
debugfs_remove(datadir);
|
||||
debugfs_remove(datadir_csv);
|
||||
debugfs_remove(gpdir);
|
||||
debugfs_remove(hierdir);
|
||||
debugfs_remove(rcu_pendingdir);
|
||||
debugfs_remove(rcudir);
|
||||
debugfs_remove_recursive(rcudir);
|
||||
}
|
||||
|
||||
|
||||
|
1244
kernel/sched.c
1244
kernel/sched.c
File diff suppressed because it is too large
Load Diff
@@ -127,21 +127,11 @@ void cpupri_set(struct cpupri *cp, int cpu, int newpri)
|
||||
|
||||
/*
|
||||
* If the cpu was currently mapped to a different value, we
|
||||
* first need to unmap the old value
|
||||
* need to map it to the new value then remove the old value.
|
||||
* Note, we must add the new value first, otherwise we risk the
|
||||
* cpu being cleared from pri_active, and this cpu could be
|
||||
* missed for a push or pull.
|
||||
*/
|
||||
if (likely(oldpri != CPUPRI_INVALID)) {
|
||||
struct cpupri_vec *vec = &cp->pri_to_cpu[oldpri];
|
||||
|
||||
spin_lock_irqsave(&vec->lock, flags);
|
||||
|
||||
vec->count--;
|
||||
if (!vec->count)
|
||||
clear_bit(oldpri, cp->pri_active);
|
||||
cpumask_clear_cpu(cpu, vec->mask);
|
||||
|
||||
spin_unlock_irqrestore(&vec->lock, flags);
|
||||
}
|
||||
|
||||
if (likely(newpri != CPUPRI_INVALID)) {
|
||||
struct cpupri_vec *vec = &cp->pri_to_cpu[newpri];
|
||||
|
||||
@@ -154,6 +144,18 @@ void cpupri_set(struct cpupri *cp, int cpu, int newpri)
|
||||
|
||||
spin_unlock_irqrestore(&vec->lock, flags);
|
||||
}
|
||||
if (likely(oldpri != CPUPRI_INVALID)) {
|
||||
struct cpupri_vec *vec = &cp->pri_to_cpu[oldpri];
|
||||
|
||||
spin_lock_irqsave(&vec->lock, flags);
|
||||
|
||||
vec->count--;
|
||||
if (!vec->count)
|
||||
clear_bit(oldpri, cp->pri_active);
|
||||
cpumask_clear_cpu(cpu, vec->mask);
|
||||
|
||||
spin_unlock_irqrestore(&vec->lock, flags);
|
||||
}
|
||||
|
||||
*currpri = newpri;
|
||||
}
|
||||
|
@@ -409,6 +409,8 @@ void proc_sched_show_task(struct task_struct *p, struct seq_file *m)
|
||||
PN(se.wait_max);
|
||||
PN(se.wait_sum);
|
||||
P(se.wait_count);
|
||||
PN(se.iowait_sum);
|
||||
P(se.iowait_count);
|
||||
P(sched_info.bkl_count);
|
||||
P(se.nr_migrations);
|
||||
P(se.nr_migrations_cold);
|
||||
@@ -479,6 +481,8 @@ void proc_sched_set_task(struct task_struct *p)
|
||||
p->se.wait_max = 0;
|
||||
p->se.wait_sum = 0;
|
||||
p->se.wait_count = 0;
|
||||
p->se.iowait_sum = 0;
|
||||
p->se.iowait_count = 0;
|
||||
p->se.sleep_max = 0;
|
||||
p->se.sum_sleep_runtime = 0;
|
||||
p->se.block_max = 0;
|
||||
|
@@ -24,7 +24,7 @@
|
||||
|
||||
/*
|
||||
* Targeted preemption latency for CPU-bound tasks:
|
||||
* (default: 20ms * (1 + ilog(ncpus)), units: nanoseconds)
|
||||
* (default: 5ms * (1 + ilog(ncpus)), units: nanoseconds)
|
||||
*
|
||||
* NOTE: this latency value is not the same as the concept of
|
||||
* 'timeslice length' - timeslices in CFS are of variable length
|
||||
@@ -34,13 +34,13 @@
|
||||
* (to see the precise effective timeslice length of your workload,
|
||||
* run vmstat and monitor the context-switches (cs) field)
|
||||
*/
|
||||
unsigned int sysctl_sched_latency = 20000000ULL;
|
||||
unsigned int sysctl_sched_latency = 5000000ULL;
|
||||
|
||||
/*
|
||||
* Minimal preemption granularity for CPU-bound tasks:
|
||||
* (default: 4 msec * (1 + ilog(ncpus)), units: nanoseconds)
|
||||
* (default: 1 msec * (1 + ilog(ncpus)), units: nanoseconds)
|
||||
*/
|
||||
unsigned int sysctl_sched_min_granularity = 4000000ULL;
|
||||
unsigned int sysctl_sched_min_granularity = 1000000ULL;
|
||||
|
||||
/*
|
||||
* is kept at sysctl_sched_latency / sysctl_sched_min_granularity
|
||||
@@ -48,10 +48,10 @@ unsigned int sysctl_sched_min_granularity = 4000000ULL;
|
||||
static unsigned int sched_nr_latency = 5;
|
||||
|
||||
/*
|
||||
* After fork, child runs first. (default) If set to 0 then
|
||||
* After fork, child runs first. If set to 0 (default) then
|
||||
* parent will (try to) run first.
|
||||
*/
|
||||
const_debug unsigned int sysctl_sched_child_runs_first = 1;
|
||||
unsigned int sysctl_sched_child_runs_first __read_mostly;
|
||||
|
||||
/*
|
||||
* sys_sched_yield() compat mode
|
||||
@@ -63,13 +63,13 @@ unsigned int __read_mostly sysctl_sched_compat_yield;
|
||||
|
||||
/*
|
||||
* SCHED_OTHER wake-up granularity.
|
||||
* (default: 5 msec * (1 + ilog(ncpus)), units: nanoseconds)
|
||||
* (default: 1 msec * (1 + ilog(ncpus)), units: nanoseconds)
|
||||
*
|
||||
* This option delays the preemption effects of decoupled workloads
|
||||
* and reduces their over-scheduling. Synchronous workloads will still
|
||||
* have immediate wakeup/sleep latencies.
|
||||
*/
|
||||
unsigned int sysctl_sched_wakeup_granularity = 5000000UL;
|
||||
unsigned int sysctl_sched_wakeup_granularity = 1000000UL;
|
||||
|
||||
const_debug unsigned int sysctl_sched_migration_cost = 500000UL;
|
||||
|
||||
@@ -79,11 +79,6 @@ static const struct sched_class fair_sched_class;
|
||||
* CFS operations on generic schedulable entities:
|
||||
*/
|
||||
|
||||
static inline struct task_struct *task_of(struct sched_entity *se)
|
||||
{
|
||||
return container_of(se, struct task_struct, se);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_FAIR_GROUP_SCHED
|
||||
|
||||
/* cpu runqueue to which this cfs_rq is attached */
|
||||
@@ -95,6 +90,14 @@ static inline struct rq *rq_of(struct cfs_rq *cfs_rq)
|
||||
/* An entity is a task if it doesn't "own" a runqueue */
|
||||
#define entity_is_task(se) (!se->my_q)
|
||||
|
||||
static inline struct task_struct *task_of(struct sched_entity *se)
|
||||
{
|
||||
#ifdef CONFIG_SCHED_DEBUG
|
||||
WARN_ON_ONCE(!entity_is_task(se));
|
||||
#endif
|
||||
return container_of(se, struct task_struct, se);
|
||||
}
|
||||
|
||||
/* Walk up scheduling entities hierarchy */
|
||||
#define for_each_sched_entity(se) \
|
||||
for (; se; se = se->parent)
|
||||
@@ -186,7 +189,12 @@ find_matching_se(struct sched_entity **se, struct sched_entity **pse)
|
||||
}
|
||||
}
|
||||
|
||||
#else /* CONFIG_FAIR_GROUP_SCHED */
|
||||
#else /* !CONFIG_FAIR_GROUP_SCHED */
|
||||
|
||||
static inline struct task_struct *task_of(struct sched_entity *se)
|
||||
{
|
||||
return container_of(se, struct task_struct, se);
|
||||
}
|
||||
|
||||
static inline struct rq *rq_of(struct cfs_rq *cfs_rq)
|
||||
{
|
||||
@@ -537,6 +545,12 @@ update_stats_wait_end(struct cfs_rq *cfs_rq, struct sched_entity *se)
|
||||
schedstat_set(se->wait_count, se->wait_count + 1);
|
||||
schedstat_set(se->wait_sum, se->wait_sum +
|
||||
rq_of(cfs_rq)->clock - se->wait_start);
|
||||
#ifdef CONFIG_SCHEDSTATS
|
||||
if (entity_is_task(se)) {
|
||||
trace_sched_stat_wait(task_of(se),
|
||||
rq_of(cfs_rq)->clock - se->wait_start);
|
||||
}
|
||||
#endif
|
||||
schedstat_set(se->wait_start, 0);
|
||||
}
|
||||
|
||||
@@ -628,8 +642,10 @@ static void enqueue_sleeper(struct cfs_rq *cfs_rq, struct sched_entity *se)
|
||||
se->sleep_start = 0;
|
||||
se->sum_sleep_runtime += delta;
|
||||
|
||||
if (tsk)
|
||||
if (tsk) {
|
||||
account_scheduler_latency(tsk, delta >> 10, 1);
|
||||
trace_sched_stat_sleep(tsk, delta);
|
||||
}
|
||||
}
|
||||
if (se->block_start) {
|
||||
u64 delta = rq_of(cfs_rq)->clock - se->block_start;
|
||||
@@ -644,6 +660,12 @@ static void enqueue_sleeper(struct cfs_rq *cfs_rq, struct sched_entity *se)
|
||||
se->sum_sleep_runtime += delta;
|
||||
|
||||
if (tsk) {
|
||||
if (tsk->in_iowait) {
|
||||
se->iowait_sum += delta;
|
||||
se->iowait_count++;
|
||||
trace_sched_stat_iowait(tsk, delta);
|
||||
}
|
||||
|
||||
/*
|
||||
* Blocking time is in units of nanosecs, so shift by
|
||||
* 20 to get a milliseconds-range estimation of the
|
||||
@@ -705,11 +727,11 @@ place_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int initial)
|
||||
|
||||
vruntime -= thresh;
|
||||
}
|
||||
|
||||
/* ensure we never gain time by being placed backwards. */
|
||||
vruntime = max_vruntime(se->vruntime, vruntime);
|
||||
}
|
||||
|
||||
/* ensure we never gain time by being placed backwards. */
|
||||
vruntime = max_vruntime(se->vruntime, vruntime);
|
||||
|
||||
se->vruntime = vruntime;
|
||||
}
|
||||
|
||||
@@ -1046,17 +1068,21 @@ 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_mask)
|
||||
* hence we need to mask them out (rq->rd->online)
|
||||
*
|
||||
* Returns the CPU we should wake onto.
|
||||
*/
|
||||
#if defined(ARCH_HAS_SCHED_WAKE_IDLE)
|
||||
|
||||
#define cpu_rd_active(cpu, rq) cpumask_test_cpu(cpu, rq->rd->online)
|
||||
|
||||
static int wake_idle(int cpu, struct task_struct *p)
|
||||
{
|
||||
struct sched_domain *sd;
|
||||
int i;
|
||||
unsigned int chosen_wakeup_cpu;
|
||||
int this_cpu;
|
||||
struct rq *task_rq = task_rq(p);
|
||||
|
||||
/*
|
||||
* At POWERSAVINGS_BALANCE_WAKEUP level, if both this_cpu and prev_cpu
|
||||
@@ -1089,10 +1115,10 @@ static int wake_idle(int cpu, struct task_struct *p)
|
||||
for_each_domain(cpu, sd) {
|
||||
if ((sd->flags & SD_WAKE_IDLE)
|
||||
|| ((sd->flags & SD_WAKE_IDLE_FAR)
|
||||
&& !task_hot(p, task_rq(p)->clock, sd))) {
|
||||
&& !task_hot(p, task_rq->clock, sd))) {
|
||||
for_each_cpu_and(i, sched_domain_span(sd),
|
||||
&p->cpus_allowed) {
|
||||
if (cpu_active(i) && idle_cpu(i)) {
|
||||
if (cpu_rd_active(i, task_rq) && idle_cpu(i)) {
|
||||
if (i != task_cpu(p)) {
|
||||
schedstat_inc(p,
|
||||
se.nr_wakeups_idle);
|
||||
@@ -1235,7 +1261,17 @@ wake_affine(struct sched_domain *this_sd, struct rq *this_rq,
|
||||
tg = task_group(p);
|
||||
weight = p->se.load.weight;
|
||||
|
||||
balanced = 100*(tl + effective_load(tg, this_cpu, weight, weight)) <=
|
||||
/*
|
||||
* In low-load situations, where prev_cpu is idle and this_cpu is idle
|
||||
* due to the sync cause above having dropped tl to 0, we'll always have
|
||||
* an imbalance, but there's really nothing you can do about that, so
|
||||
* that's good too.
|
||||
*
|
||||
* Otherwise check if either cpus are near enough in load to allow this
|
||||
* task to be woken on this_cpu.
|
||||
*/
|
||||
balanced = !tl ||
|
||||
100*(tl + effective_load(tg, this_cpu, weight, weight)) <=
|
||||
imbalance*(load + effective_load(tg, prev_cpu, 0, weight));
|
||||
|
||||
/*
|
||||
@@ -1278,8 +1314,6 @@ static int select_task_rq_fair(struct task_struct *p, int sync)
|
||||
this_rq = cpu_rq(this_cpu);
|
||||
new_cpu = prev_cpu;
|
||||
|
||||
if (prev_cpu == this_cpu)
|
||||
goto out;
|
||||
/*
|
||||
* 'this_sd' is the first domain that both
|
||||
* this_cpu and prev_cpu are present in:
|
||||
@@ -1721,6 +1755,8 @@ static void task_new_fair(struct rq *rq, struct task_struct *p)
|
||||
sched_info_queued(p);
|
||||
|
||||
update_curr(cfs_rq);
|
||||
if (curr)
|
||||
se->vruntime = curr->vruntime;
|
||||
place_entity(cfs_rq, se, 1);
|
||||
|
||||
/* 'curr' will be NULL if the child belongs to a different group */
|
||||
|
@@ -1,4 +1,4 @@
|
||||
SCHED_FEAT(NEW_FAIR_SLEEPERS, 1)
|
||||
SCHED_FEAT(NEW_FAIR_SLEEPERS, 0)
|
||||
SCHED_FEAT(NORMALIZED_SLEEPER, 0)
|
||||
SCHED_FEAT(ADAPTIVE_GRAN, 1)
|
||||
SCHED_FEAT(WAKEUP_PREEMPT, 1)
|
||||
|
@@ -3,15 +3,18 @@
|
||||
* policies)
|
||||
*/
|
||||
|
||||
static inline struct task_struct *rt_task_of(struct sched_rt_entity *rt_se)
|
||||
{
|
||||
return container_of(rt_se, struct task_struct, rt);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_RT_GROUP_SCHED
|
||||
|
||||
#define rt_entity_is_task(rt_se) (!(rt_se)->my_q)
|
||||
|
||||
static inline struct task_struct *rt_task_of(struct sched_rt_entity *rt_se)
|
||||
{
|
||||
#ifdef CONFIG_SCHED_DEBUG
|
||||
WARN_ON_ONCE(!rt_entity_is_task(rt_se));
|
||||
#endif
|
||||
return container_of(rt_se, struct task_struct, rt);
|
||||
}
|
||||
|
||||
static inline struct rq *rq_of_rt_rq(struct rt_rq *rt_rq)
|
||||
{
|
||||
return rt_rq->rq;
|
||||
@@ -26,6 +29,11 @@ static inline struct rt_rq *rt_rq_of_se(struct sched_rt_entity *rt_se)
|
||||
|
||||
#define rt_entity_is_task(rt_se) (1)
|
||||
|
||||
static inline struct task_struct *rt_task_of(struct sched_rt_entity *rt_se)
|
||||
{
|
||||
return container_of(rt_se, struct task_struct, rt);
|
||||
}
|
||||
|
||||
static inline struct rq *rq_of_rt_rq(struct rt_rq *rt_rq)
|
||||
{
|
||||
return container_of(rt_rq, struct rq, rt);
|
||||
@@ -128,6 +136,11 @@ static void dequeue_pushable_task(struct rq *rq, struct task_struct *p)
|
||||
plist_del(&p->pushable_tasks, &rq->rt.pushable_tasks);
|
||||
}
|
||||
|
||||
static inline int has_pushable_tasks(struct rq *rq)
|
||||
{
|
||||
return !plist_head_empty(&rq->rt.pushable_tasks);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline void enqueue_pushable_task(struct rq *rq, struct task_struct *p)
|
||||
@@ -602,6 +615,8 @@ static void update_curr_rt(struct rq *rq)
|
||||
curr->se.exec_start = rq->clock;
|
||||
cpuacct_charge(curr, delta_exec);
|
||||
|
||||
sched_rt_avg_update(rq, delta_exec);
|
||||
|
||||
if (!rt_bandwidth_enabled())
|
||||
return;
|
||||
|
||||
@@ -874,8 +889,6 @@ static void enqueue_task_rt(struct rq *rq, struct task_struct *p, int wakeup)
|
||||
|
||||
if (!task_current(rq, p) && p->rt.nr_cpus_allowed > 1)
|
||||
enqueue_pushable_task(rq, p);
|
||||
|
||||
inc_cpu_load(rq, p->se.load.weight);
|
||||
}
|
||||
|
||||
static void dequeue_task_rt(struct rq *rq, struct task_struct *p, int sleep)
|
||||
@@ -886,8 +899,6 @@ static void dequeue_task_rt(struct rq *rq, struct task_struct *p, int sleep)
|
||||
dequeue_rt_entity(rt_se);
|
||||
|
||||
dequeue_pushable_task(rq, p);
|
||||
|
||||
dec_cpu_load(rq, p->se.load.weight);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1064,6 +1075,14 @@ static struct task_struct *pick_next_task_rt(struct rq *rq)
|
||||
if (p)
|
||||
dequeue_pushable_task(rq, p);
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
/*
|
||||
* We detect this state here so that we can avoid taking the RQ
|
||||
* lock again later if there is no need to push
|
||||
*/
|
||||
rq->post_schedule = has_pushable_tasks(rq);
|
||||
#endif
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
@@ -1161,13 +1180,6 @@ static int find_lowest_rq(struct task_struct *task)
|
||||
if (!cpupri_find(&task_rq(task)->rd->cpupri, task, lowest_mask))
|
||||
return -1; /* No targets found */
|
||||
|
||||
/*
|
||||
* Only consider CPUs that are usable for migration.
|
||||
* I guess we might want to change cpupri_find() to ignore those
|
||||
* in the first place.
|
||||
*/
|
||||
cpumask_and(lowest_mask, lowest_mask, cpu_active_mask);
|
||||
|
||||
/*
|
||||
* At this point we have built a mask of cpus representing the
|
||||
* lowest priority tasks in the system. Now we want to elect
|
||||
@@ -1262,11 +1274,6 @@ static struct rq *find_lock_lowest_rq(struct task_struct *task, struct rq *rq)
|
||||
return lowest_rq;
|
||||
}
|
||||
|
||||
static inline int has_pushable_tasks(struct rq *rq)
|
||||
{
|
||||
return !plist_head_empty(&rq->rt.pushable_tasks);
|
||||
}
|
||||
|
||||
static struct task_struct *pick_next_pushable_task(struct rq *rq)
|
||||
{
|
||||
struct task_struct *p;
|
||||
@@ -1466,23 +1473,9 @@ static void pre_schedule_rt(struct rq *rq, struct task_struct *prev)
|
||||
pull_rt_task(rq);
|
||||
}
|
||||
|
||||
/*
|
||||
* assumes rq->lock is held
|
||||
*/
|
||||
static int needs_post_schedule_rt(struct rq *rq)
|
||||
{
|
||||
return has_pushable_tasks(rq);
|
||||
}
|
||||
|
||||
static void post_schedule_rt(struct rq *rq)
|
||||
{
|
||||
/*
|
||||
* This is only called if needs_post_schedule_rt() indicates that
|
||||
* we need to push tasks away
|
||||
*/
|
||||
spin_lock_irq(&rq->lock);
|
||||
push_rt_tasks(rq);
|
||||
spin_unlock_irq(&rq->lock);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1758,7 +1751,6 @@ static const struct sched_class rt_sched_class = {
|
||||
.rq_online = rq_online_rt,
|
||||
.rq_offline = rq_offline_rt,
|
||||
.pre_schedule = pre_schedule_rt,
|
||||
.needs_post_schedule = needs_post_schedule_rt,
|
||||
.post_schedule = post_schedule_rt,
|
||||
.task_wake_up = task_wake_up_rt,
|
||||
.switched_from = switched_from_rt,
|
||||
|
@@ -227,7 +227,7 @@ restart:
|
||||
preempt_count() = prev_count;
|
||||
}
|
||||
|
||||
rcu_bh_qsctr_inc(cpu);
|
||||
rcu_bh_qs(cpu);
|
||||
}
|
||||
h++;
|
||||
pending >>= 1;
|
||||
@@ -721,7 +721,7 @@ static int ksoftirqd(void * __bind_cpu)
|
||||
preempt_enable_no_resched();
|
||||
cond_resched();
|
||||
preempt_disable();
|
||||
rcu_qsctr_inc((long)__bind_cpu);
|
||||
rcu_sched_qs((long)__bind_cpu);
|
||||
}
|
||||
preempt_enable();
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
|
@@ -21,44 +21,29 @@
|
||||
#include <linux/debug_locks.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#ifndef _spin_trylock
|
||||
int __lockfunc _spin_trylock(spinlock_t *lock)
|
||||
{
|
||||
preempt_disable();
|
||||
if (_raw_spin_trylock(lock)) {
|
||||
spin_acquire(&lock->dep_map, 0, 1, _RET_IP_);
|
||||
return 1;
|
||||
}
|
||||
|
||||
preempt_enable();
|
||||
return 0;
|
||||
return __spin_trylock(lock);
|
||||
}
|
||||
EXPORT_SYMBOL(_spin_trylock);
|
||||
#endif
|
||||
|
||||
#ifndef _read_trylock
|
||||
int __lockfunc _read_trylock(rwlock_t *lock)
|
||||
{
|
||||
preempt_disable();
|
||||
if (_raw_read_trylock(lock)) {
|
||||
rwlock_acquire_read(&lock->dep_map, 0, 1, _RET_IP_);
|
||||
return 1;
|
||||
}
|
||||
|
||||
preempt_enable();
|
||||
return 0;
|
||||
return __read_trylock(lock);
|
||||
}
|
||||
EXPORT_SYMBOL(_read_trylock);
|
||||
#endif
|
||||
|
||||
#ifndef _write_trylock
|
||||
int __lockfunc _write_trylock(rwlock_t *lock)
|
||||
{
|
||||
preempt_disable();
|
||||
if (_raw_write_trylock(lock)) {
|
||||
rwlock_acquire(&lock->dep_map, 0, 1, _RET_IP_);
|
||||
return 1;
|
||||
}
|
||||
|
||||
preempt_enable();
|
||||
return 0;
|
||||
return __write_trylock(lock);
|
||||
}
|
||||
EXPORT_SYMBOL(_write_trylock);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* If lockdep is enabled then we use the non-preemption spin-ops
|
||||
@@ -67,132 +52,101 @@ EXPORT_SYMBOL(_write_trylock);
|
||||
*/
|
||||
#if !defined(CONFIG_GENERIC_LOCKBREAK) || defined(CONFIG_DEBUG_LOCK_ALLOC)
|
||||
|
||||
#ifndef _read_lock
|
||||
void __lockfunc _read_lock(rwlock_t *lock)
|
||||
{
|
||||
preempt_disable();
|
||||
rwlock_acquire_read(&lock->dep_map, 0, 0, _RET_IP_);
|
||||
LOCK_CONTENDED(lock, _raw_read_trylock, _raw_read_lock);
|
||||
__read_lock(lock);
|
||||
}
|
||||
EXPORT_SYMBOL(_read_lock);
|
||||
#endif
|
||||
|
||||
#ifndef _spin_lock_irqsave
|
||||
unsigned long __lockfunc _spin_lock_irqsave(spinlock_t *lock)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
local_irq_save(flags);
|
||||
preempt_disable();
|
||||
spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
|
||||
/*
|
||||
* On lockdep we dont want the hand-coded irq-enable of
|
||||
* _raw_spin_lock_flags() code, because lockdep assumes
|
||||
* that interrupts are not re-enabled during lock-acquire:
|
||||
*/
|
||||
#ifdef CONFIG_LOCKDEP
|
||||
LOCK_CONTENDED(lock, _raw_spin_trylock, _raw_spin_lock);
|
||||
#else
|
||||
_raw_spin_lock_flags(lock, &flags);
|
||||
#endif
|
||||
return flags;
|
||||
return __spin_lock_irqsave(lock);
|
||||
}
|
||||
EXPORT_SYMBOL(_spin_lock_irqsave);
|
||||
#endif
|
||||
|
||||
#ifndef _spin_lock_irq
|
||||
void __lockfunc _spin_lock_irq(spinlock_t *lock)
|
||||
{
|
||||
local_irq_disable();
|
||||
preempt_disable();
|
||||
spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
|
||||
LOCK_CONTENDED(lock, _raw_spin_trylock, _raw_spin_lock);
|
||||
__spin_lock_irq(lock);
|
||||
}
|
||||
EXPORT_SYMBOL(_spin_lock_irq);
|
||||
#endif
|
||||
|
||||
#ifndef _spin_lock_bh
|
||||
void __lockfunc _spin_lock_bh(spinlock_t *lock)
|
||||
{
|
||||
local_bh_disable();
|
||||
preempt_disable();
|
||||
spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
|
||||
LOCK_CONTENDED(lock, _raw_spin_trylock, _raw_spin_lock);
|
||||
__spin_lock_bh(lock);
|
||||
}
|
||||
EXPORT_SYMBOL(_spin_lock_bh);
|
||||
#endif
|
||||
|
||||
#ifndef _read_lock_irqsave
|
||||
unsigned long __lockfunc _read_lock_irqsave(rwlock_t *lock)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
local_irq_save(flags);
|
||||
preempt_disable();
|
||||
rwlock_acquire_read(&lock->dep_map, 0, 0, _RET_IP_);
|
||||
LOCK_CONTENDED_FLAGS(lock, _raw_read_trylock, _raw_read_lock,
|
||||
_raw_read_lock_flags, &flags);
|
||||
return flags;
|
||||
return __read_lock_irqsave(lock);
|
||||
}
|
||||
EXPORT_SYMBOL(_read_lock_irqsave);
|
||||
#endif
|
||||
|
||||
#ifndef _read_lock_irq
|
||||
void __lockfunc _read_lock_irq(rwlock_t *lock)
|
||||
{
|
||||
local_irq_disable();
|
||||
preempt_disable();
|
||||
rwlock_acquire_read(&lock->dep_map, 0, 0, _RET_IP_);
|
||||
LOCK_CONTENDED(lock, _raw_read_trylock, _raw_read_lock);
|
||||
__read_lock_irq(lock);
|
||||
}
|
||||
EXPORT_SYMBOL(_read_lock_irq);
|
||||
#endif
|
||||
|
||||
#ifndef _read_lock_bh
|
||||
void __lockfunc _read_lock_bh(rwlock_t *lock)
|
||||
{
|
||||
local_bh_disable();
|
||||
preempt_disable();
|
||||
rwlock_acquire_read(&lock->dep_map, 0, 0, _RET_IP_);
|
||||
LOCK_CONTENDED(lock, _raw_read_trylock, _raw_read_lock);
|
||||
__read_lock_bh(lock);
|
||||
}
|
||||
EXPORT_SYMBOL(_read_lock_bh);
|
||||
#endif
|
||||
|
||||
#ifndef _write_lock_irqsave
|
||||
unsigned long __lockfunc _write_lock_irqsave(rwlock_t *lock)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
local_irq_save(flags);
|
||||
preempt_disable();
|
||||
rwlock_acquire(&lock->dep_map, 0, 0, _RET_IP_);
|
||||
LOCK_CONTENDED_FLAGS(lock, _raw_write_trylock, _raw_write_lock,
|
||||
_raw_write_lock_flags, &flags);
|
||||
return flags;
|
||||
return __write_lock_irqsave(lock);
|
||||
}
|
||||
EXPORT_SYMBOL(_write_lock_irqsave);
|
||||
#endif
|
||||
|
||||
#ifndef _write_lock_irq
|
||||
void __lockfunc _write_lock_irq(rwlock_t *lock)
|
||||
{
|
||||
local_irq_disable();
|
||||
preempt_disable();
|
||||
rwlock_acquire(&lock->dep_map, 0, 0, _RET_IP_);
|
||||
LOCK_CONTENDED(lock, _raw_write_trylock, _raw_write_lock);
|
||||
__write_lock_irq(lock);
|
||||
}
|
||||
EXPORT_SYMBOL(_write_lock_irq);
|
||||
#endif
|
||||
|
||||
#ifndef _write_lock_bh
|
||||
void __lockfunc _write_lock_bh(rwlock_t *lock)
|
||||
{
|
||||
local_bh_disable();
|
||||
preempt_disable();
|
||||
rwlock_acquire(&lock->dep_map, 0, 0, _RET_IP_);
|
||||
LOCK_CONTENDED(lock, _raw_write_trylock, _raw_write_lock);
|
||||
__write_lock_bh(lock);
|
||||
}
|
||||
EXPORT_SYMBOL(_write_lock_bh);
|
||||
#endif
|
||||
|
||||
#ifndef _spin_lock
|
||||
void __lockfunc _spin_lock(spinlock_t *lock)
|
||||
{
|
||||
preempt_disable();
|
||||
spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
|
||||
LOCK_CONTENDED(lock, _raw_spin_trylock, _raw_spin_lock);
|
||||
__spin_lock(lock);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(_spin_lock);
|
||||
#endif
|
||||
|
||||
#ifndef _write_lock
|
||||
void __lockfunc _write_lock(rwlock_t *lock)
|
||||
{
|
||||
preempt_disable();
|
||||
rwlock_acquire(&lock->dep_map, 0, 0, _RET_IP_);
|
||||
LOCK_CONTENDED(lock, _raw_write_trylock, _raw_write_lock);
|
||||
__write_lock(lock);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(_write_lock);
|
||||
#endif
|
||||
|
||||
#else /* CONFIG_PREEMPT: */
|
||||
|
||||
@@ -318,125 +272,109 @@ EXPORT_SYMBOL(_spin_lock_nest_lock);
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef _spin_unlock
|
||||
void __lockfunc _spin_unlock(spinlock_t *lock)
|
||||
{
|
||||
spin_release(&lock->dep_map, 1, _RET_IP_);
|
||||
_raw_spin_unlock(lock);
|
||||
preempt_enable();
|
||||
__spin_unlock(lock);
|
||||
}
|
||||
EXPORT_SYMBOL(_spin_unlock);
|
||||
#endif
|
||||
|
||||
#ifndef _write_unlock
|
||||
void __lockfunc _write_unlock(rwlock_t *lock)
|
||||
{
|
||||
rwlock_release(&lock->dep_map, 1, _RET_IP_);
|
||||
_raw_write_unlock(lock);
|
||||
preempt_enable();
|
||||
__write_unlock(lock);
|
||||
}
|
||||
EXPORT_SYMBOL(_write_unlock);
|
||||
#endif
|
||||
|
||||
#ifndef _read_unlock
|
||||
void __lockfunc _read_unlock(rwlock_t *lock)
|
||||
{
|
||||
rwlock_release(&lock->dep_map, 1, _RET_IP_);
|
||||
_raw_read_unlock(lock);
|
||||
preempt_enable();
|
||||
__read_unlock(lock);
|
||||
}
|
||||
EXPORT_SYMBOL(_read_unlock);
|
||||
#endif
|
||||
|
||||
#ifndef _spin_unlock_irqrestore
|
||||
void __lockfunc _spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags)
|
||||
{
|
||||
spin_release(&lock->dep_map, 1, _RET_IP_);
|
||||
_raw_spin_unlock(lock);
|
||||
local_irq_restore(flags);
|
||||
preempt_enable();
|
||||
__spin_unlock_irqrestore(lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL(_spin_unlock_irqrestore);
|
||||
#endif
|
||||
|
||||
#ifndef _spin_unlock_irq
|
||||
void __lockfunc _spin_unlock_irq(spinlock_t *lock)
|
||||
{
|
||||
spin_release(&lock->dep_map, 1, _RET_IP_);
|
||||
_raw_spin_unlock(lock);
|
||||
local_irq_enable();
|
||||
preempt_enable();
|
||||
__spin_unlock_irq(lock);
|
||||
}
|
||||
EXPORT_SYMBOL(_spin_unlock_irq);
|
||||
#endif
|
||||
|
||||
#ifndef _spin_unlock_bh
|
||||
void __lockfunc _spin_unlock_bh(spinlock_t *lock)
|
||||
{
|
||||
spin_release(&lock->dep_map, 1, _RET_IP_);
|
||||
_raw_spin_unlock(lock);
|
||||
preempt_enable_no_resched();
|
||||
local_bh_enable_ip((unsigned long)__builtin_return_address(0));
|
||||
__spin_unlock_bh(lock);
|
||||
}
|
||||
EXPORT_SYMBOL(_spin_unlock_bh);
|
||||
#endif
|
||||
|
||||
#ifndef _read_unlock_irqrestore
|
||||
void __lockfunc _read_unlock_irqrestore(rwlock_t *lock, unsigned long flags)
|
||||
{
|
||||
rwlock_release(&lock->dep_map, 1, _RET_IP_);
|
||||
_raw_read_unlock(lock);
|
||||
local_irq_restore(flags);
|
||||
preempt_enable();
|
||||
__read_unlock_irqrestore(lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL(_read_unlock_irqrestore);
|
||||
#endif
|
||||
|
||||
#ifndef _read_unlock_irq
|
||||
void __lockfunc _read_unlock_irq(rwlock_t *lock)
|
||||
{
|
||||
rwlock_release(&lock->dep_map, 1, _RET_IP_);
|
||||
_raw_read_unlock(lock);
|
||||
local_irq_enable();
|
||||
preempt_enable();
|
||||
__read_unlock_irq(lock);
|
||||
}
|
||||
EXPORT_SYMBOL(_read_unlock_irq);
|
||||
#endif
|
||||
|
||||
#ifndef _read_unlock_bh
|
||||
void __lockfunc _read_unlock_bh(rwlock_t *lock)
|
||||
{
|
||||
rwlock_release(&lock->dep_map, 1, _RET_IP_);
|
||||
_raw_read_unlock(lock);
|
||||
preempt_enable_no_resched();
|
||||
local_bh_enable_ip((unsigned long)__builtin_return_address(0));
|
||||
__read_unlock_bh(lock);
|
||||
}
|
||||
EXPORT_SYMBOL(_read_unlock_bh);
|
||||
#endif
|
||||
|
||||
#ifndef _write_unlock_irqrestore
|
||||
void __lockfunc _write_unlock_irqrestore(rwlock_t *lock, unsigned long flags)
|
||||
{
|
||||
rwlock_release(&lock->dep_map, 1, _RET_IP_);
|
||||
_raw_write_unlock(lock);
|
||||
local_irq_restore(flags);
|
||||
preempt_enable();
|
||||
__write_unlock_irqrestore(lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL(_write_unlock_irqrestore);
|
||||
#endif
|
||||
|
||||
#ifndef _write_unlock_irq
|
||||
void __lockfunc _write_unlock_irq(rwlock_t *lock)
|
||||
{
|
||||
rwlock_release(&lock->dep_map, 1, _RET_IP_);
|
||||
_raw_write_unlock(lock);
|
||||
local_irq_enable();
|
||||
preempt_enable();
|
||||
__write_unlock_irq(lock);
|
||||
}
|
||||
EXPORT_SYMBOL(_write_unlock_irq);
|
||||
#endif
|
||||
|
||||
#ifndef _write_unlock_bh
|
||||
void __lockfunc _write_unlock_bh(rwlock_t *lock)
|
||||
{
|
||||
rwlock_release(&lock->dep_map, 1, _RET_IP_);
|
||||
_raw_write_unlock(lock);
|
||||
preempt_enable_no_resched();
|
||||
local_bh_enable_ip((unsigned long)__builtin_return_address(0));
|
||||
__write_unlock_bh(lock);
|
||||
}
|
||||
EXPORT_SYMBOL(_write_unlock_bh);
|
||||
#endif
|
||||
|
||||
#ifndef _spin_trylock_bh
|
||||
int __lockfunc _spin_trylock_bh(spinlock_t *lock)
|
||||
{
|
||||
local_bh_disable();
|
||||
preempt_disable();
|
||||
if (_raw_spin_trylock(lock)) {
|
||||
spin_acquire(&lock->dep_map, 0, 1, _RET_IP_);
|
||||
return 1;
|
||||
}
|
||||
|
||||
preempt_enable_no_resched();
|
||||
local_bh_enable_ip((unsigned long)__builtin_return_address(0));
|
||||
return 0;
|
||||
return __spin_trylock_bh(lock);
|
||||
}
|
||||
EXPORT_SYMBOL(_spin_trylock_bh);
|
||||
#endif
|
||||
|
||||
notrace int in_lock_functions(unsigned long addr)
|
||||
{
|
||||
|
@@ -49,7 +49,6 @@
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/ftrace.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/slow-work.h>
|
||||
#include <linux/perf_counter.h>
|
||||
|
||||
@@ -246,6 +245,14 @@ static int max_wakeup_granularity_ns = NSEC_PER_SEC; /* 1 second */
|
||||
#endif
|
||||
|
||||
static struct ctl_table kern_table[] = {
|
||||
{
|
||||
.ctl_name = CTL_UNNUMBERED,
|
||||
.procname = "sched_child_runs_first",
|
||||
.data = &sysctl_sched_child_runs_first,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec,
|
||||
},
|
||||
#ifdef CONFIG_SCHED_DEBUG
|
||||
{
|
||||
.ctl_name = CTL_UNNUMBERED,
|
||||
@@ -298,14 +305,6 @@ static struct ctl_table kern_table[] = {
|
||||
.strategy = &sysctl_intvec,
|
||||
.extra1 = &zero,
|
||||
},
|
||||
{
|
||||
.ctl_name = CTL_UNNUMBERED,
|
||||
.procname = "sched_child_runs_first",
|
||||
.data = &sysctl_sched_child_runs_first,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec,
|
||||
},
|
||||
{
|
||||
.ctl_name = CTL_UNNUMBERED,
|
||||
.procname = "sched_features",
|
||||
@@ -330,6 +329,14 @@ static struct ctl_table kern_table[] = {
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec,
|
||||
},
|
||||
{
|
||||
.ctl_name = CTL_UNNUMBERED,
|
||||
.procname = "sched_time_avg",
|
||||
.data = &sysctl_sched_time_avg,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec,
|
||||
},
|
||||
{
|
||||
.ctl_name = CTL_UNNUMBERED,
|
||||
.procname = "timer_migration",
|
||||
|
@@ -108,7 +108,7 @@ static int prepare_reply(struct genl_info *info, u8 cmd, struct sk_buff **skbp,
|
||||
/*
|
||||
* Send taskstats data in @skb to listener with nl_pid @pid
|
||||
*/
|
||||
static int send_reply(struct sk_buff *skb, pid_t pid)
|
||||
static int send_reply(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct genlmsghdr *genlhdr = nlmsg_data(nlmsg_hdr(skb));
|
||||
void *reply = genlmsg_data(genlhdr);
|
||||
@@ -120,7 +120,7 @@ static int send_reply(struct sk_buff *skb, pid_t pid)
|
||||
return rc;
|
||||
}
|
||||
|
||||
return genlmsg_unicast(skb, pid);
|
||||
return genlmsg_reply(skb, info);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -150,7 +150,7 @@ static void send_cpu_listeners(struct sk_buff *skb,
|
||||
if (!skb_next)
|
||||
break;
|
||||
}
|
||||
rc = genlmsg_unicast(skb_cur, s->pid);
|
||||
rc = genlmsg_unicast(&init_net, skb_cur, s->pid);
|
||||
if (rc == -ECONNREFUSED) {
|
||||
s->valid = 0;
|
||||
delcount++;
|
||||
@@ -418,7 +418,7 @@ static int cgroupstats_user_cmd(struct sk_buff *skb, struct genl_info *info)
|
||||
goto err;
|
||||
}
|
||||
|
||||
rc = send_reply(rep_skb, info->snd_pid);
|
||||
rc = send_reply(rep_skb, info);
|
||||
|
||||
err:
|
||||
fput_light(file, fput_needed);
|
||||
@@ -487,7 +487,7 @@ free_return_rc:
|
||||
} else
|
||||
goto err;
|
||||
|
||||
return send_reply(rep_skb, info->snd_pid);
|
||||
return send_reply(rep_skb, info);
|
||||
err:
|
||||
nlmsg_free(rep_skb);
|
||||
return rc;
|
||||
|
@@ -1156,8 +1156,7 @@ void update_process_times(int user_tick)
|
||||
/* Note: this timer irq context must be accounted for as well. */
|
||||
account_process_tick(p, user_tick);
|
||||
run_local_timers();
|
||||
if (rcu_pending(cpu))
|
||||
rcu_check_callbacks(cpu, user_tick);
|
||||
rcu_check_callbacks(cpu, user_tick);
|
||||
printk_tick();
|
||||
scheduler_tick();
|
||||
run_posix_cpu_timers(p);
|
||||
|
@@ -41,7 +41,7 @@ config HAVE_FTRACE_MCOUNT_RECORD
|
||||
config HAVE_HW_BRANCH_TRACER
|
||||
bool
|
||||
|
||||
config HAVE_FTRACE_SYSCALLS
|
||||
config HAVE_SYSCALL_TRACEPOINTS
|
||||
bool
|
||||
|
||||
config TRACER_MAX_TRACE
|
||||
@@ -60,9 +60,14 @@ config EVENT_TRACING
|
||||
bool
|
||||
|
||||
config CONTEXT_SWITCH_TRACER
|
||||
select MARKERS
|
||||
bool
|
||||
|
||||
config RING_BUFFER_ALLOW_SWAP
|
||||
bool
|
||||
help
|
||||
Allow the use of ring_buffer_swap_cpu.
|
||||
Adds a very slight overhead to tracing when enabled.
|
||||
|
||||
# All tracer options should select GENERIC_TRACER. For those options that are
|
||||
# enabled by all tracers (context switch and event tracer) they select TRACING.
|
||||
# This allows those options to appear when no other tracer is selected. But the
|
||||
@@ -147,6 +152,7 @@ config IRQSOFF_TRACER
|
||||
select TRACE_IRQFLAGS
|
||||
select GENERIC_TRACER
|
||||
select TRACER_MAX_TRACE
|
||||
select RING_BUFFER_ALLOW_SWAP
|
||||
help
|
||||
This option measures the time spent in irqs-off critical
|
||||
sections, with microsecond accuracy.
|
||||
@@ -168,6 +174,7 @@ config PREEMPT_TRACER
|
||||
depends on PREEMPT
|
||||
select GENERIC_TRACER
|
||||
select TRACER_MAX_TRACE
|
||||
select RING_BUFFER_ALLOW_SWAP
|
||||
help
|
||||
This option measures the time spent in preemption off critical
|
||||
sections, with microsecond accuracy.
|
||||
@@ -211,7 +218,7 @@ config ENABLE_DEFAULT_TRACERS
|
||||
|
||||
config FTRACE_SYSCALLS
|
||||
bool "Trace syscalls"
|
||||
depends on HAVE_FTRACE_SYSCALLS
|
||||
depends on HAVE_SYSCALL_TRACEPOINTS
|
||||
select GENERIC_TRACER
|
||||
select KALLSYMS
|
||||
help
|
||||
|
@@ -65,13 +65,15 @@ static void trace_note(struct blk_trace *bt, pid_t pid, int action,
|
||||
{
|
||||
struct blk_io_trace *t;
|
||||
struct ring_buffer_event *event = NULL;
|
||||
struct ring_buffer *buffer = NULL;
|
||||
int pc = 0;
|
||||
int cpu = smp_processor_id();
|
||||
bool blk_tracer = blk_tracer_enabled;
|
||||
|
||||
if (blk_tracer) {
|
||||
buffer = blk_tr->buffer;
|
||||
pc = preempt_count();
|
||||
event = trace_buffer_lock_reserve(blk_tr, TRACE_BLK,
|
||||
event = trace_buffer_lock_reserve(buffer, TRACE_BLK,
|
||||
sizeof(*t) + len,
|
||||
0, pc);
|
||||
if (!event)
|
||||
@@ -96,7 +98,7 @@ record_it:
|
||||
memcpy((void *) t + sizeof(*t), data, len);
|
||||
|
||||
if (blk_tracer)
|
||||
trace_buffer_unlock_commit(blk_tr, event, 0, pc);
|
||||
trace_buffer_unlock_commit(buffer, event, 0, pc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,6 +181,7 @@ static void __blk_add_trace(struct blk_trace *bt, sector_t sector, int bytes,
|
||||
{
|
||||
struct task_struct *tsk = current;
|
||||
struct ring_buffer_event *event = NULL;
|
||||
struct ring_buffer *buffer = NULL;
|
||||
struct blk_io_trace *t;
|
||||
unsigned long flags = 0;
|
||||
unsigned long *sequence;
|
||||
@@ -204,8 +207,9 @@ static void __blk_add_trace(struct blk_trace *bt, sector_t sector, int bytes,
|
||||
if (blk_tracer) {
|
||||
tracing_record_cmdline(current);
|
||||
|
||||
buffer = blk_tr->buffer;
|
||||
pc = preempt_count();
|
||||
event = trace_buffer_lock_reserve(blk_tr, TRACE_BLK,
|
||||
event = trace_buffer_lock_reserve(buffer, TRACE_BLK,
|
||||
sizeof(*t) + pdu_len,
|
||||
0, pc);
|
||||
if (!event)
|
||||
@@ -252,7 +256,7 @@ record_it:
|
||||
memcpy((void *) t + sizeof(*t), pdu_data, pdu_len);
|
||||
|
||||
if (blk_tracer) {
|
||||
trace_buffer_unlock_commit(blk_tr, event, 0, pc);
|
||||
trace_buffer_unlock_commit(buffer, event, 0, pc);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@@ -1016,71 +1016,35 @@ static int
|
||||
__ftrace_replace_code(struct dyn_ftrace *rec, int enable)
|
||||
{
|
||||
unsigned long ftrace_addr;
|
||||
unsigned long ip, fl;
|
||||
unsigned long flag = 0UL;
|
||||
|
||||
ftrace_addr = (unsigned long)FTRACE_ADDR;
|
||||
|
||||
ip = rec->ip;
|
||||
|
||||
/*
|
||||
* If this record is not to be traced and
|
||||
* it is not enabled then do nothing.
|
||||
* If this record is not to be traced or we want to disable it,
|
||||
* then disable it.
|
||||
*
|
||||
* If this record is not to be traced and
|
||||
* it is enabled then disable it.
|
||||
* If we want to enable it and filtering is off, then enable it.
|
||||
*
|
||||
* If we want to enable it and filtering is on, enable it only if
|
||||
* it's filtered
|
||||
*/
|
||||
if (rec->flags & FTRACE_FL_NOTRACE) {
|
||||
if (rec->flags & FTRACE_FL_ENABLED)
|
||||
rec->flags &= ~FTRACE_FL_ENABLED;
|
||||
else
|
||||
return 0;
|
||||
|
||||
} else if (ftrace_filtered && enable) {
|
||||
/*
|
||||
* Filtering is on:
|
||||
*/
|
||||
|
||||
fl = rec->flags & (FTRACE_FL_FILTER | FTRACE_FL_ENABLED);
|
||||
|
||||
/* Record is filtered and enabled, do nothing */
|
||||
if (fl == (FTRACE_FL_FILTER | FTRACE_FL_ENABLED))
|
||||
return 0;
|
||||
|
||||
/* Record is not filtered or enabled, do nothing */
|
||||
if (!fl)
|
||||
return 0;
|
||||
|
||||
/* Record is not filtered but enabled, disable it */
|
||||
if (fl == FTRACE_FL_ENABLED)
|
||||
rec->flags &= ~FTRACE_FL_ENABLED;
|
||||
else
|
||||
/* Otherwise record is filtered but not enabled, enable it */
|
||||
rec->flags |= FTRACE_FL_ENABLED;
|
||||
} else {
|
||||
/* Disable or not filtered */
|
||||
|
||||
if (enable) {
|
||||
/* if record is enabled, do nothing */
|
||||
if (rec->flags & FTRACE_FL_ENABLED)
|
||||
return 0;
|
||||
|
||||
rec->flags |= FTRACE_FL_ENABLED;
|
||||
|
||||
} else {
|
||||
|
||||
/* if record is not enabled, do nothing */
|
||||
if (!(rec->flags & FTRACE_FL_ENABLED))
|
||||
return 0;
|
||||
|
||||
rec->flags &= ~FTRACE_FL_ENABLED;
|
||||
}
|
||||
if (enable && !(rec->flags & FTRACE_FL_NOTRACE)) {
|
||||
if (!ftrace_filtered || (rec->flags & FTRACE_FL_FILTER))
|
||||
flag = FTRACE_FL_ENABLED;
|
||||
}
|
||||
|
||||
if (rec->flags & FTRACE_FL_ENABLED)
|
||||
/* If the state of this record hasn't changed, then do nothing */
|
||||
if ((rec->flags & FTRACE_FL_ENABLED) == flag)
|
||||
return 0;
|
||||
|
||||
if (flag) {
|
||||
rec->flags |= FTRACE_FL_ENABLED;
|
||||
return ftrace_make_call(rec, ftrace_addr);
|
||||
else
|
||||
return ftrace_make_nop(NULL, rec, ftrace_addr);
|
||||
}
|
||||
|
||||
rec->flags &= ~FTRACE_FL_ENABLED;
|
||||
return ftrace_make_nop(NULL, rec, ftrace_addr);
|
||||
}
|
||||
|
||||
static void ftrace_replace_code(int enable)
|
||||
@@ -1375,7 +1339,6 @@ struct ftrace_iterator {
|
||||
unsigned flags;
|
||||
unsigned char buffer[FTRACE_BUFF_MAX+1];
|
||||
unsigned buffer_idx;
|
||||
unsigned filtered;
|
||||
};
|
||||
|
||||
static void *
|
||||
@@ -1438,18 +1401,13 @@ static int t_hash_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct ftrace_func_probe *rec;
|
||||
struct hlist_node *hnd = v;
|
||||
char str[KSYM_SYMBOL_LEN];
|
||||
|
||||
rec = hlist_entry(hnd, struct ftrace_func_probe, node);
|
||||
|
||||
if (rec->ops->print)
|
||||
return rec->ops->print(m, rec->ip, rec->ops, rec->data);
|
||||
|
||||
kallsyms_lookup(rec->ip, NULL, NULL, NULL, str);
|
||||
seq_printf(m, "%s:", str);
|
||||
|
||||
kallsyms_lookup((unsigned long)rec->ops->func, NULL, NULL, NULL, str);
|
||||
seq_printf(m, "%s", str);
|
||||
seq_printf(m, "%pf:%pf", (void *)rec->ip, (void *)rec->ops->func);
|
||||
|
||||
if (rec->data)
|
||||
seq_printf(m, ":%p", rec->data);
|
||||
@@ -1547,7 +1505,6 @@ static int t_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct ftrace_iterator *iter = m->private;
|
||||
struct dyn_ftrace *rec = v;
|
||||
char str[KSYM_SYMBOL_LEN];
|
||||
|
||||
if (iter->flags & FTRACE_ITER_HASH)
|
||||
return t_hash_show(m, v);
|
||||
@@ -1560,9 +1517,7 @@ static int t_show(struct seq_file *m, void *v)
|
||||
if (!rec)
|
||||
return 0;
|
||||
|
||||
kallsyms_lookup(rec->ip, NULL, NULL, NULL, str);
|
||||
|
||||
seq_printf(m, "%s\n", str);
|
||||
seq_printf(m, "%pf\n", (void *)rec->ip);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1601,17 +1556,6 @@ ftrace_avail_open(struct inode *inode, struct file *file)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ftrace_avail_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct seq_file *m = (struct seq_file *)file->private_data;
|
||||
struct ftrace_iterator *iter = m->private;
|
||||
|
||||
seq_release(inode, file);
|
||||
kfree(iter);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ftrace_failures_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
@@ -2317,7 +2261,6 @@ ftrace_regex_write(struct file *file, const char __user *ubuf,
|
||||
}
|
||||
|
||||
if (isspace(ch)) {
|
||||
iter->filtered++;
|
||||
iter->buffer[iter->buffer_idx] = 0;
|
||||
ret = ftrace_process_regex(iter->buffer,
|
||||
iter->buffer_idx, enable);
|
||||
@@ -2448,7 +2391,6 @@ ftrace_regex_release(struct inode *inode, struct file *file, int enable)
|
||||
iter = file->private_data;
|
||||
|
||||
if (iter->buffer_idx) {
|
||||
iter->filtered++;
|
||||
iter->buffer[iter->buffer_idx] = 0;
|
||||
ftrace_match_records(iter->buffer, iter->buffer_idx, enable);
|
||||
}
|
||||
@@ -2479,14 +2421,14 @@ static const struct file_operations ftrace_avail_fops = {
|
||||
.open = ftrace_avail_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = ftrace_avail_release,
|
||||
.release = seq_release_private,
|
||||
};
|
||||
|
||||
static const struct file_operations ftrace_failures_fops = {
|
||||
.open = ftrace_failures_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = ftrace_avail_release,
|
||||
.release = seq_release_private,
|
||||
};
|
||||
|
||||
static const struct file_operations ftrace_filter_fops = {
|
||||
@@ -2548,7 +2490,6 @@ static void g_stop(struct seq_file *m, void *p)
|
||||
static int g_show(struct seq_file *m, void *v)
|
||||
{
|
||||
unsigned long *ptr = v;
|
||||
char str[KSYM_SYMBOL_LEN];
|
||||
|
||||
if (!ptr)
|
||||
return 0;
|
||||
@@ -2558,9 +2499,7 @@ static int g_show(struct seq_file *m, void *v)
|
||||
return 0;
|
||||
}
|
||||
|
||||
kallsyms_lookup(*ptr, NULL, NULL, NULL, str);
|
||||
|
||||
seq_printf(m, "%s\n", str);
|
||||
seq_printf(m, "%pf\n", v);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@@ -183,11 +183,9 @@ static void kmemtrace_stop_probes(void)
|
||||
|
||||
static int kmem_trace_init(struct trace_array *tr)
|
||||
{
|
||||
int cpu;
|
||||
kmemtrace_array = tr;
|
||||
|
||||
for_each_cpu(cpu, cpu_possible_mask)
|
||||
tracing_reset(tr, cpu);
|
||||
tracing_reset_online_cpus(tr);
|
||||
|
||||
kmemtrace_start_probes();
|
||||
|
||||
@@ -239,12 +237,52 @@ struct kmemtrace_user_event_alloc {
|
||||
};
|
||||
|
||||
static enum print_line_t
|
||||
kmemtrace_print_alloc_user(struct trace_iterator *iter,
|
||||
struct kmemtrace_alloc_entry *entry)
|
||||
kmemtrace_print_alloc(struct trace_iterator *iter, int flags)
|
||||
{
|
||||
struct kmemtrace_user_event_alloc *ev_alloc;
|
||||
struct trace_seq *s = &iter->seq;
|
||||
struct kmemtrace_alloc_entry *entry;
|
||||
int ret;
|
||||
|
||||
trace_assign_type(entry, iter->ent);
|
||||
|
||||
ret = trace_seq_printf(s, "type_id %d call_site %pF ptr %lu "
|
||||
"bytes_req %lu bytes_alloc %lu gfp_flags %lu node %d\n",
|
||||
entry->type_id, (void *)entry->call_site, (unsigned long)entry->ptr,
|
||||
(unsigned long)entry->bytes_req, (unsigned long)entry->bytes_alloc,
|
||||
(unsigned long)entry->gfp_flags, entry->node);
|
||||
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
return TRACE_TYPE_HANDLED;
|
||||
}
|
||||
|
||||
static enum print_line_t
|
||||
kmemtrace_print_free(struct trace_iterator *iter, int flags)
|
||||
{
|
||||
struct trace_seq *s = &iter->seq;
|
||||
struct kmemtrace_free_entry *entry;
|
||||
int ret;
|
||||
|
||||
trace_assign_type(entry, iter->ent);
|
||||
|
||||
ret = trace_seq_printf(s, "type_id %d call_site %pF ptr %lu\n",
|
||||
entry->type_id, (void *)entry->call_site,
|
||||
(unsigned long)entry->ptr);
|
||||
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
return TRACE_TYPE_HANDLED;
|
||||
}
|
||||
|
||||
static enum print_line_t
|
||||
kmemtrace_print_alloc_user(struct trace_iterator *iter, int flags)
|
||||
{
|
||||
struct trace_seq *s = &iter->seq;
|
||||
struct kmemtrace_alloc_entry *entry;
|
||||
struct kmemtrace_user_event *ev;
|
||||
struct kmemtrace_user_event_alloc *ev_alloc;
|
||||
|
||||
trace_assign_type(entry, iter->ent);
|
||||
|
||||
ev = trace_seq_reserve(s, sizeof(*ev));
|
||||
if (!ev)
|
||||
@@ -271,12 +309,14 @@ kmemtrace_print_alloc_user(struct trace_iterator *iter,
|
||||
}
|
||||
|
||||
static enum print_line_t
|
||||
kmemtrace_print_free_user(struct trace_iterator *iter,
|
||||
struct kmemtrace_free_entry *entry)
|
||||
kmemtrace_print_free_user(struct trace_iterator *iter, int flags)
|
||||
{
|
||||
struct trace_seq *s = &iter->seq;
|
||||
struct kmemtrace_free_entry *entry;
|
||||
struct kmemtrace_user_event *ev;
|
||||
|
||||
trace_assign_type(entry, iter->ent);
|
||||
|
||||
ev = trace_seq_reserve(s, sizeof(*ev));
|
||||
if (!ev)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
@@ -294,12 +334,14 @@ kmemtrace_print_free_user(struct trace_iterator *iter,
|
||||
|
||||
/* The two other following provide a more minimalistic output */
|
||||
static enum print_line_t
|
||||
kmemtrace_print_alloc_compress(struct trace_iterator *iter,
|
||||
struct kmemtrace_alloc_entry *entry)
|
||||
kmemtrace_print_alloc_compress(struct trace_iterator *iter)
|
||||
{
|
||||
struct kmemtrace_alloc_entry *entry;
|
||||
struct trace_seq *s = &iter->seq;
|
||||
int ret;
|
||||
|
||||
trace_assign_type(entry, iter->ent);
|
||||
|
||||
/* Alloc entry */
|
||||
ret = trace_seq_printf(s, " + ");
|
||||
if (!ret)
|
||||
@@ -345,29 +387,24 @@ kmemtrace_print_alloc_compress(struct trace_iterator *iter,
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
/* Node */
|
||||
ret = trace_seq_printf(s, "%4d ", entry->node);
|
||||
/* Node and call site*/
|
||||
ret = trace_seq_printf(s, "%4d %pf\n", entry->node,
|
||||
(void *)entry->call_site);
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
/* Call site */
|
||||
ret = seq_print_ip_sym(s, entry->call_site, 0);
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
if (!trace_seq_printf(s, "\n"))
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
return TRACE_TYPE_HANDLED;
|
||||
}
|
||||
|
||||
static enum print_line_t
|
||||
kmemtrace_print_free_compress(struct trace_iterator *iter,
|
||||
struct kmemtrace_free_entry *entry)
|
||||
kmemtrace_print_free_compress(struct trace_iterator *iter)
|
||||
{
|
||||
struct kmemtrace_free_entry *entry;
|
||||
struct trace_seq *s = &iter->seq;
|
||||
int ret;
|
||||
|
||||
trace_assign_type(entry, iter->ent);
|
||||
|
||||
/* Free entry */
|
||||
ret = trace_seq_printf(s, " - ");
|
||||
if (!ret)
|
||||
@@ -401,19 +438,11 @@ kmemtrace_print_free_compress(struct trace_iterator *iter,
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
/* Skip node */
|
||||
ret = trace_seq_printf(s, " ");
|
||||
/* Skip node and print call site*/
|
||||
ret = trace_seq_printf(s, " %pf\n", (void *)entry->call_site);
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
/* Call site */
|
||||
ret = seq_print_ip_sym(s, entry->call_site, 0);
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
if (!trace_seq_printf(s, "\n"))
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
return TRACE_TYPE_HANDLED;
|
||||
}
|
||||
|
||||
@@ -421,32 +450,31 @@ static enum print_line_t kmemtrace_print_line(struct trace_iterator *iter)
|
||||
{
|
||||
struct trace_entry *entry = iter->ent;
|
||||
|
||||
if (!(kmem_tracer_flags.val & TRACE_KMEM_OPT_MINIMAL))
|
||||
return TRACE_TYPE_UNHANDLED;
|
||||
|
||||
switch (entry->type) {
|
||||
case TRACE_KMEM_ALLOC: {
|
||||
struct kmemtrace_alloc_entry *field;
|
||||
|
||||
trace_assign_type(field, entry);
|
||||
if (kmem_tracer_flags.val & TRACE_KMEM_OPT_MINIMAL)
|
||||
return kmemtrace_print_alloc_compress(iter, field);
|
||||
else
|
||||
return kmemtrace_print_alloc_user(iter, field);
|
||||
}
|
||||
|
||||
case TRACE_KMEM_FREE: {
|
||||
struct kmemtrace_free_entry *field;
|
||||
|
||||
trace_assign_type(field, entry);
|
||||
if (kmem_tracer_flags.val & TRACE_KMEM_OPT_MINIMAL)
|
||||
return kmemtrace_print_free_compress(iter, field);
|
||||
else
|
||||
return kmemtrace_print_free_user(iter, field);
|
||||
}
|
||||
|
||||
case TRACE_KMEM_ALLOC:
|
||||
return kmemtrace_print_alloc_compress(iter);
|
||||
case TRACE_KMEM_FREE:
|
||||
return kmemtrace_print_free_compress(iter);
|
||||
default:
|
||||
return TRACE_TYPE_UNHANDLED;
|
||||
}
|
||||
}
|
||||
|
||||
static struct trace_event kmem_trace_alloc = {
|
||||
.type = TRACE_KMEM_ALLOC,
|
||||
.trace = kmemtrace_print_alloc,
|
||||
.binary = kmemtrace_print_alloc_user,
|
||||
};
|
||||
|
||||
static struct trace_event kmem_trace_free = {
|
||||
.type = TRACE_KMEM_FREE,
|
||||
.trace = kmemtrace_print_free,
|
||||
.binary = kmemtrace_print_free_user,
|
||||
};
|
||||
|
||||
static struct tracer kmem_tracer __read_mostly = {
|
||||
.name = "kmemtrace",
|
||||
.init = kmem_trace_init,
|
||||
@@ -463,6 +491,21 @@ void kmemtrace_init(void)
|
||||
|
||||
static int __init init_kmem_tracer(void)
|
||||
{
|
||||
return register_tracer(&kmem_tracer);
|
||||
if (!register_ftrace_event(&kmem_trace_alloc)) {
|
||||
pr_warning("Warning: could not register kmem events\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!register_ftrace_event(&kmem_trace_free)) {
|
||||
pr_warning("Warning: could not register kmem events\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!register_tracer(&kmem_tracer)) {
|
||||
pr_warning("Warning: could not register the kmem tracer\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
device_initcall(init_kmem_tracer);
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -34,8 +34,6 @@ enum trace_type {
|
||||
TRACE_GRAPH_ENT,
|
||||
TRACE_USER_STACK,
|
||||
TRACE_HW_BRANCHES,
|
||||
TRACE_SYSCALL_ENTER,
|
||||
TRACE_SYSCALL_EXIT,
|
||||
TRACE_KMEM_ALLOC,
|
||||
TRACE_KMEM_FREE,
|
||||
TRACE_POWER,
|
||||
@@ -236,9 +234,6 @@ struct trace_array_cpu {
|
||||
atomic_t disabled;
|
||||
void *buffer_page; /* ring buffer spare */
|
||||
|
||||
/* these fields get copied into max-trace: */
|
||||
unsigned long trace_idx;
|
||||
unsigned long overrun;
|
||||
unsigned long saved_latency;
|
||||
unsigned long critical_start;
|
||||
unsigned long critical_end;
|
||||
@@ -246,6 +241,7 @@ struct trace_array_cpu {
|
||||
unsigned long nice;
|
||||
unsigned long policy;
|
||||
unsigned long rt_priority;
|
||||
unsigned long skipped_entries;
|
||||
cycle_t preempt_timestamp;
|
||||
pid_t pid;
|
||||
uid_t uid;
|
||||
@@ -319,10 +315,6 @@ extern void __ftrace_bad_type(void);
|
||||
TRACE_KMEM_ALLOC); \
|
||||
IF_ASSIGN(var, ent, struct kmemtrace_free_entry, \
|
||||
TRACE_KMEM_FREE); \
|
||||
IF_ASSIGN(var, ent, struct syscall_trace_enter, \
|
||||
TRACE_SYSCALL_ENTER); \
|
||||
IF_ASSIGN(var, ent, struct syscall_trace_exit, \
|
||||
TRACE_SYSCALL_EXIT); \
|
||||
__ftrace_bad_type(); \
|
||||
} while (0)
|
||||
|
||||
@@ -423,12 +415,13 @@ void init_tracer_sysprof_debugfs(struct dentry *d_tracer);
|
||||
|
||||
struct ring_buffer_event;
|
||||
|
||||
struct ring_buffer_event *trace_buffer_lock_reserve(struct trace_array *tr,
|
||||
int type,
|
||||
unsigned long len,
|
||||
unsigned long flags,
|
||||
int pc);
|
||||
void trace_buffer_unlock_commit(struct trace_array *tr,
|
||||
struct ring_buffer_event *
|
||||
trace_buffer_lock_reserve(struct ring_buffer *buffer,
|
||||
int type,
|
||||
unsigned long len,
|
||||
unsigned long flags,
|
||||
int pc);
|
||||
void trace_buffer_unlock_commit(struct ring_buffer *buffer,
|
||||
struct ring_buffer_event *event,
|
||||
unsigned long flags, int pc);
|
||||
|
||||
@@ -467,6 +460,7 @@ void trace_function(struct trace_array *tr,
|
||||
|
||||
void trace_graph_return(struct ftrace_graph_ret *trace);
|
||||
int trace_graph_entry(struct ftrace_graph_ent *trace);
|
||||
void set_graph_array(struct trace_array *tr);
|
||||
|
||||
void tracing_start_cmdline_record(void);
|
||||
void tracing_stop_cmdline_record(void);
|
||||
@@ -478,16 +472,40 @@ void unregister_tracer(struct tracer *type);
|
||||
|
||||
extern unsigned long nsecs_to_usecs(unsigned long nsecs);
|
||||
|
||||
#ifdef CONFIG_TRACER_MAX_TRACE
|
||||
extern unsigned long tracing_max_latency;
|
||||
extern unsigned long tracing_thresh;
|
||||
|
||||
void update_max_tr(struct trace_array *tr, struct task_struct *tsk, int cpu);
|
||||
void update_max_tr_single(struct trace_array *tr,
|
||||
struct task_struct *tsk, int cpu);
|
||||
#endif /* CONFIG_TRACER_MAX_TRACE */
|
||||
|
||||
void __trace_stack(struct trace_array *tr,
|
||||
unsigned long flags,
|
||||
int skip, int pc);
|
||||
#ifdef CONFIG_STACKTRACE
|
||||
void ftrace_trace_stack(struct ring_buffer *buffer, unsigned long flags,
|
||||
int skip, int pc);
|
||||
|
||||
void ftrace_trace_userstack(struct ring_buffer *buffer, unsigned long flags,
|
||||
int pc);
|
||||
|
||||
void __trace_stack(struct trace_array *tr, unsigned long flags, int skip,
|
||||
int pc);
|
||||
#else
|
||||
static inline void ftrace_trace_stack(struct trace_array *tr,
|
||||
unsigned long flags, int skip, int pc)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void ftrace_trace_userstack(struct trace_array *tr,
|
||||
unsigned long flags, int pc)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void __trace_stack(struct trace_array *tr, unsigned long flags,
|
||||
int skip, int pc)
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_STACKTRACE */
|
||||
|
||||
extern cycle_t ftrace_now(int cpu);
|
||||
|
||||
@@ -513,6 +531,10 @@ extern unsigned long ftrace_update_tot_cnt;
|
||||
extern int DYN_FTRACE_TEST_NAME(void);
|
||||
#endif
|
||||
|
||||
extern int ring_buffer_expanded;
|
||||
extern bool tracing_selftest_disabled;
|
||||
DECLARE_PER_CPU(local_t, ftrace_cpu_disabled);
|
||||
|
||||
#ifdef CONFIG_FTRACE_STARTUP_TEST
|
||||
extern int trace_selftest_startup_function(struct tracer *trace,
|
||||
struct trace_array *tr);
|
||||
@@ -544,9 +566,16 @@ extern int
|
||||
trace_vbprintk(unsigned long ip, const char *fmt, va_list args);
|
||||
extern int
|
||||
trace_vprintk(unsigned long ip, const char *fmt, va_list args);
|
||||
extern int
|
||||
trace_array_vprintk(struct trace_array *tr,
|
||||
unsigned long ip, const char *fmt, va_list args);
|
||||
int trace_array_printk(struct trace_array *tr,
|
||||
unsigned long ip, const char *fmt, ...);
|
||||
|
||||
extern unsigned long trace_flags;
|
||||
|
||||
extern int trace_clock_id;
|
||||
|
||||
/* Standard output formatting function used for function return traces */
|
||||
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
|
||||
extern enum print_line_t print_graph_function(struct trace_iterator *iter);
|
||||
@@ -635,9 +664,8 @@ enum trace_iterator_flags {
|
||||
TRACE_ITER_PRINTK_MSGONLY = 0x10000,
|
||||
TRACE_ITER_CONTEXT_INFO = 0x20000, /* Print pid/cpu/time */
|
||||
TRACE_ITER_LATENCY_FMT = 0x40000,
|
||||
TRACE_ITER_GLOBAL_CLK = 0x80000,
|
||||
TRACE_ITER_SLEEP_TIME = 0x100000,
|
||||
TRACE_ITER_GRAPH_TIME = 0x200000,
|
||||
TRACE_ITER_SLEEP_TIME = 0x80000,
|
||||
TRACE_ITER_GRAPH_TIME = 0x100000,
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -734,6 +762,7 @@ struct ftrace_event_field {
|
||||
struct list_head link;
|
||||
char *name;
|
||||
char *type;
|
||||
int filter_type;
|
||||
int offset;
|
||||
int size;
|
||||
int is_signed;
|
||||
@@ -743,13 +772,15 @@ struct event_filter {
|
||||
int n_preds;
|
||||
struct filter_pred **preds;
|
||||
char *filter_string;
|
||||
bool no_reset;
|
||||
};
|
||||
|
||||
struct event_subsystem {
|
||||
struct list_head list;
|
||||
const char *name;
|
||||
struct dentry *entry;
|
||||
void *filter;
|
||||
struct event_filter *filter;
|
||||
int nr_events;
|
||||
};
|
||||
|
||||
struct filter_pred;
|
||||
@@ -777,6 +808,7 @@ extern int apply_subsystem_event_filter(struct event_subsystem *system,
|
||||
char *filter_string);
|
||||
extern void print_subsystem_event_filter(struct event_subsystem *system,
|
||||
struct trace_seq *s);
|
||||
extern int filter_assign_type(const char *type);
|
||||
|
||||
static inline int
|
||||
filter_check_discard(struct ftrace_event_call *call, void *rec,
|
||||
|
@@ -41,14 +41,12 @@ void disable_boot_trace(void)
|
||||
|
||||
static int boot_trace_init(struct trace_array *tr)
|
||||
{
|
||||
int cpu;
|
||||
boot_trace = tr;
|
||||
|
||||
if (!tr)
|
||||
return 0;
|
||||
|
||||
for_each_cpu(cpu, cpu_possible_mask)
|
||||
tracing_reset(tr, cpu);
|
||||
tracing_reset_online_cpus(tr);
|
||||
|
||||
tracing_sched_switch_assign_trace(tr);
|
||||
return 0;
|
||||
@@ -132,6 +130,7 @@ struct tracer boot_tracer __read_mostly =
|
||||
void trace_boot_call(struct boot_trace_call *bt, initcall_t fn)
|
||||
{
|
||||
struct ring_buffer_event *event;
|
||||
struct ring_buffer *buffer;
|
||||
struct trace_boot_call *entry;
|
||||
struct trace_array *tr = boot_trace;
|
||||
|
||||
@@ -144,13 +143,14 @@ void trace_boot_call(struct boot_trace_call *bt, initcall_t fn)
|
||||
sprint_symbol(bt->func, (unsigned long)fn);
|
||||
preempt_disable();
|
||||
|
||||
event = trace_buffer_lock_reserve(tr, TRACE_BOOT_CALL,
|
||||
buffer = tr->buffer;
|
||||
event = trace_buffer_lock_reserve(buffer, TRACE_BOOT_CALL,
|
||||
sizeof(*entry), 0, 0);
|
||||
if (!event)
|
||||
goto out;
|
||||
entry = ring_buffer_event_data(event);
|
||||
entry->boot_call = *bt;
|
||||
trace_buffer_unlock_commit(tr, event, 0, 0);
|
||||
trace_buffer_unlock_commit(buffer, event, 0, 0);
|
||||
out:
|
||||
preempt_enable();
|
||||
}
|
||||
@@ -158,6 +158,7 @@ void trace_boot_call(struct boot_trace_call *bt, initcall_t fn)
|
||||
void trace_boot_ret(struct boot_trace_ret *bt, initcall_t fn)
|
||||
{
|
||||
struct ring_buffer_event *event;
|
||||
struct ring_buffer *buffer;
|
||||
struct trace_boot_ret *entry;
|
||||
struct trace_array *tr = boot_trace;
|
||||
|
||||
@@ -167,13 +168,14 @@ void trace_boot_ret(struct boot_trace_ret *bt, initcall_t fn)
|
||||
sprint_symbol(bt->func, (unsigned long)fn);
|
||||
preempt_disable();
|
||||
|
||||
event = trace_buffer_lock_reserve(tr, TRACE_BOOT_RET,
|
||||
buffer = tr->buffer;
|
||||
event = trace_buffer_lock_reserve(buffer, TRACE_BOOT_RET,
|
||||
sizeof(*entry), 0, 0);
|
||||
if (!event)
|
||||
goto out;
|
||||
entry = ring_buffer_event_data(event);
|
||||
entry->boot_ret = *bt;
|
||||
trace_buffer_unlock_commit(tr, event, 0, 0);
|
||||
trace_buffer_unlock_commit(buffer, event, 0, 0);
|
||||
out:
|
||||
preempt_enable();
|
||||
}
|
||||
|
@@ -17,6 +17,8 @@
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <asm/setup.h>
|
||||
|
||||
#include "trace_output.h"
|
||||
|
||||
#define TRACE_SYSTEM "TRACE_SYSTEM"
|
||||
@@ -25,8 +27,9 @@ DEFINE_MUTEX(event_mutex);
|
||||
|
||||
LIST_HEAD(ftrace_events);
|
||||
|
||||
int trace_define_field(struct ftrace_event_call *call, char *type,
|
||||
char *name, int offset, int size, int is_signed)
|
||||
int trace_define_field(struct ftrace_event_call *call, const char *type,
|
||||
const char *name, int offset, int size, int is_signed,
|
||||
int filter_type)
|
||||
{
|
||||
struct ftrace_event_field *field;
|
||||
|
||||
@@ -42,9 +45,15 @@ int trace_define_field(struct ftrace_event_call *call, char *type,
|
||||
if (!field->type)
|
||||
goto err;
|
||||
|
||||
if (filter_type == FILTER_OTHER)
|
||||
field->filter_type = filter_assign_type(type);
|
||||
else
|
||||
field->filter_type = filter_type;
|
||||
|
||||
field->offset = offset;
|
||||
field->size = size;
|
||||
field->is_signed = is_signed;
|
||||
|
||||
list_add(&field->link, &call->fields);
|
||||
|
||||
return 0;
|
||||
@@ -60,6 +69,29 @@ err:
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(trace_define_field);
|
||||
|
||||
#define __common_field(type, item) \
|
||||
ret = trace_define_field(call, #type, "common_" #item, \
|
||||
offsetof(typeof(ent), item), \
|
||||
sizeof(ent.item), \
|
||||
is_signed_type(type), FILTER_OTHER); \
|
||||
if (ret) \
|
||||
return ret;
|
||||
|
||||
int trace_define_common_fields(struct ftrace_event_call *call)
|
||||
{
|
||||
int ret;
|
||||
struct trace_entry ent;
|
||||
|
||||
__common_field(unsigned short, type);
|
||||
__common_field(unsigned char, flags);
|
||||
__common_field(unsigned char, preempt_count);
|
||||
__common_field(int, pid);
|
||||
__common_field(int, tgid);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(trace_define_common_fields);
|
||||
|
||||
#ifdef CONFIG_MODULES
|
||||
|
||||
static void trace_destroy_fields(struct ftrace_event_call *call)
|
||||
@@ -84,14 +116,14 @@ static void ftrace_event_enable_disable(struct ftrace_event_call *call,
|
||||
if (call->enabled) {
|
||||
call->enabled = 0;
|
||||
tracing_stop_cmdline_record();
|
||||
call->unregfunc();
|
||||
call->unregfunc(call->data);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if (!call->enabled) {
|
||||
call->enabled = 1;
|
||||
tracing_start_cmdline_record();
|
||||
call->regfunc();
|
||||
call->regfunc(call->data);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -574,7 +606,7 @@ event_format_read(struct file *filp, char __user *ubuf, size_t cnt,
|
||||
trace_seq_printf(s, "format:\n");
|
||||
trace_write_header(s);
|
||||
|
||||
r = call->show_format(s);
|
||||
r = call->show_format(call, s);
|
||||
if (!r) {
|
||||
/*
|
||||
* ug! The format output is bigger than a PAGE!!
|
||||
@@ -849,8 +881,10 @@ event_subsystem_dir(const char *name, struct dentry *d_events)
|
||||
|
||||
/* First see if we did not already create this dir */
|
||||
list_for_each_entry(system, &event_subsystems, list) {
|
||||
if (strcmp(system->name, name) == 0)
|
||||
if (strcmp(system->name, name) == 0) {
|
||||
system->nr_events++;
|
||||
return system->entry;
|
||||
}
|
||||
}
|
||||
|
||||
/* need to create new entry */
|
||||
@@ -869,6 +903,7 @@ event_subsystem_dir(const char *name, struct dentry *d_events)
|
||||
return d_events;
|
||||
}
|
||||
|
||||
system->nr_events = 1;
|
||||
system->name = kstrdup(name, GFP_KERNEL);
|
||||
if (!system->name) {
|
||||
debugfs_remove(system->entry);
|
||||
@@ -920,15 +955,6 @@ event_create_dir(struct ftrace_event_call *call, struct dentry *d_events,
|
||||
if (strcmp(call->system, TRACE_SYSTEM) != 0)
|
||||
d_events = event_subsystem_dir(call->system, d_events);
|
||||
|
||||
if (call->raw_init) {
|
||||
ret = call->raw_init();
|
||||
if (ret < 0) {
|
||||
pr_warning("Could not initialize trace point"
|
||||
" events/%s\n", call->name);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
call->dir = debugfs_create_dir(call->name, d_events);
|
||||
if (!call->dir) {
|
||||
pr_warning("Could not create debugfs "
|
||||
@@ -945,7 +971,7 @@ event_create_dir(struct ftrace_event_call *call, struct dentry *d_events,
|
||||
id);
|
||||
|
||||
if (call->define_fields) {
|
||||
ret = call->define_fields();
|
||||
ret = call->define_fields(call);
|
||||
if (ret < 0) {
|
||||
pr_warning("Could not initialize trace point"
|
||||
" events/%s\n", call->name);
|
||||
@@ -987,6 +1013,32 @@ struct ftrace_module_file_ops {
|
||||
struct file_operations filter;
|
||||
};
|
||||
|
||||
static void remove_subsystem_dir(const char *name)
|
||||
{
|
||||
struct event_subsystem *system;
|
||||
|
||||
if (strcmp(name, TRACE_SYSTEM) == 0)
|
||||
return;
|
||||
|
||||
list_for_each_entry(system, &event_subsystems, list) {
|
||||
if (strcmp(system->name, name) == 0) {
|
||||
if (!--system->nr_events) {
|
||||
struct event_filter *filter = system->filter;
|
||||
|
||||
debugfs_remove_recursive(system->entry);
|
||||
list_del(&system->list);
|
||||
if (filter) {
|
||||
kfree(filter->filter_string);
|
||||
kfree(filter);
|
||||
}
|
||||
kfree(system->name);
|
||||
kfree(system);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct ftrace_module_file_ops *
|
||||
trace_create_file_ops(struct module *mod)
|
||||
{
|
||||
@@ -1027,6 +1079,7 @@ static void trace_module_add_events(struct module *mod)
|
||||
struct ftrace_module_file_ops *file_ops = NULL;
|
||||
struct ftrace_event_call *call, *start, *end;
|
||||
struct dentry *d_events;
|
||||
int ret;
|
||||
|
||||
start = mod->trace_events;
|
||||
end = mod->trace_events + mod->num_trace_events;
|
||||
@@ -1042,7 +1095,15 @@ static void trace_module_add_events(struct module *mod)
|
||||
/* The linker may leave blanks */
|
||||
if (!call->name)
|
||||
continue;
|
||||
|
||||
if (call->raw_init) {
|
||||
ret = call->raw_init();
|
||||
if (ret < 0) {
|
||||
if (ret != -ENOSYS)
|
||||
pr_warning("Could not initialize trace "
|
||||
"point events/%s\n", call->name);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* This module has events, create file ops for this module
|
||||
* if not already done.
|
||||
@@ -1077,6 +1138,7 @@ static void trace_module_remove_events(struct module *mod)
|
||||
list_del(&call->list);
|
||||
trace_destroy_fields(call);
|
||||
destroy_preds(call);
|
||||
remove_subsystem_dir(call->system);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1133,6 +1195,18 @@ struct notifier_block trace_module_nb = {
|
||||
extern struct ftrace_event_call __start_ftrace_events[];
|
||||
extern struct ftrace_event_call __stop_ftrace_events[];
|
||||
|
||||
static char bootup_event_buf[COMMAND_LINE_SIZE] __initdata;
|
||||
|
||||
static __init int setup_trace_event(char *str)
|
||||
{
|
||||
strlcpy(bootup_event_buf, str, COMMAND_LINE_SIZE);
|
||||
ring_buffer_expanded = 1;
|
||||
tracing_selftest_disabled = 1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
__setup("trace_event=", setup_trace_event);
|
||||
|
||||
static __init int event_trace_init(void)
|
||||
{
|
||||
struct ftrace_event_call *call;
|
||||
@@ -1140,6 +1214,8 @@ static __init int event_trace_init(void)
|
||||
struct dentry *entry;
|
||||
struct dentry *d_events;
|
||||
int ret;
|
||||
char *buf = bootup_event_buf;
|
||||
char *token;
|
||||
|
||||
d_tracer = tracing_init_dentry();
|
||||
if (!d_tracer)
|
||||
@@ -1179,12 +1255,34 @@ static __init int event_trace_init(void)
|
||||
/* The linker may leave blanks */
|
||||
if (!call->name)
|
||||
continue;
|
||||
if (call->raw_init) {
|
||||
ret = call->raw_init();
|
||||
if (ret < 0) {
|
||||
if (ret != -ENOSYS)
|
||||
pr_warning("Could not initialize trace "
|
||||
"point events/%s\n", call->name);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
list_add(&call->list, &ftrace_events);
|
||||
event_create_dir(call, d_events, &ftrace_event_id_fops,
|
||||
&ftrace_enable_fops, &ftrace_event_filter_fops,
|
||||
&ftrace_event_format_fops);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
token = strsep(&buf, ",");
|
||||
|
||||
if (!token)
|
||||
break;
|
||||
if (!*token)
|
||||
continue;
|
||||
|
||||
ret = ftrace_set_clr_event(token, 1);
|
||||
if (ret)
|
||||
pr_warning("Failed to enable trace event: %s\n", token);
|
||||
}
|
||||
|
||||
ret = register_module_notifier(&trace_module_nb);
|
||||
if (ret)
|
||||
pr_warning("Failed to register trace events module notifier\n");
|
||||
@@ -1340,6 +1438,7 @@ static void
|
||||
function_test_events_call(unsigned long ip, unsigned long parent_ip)
|
||||
{
|
||||
struct ring_buffer_event *event;
|
||||
struct ring_buffer *buffer;
|
||||
struct ftrace_entry *entry;
|
||||
unsigned long flags;
|
||||
long disabled;
|
||||
@@ -1357,7 +1456,8 @@ function_test_events_call(unsigned long ip, unsigned long parent_ip)
|
||||
|
||||
local_save_flags(flags);
|
||||
|
||||
event = trace_current_buffer_lock_reserve(TRACE_FN, sizeof(*entry),
|
||||
event = trace_current_buffer_lock_reserve(&buffer,
|
||||
TRACE_FN, sizeof(*entry),
|
||||
flags, pc);
|
||||
if (!event)
|
||||
goto out;
|
||||
@@ -1365,7 +1465,7 @@ function_test_events_call(unsigned long ip, unsigned long parent_ip)
|
||||
entry->ip = ip;
|
||||
entry->parent_ip = parent_ip;
|
||||
|
||||
trace_nowake_buffer_unlock_commit(event, flags, pc);
|
||||
trace_nowake_buffer_unlock_commit(buffer, event, flags, pc);
|
||||
|
||||
out:
|
||||
atomic_dec(&per_cpu(test_event_disable, cpu));
|
||||
@@ -1392,10 +1492,10 @@ static __init void event_trace_self_test_with_function(void)
|
||||
|
||||
static __init int event_trace_self_tests_init(void)
|
||||
{
|
||||
|
||||
event_trace_self_tests();
|
||||
|
||||
event_trace_self_test_with_function();
|
||||
if (!tracing_selftest_disabled) {
|
||||
event_trace_self_tests();
|
||||
event_trace_self_test_with_function();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@@ -163,6 +163,20 @@ static int filter_pred_string(struct filter_pred *pred, void *event,
|
||||
return match;
|
||||
}
|
||||
|
||||
/* Filter predicate for char * pointers */
|
||||
static int filter_pred_pchar(struct filter_pred *pred, void *event,
|
||||
int val1, int val2)
|
||||
{
|
||||
char **addr = (char **)(event + pred->offset);
|
||||
int cmp, match;
|
||||
|
||||
cmp = strncmp(*addr, pred->str_val, pred->str_len);
|
||||
|
||||
match = (!cmp) ^ pred->not;
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
/*
|
||||
* Filter predicate for dynamic sized arrays of characters.
|
||||
* These are implemented through a list of strings at the end
|
||||
@@ -176,11 +190,13 @@ static int filter_pred_string(struct filter_pred *pred, void *event,
|
||||
static int filter_pred_strloc(struct filter_pred *pred, void *event,
|
||||
int val1, int val2)
|
||||
{
|
||||
unsigned short str_loc = *(unsigned short *)(event + pred->offset);
|
||||
u32 str_item = *(u32 *)(event + pred->offset);
|
||||
int str_loc = str_item & 0xffff;
|
||||
int str_len = str_item >> 16;
|
||||
char *addr = (char *)(event + str_loc);
|
||||
int cmp, match;
|
||||
|
||||
cmp = strncmp(addr, pred->str_val, pred->str_len);
|
||||
cmp = strncmp(addr, pred->str_val, str_len);
|
||||
|
||||
match = (!cmp) ^ pred->not;
|
||||
|
||||
@@ -293,7 +309,7 @@ void print_event_filter(struct ftrace_event_call *call, struct trace_seq *s)
|
||||
struct event_filter *filter = call->filter;
|
||||
|
||||
mutex_lock(&event_mutex);
|
||||
if (filter->filter_string)
|
||||
if (filter && filter->filter_string)
|
||||
trace_seq_printf(s, "%s\n", filter->filter_string);
|
||||
else
|
||||
trace_seq_printf(s, "none\n");
|
||||
@@ -306,7 +322,7 @@ void print_subsystem_event_filter(struct event_subsystem *system,
|
||||
struct event_filter *filter = system->filter;
|
||||
|
||||
mutex_lock(&event_mutex);
|
||||
if (filter->filter_string)
|
||||
if (filter && filter->filter_string)
|
||||
trace_seq_printf(s, "%s\n", filter->filter_string);
|
||||
else
|
||||
trace_seq_printf(s, "none\n");
|
||||
@@ -374,6 +390,9 @@ void destroy_preds(struct ftrace_event_call *call)
|
||||
struct event_filter *filter = call->filter;
|
||||
int i;
|
||||
|
||||
if (!filter)
|
||||
return;
|
||||
|
||||
for (i = 0; i < MAX_FILTER_PRED; i++) {
|
||||
if (filter->preds[i])
|
||||
filter_free_pred(filter->preds[i]);
|
||||
@@ -384,17 +403,19 @@ void destroy_preds(struct ftrace_event_call *call)
|
||||
call->filter = NULL;
|
||||
}
|
||||
|
||||
int init_preds(struct ftrace_event_call *call)
|
||||
static int init_preds(struct ftrace_event_call *call)
|
||||
{
|
||||
struct event_filter *filter;
|
||||
struct filter_pred *pred;
|
||||
int i;
|
||||
|
||||
if (call->filter)
|
||||
return 0;
|
||||
|
||||
filter = call->filter = kzalloc(sizeof(*filter), GFP_KERNEL);
|
||||
if (!call->filter)
|
||||
return -ENOMEM;
|
||||
|
||||
call->filter_active = 0;
|
||||
filter->n_preds = 0;
|
||||
|
||||
filter->preds = kzalloc(MAX_FILTER_PRED * sizeof(pred), GFP_KERNEL);
|
||||
@@ -416,30 +437,55 @@ oom:
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(init_preds);
|
||||
|
||||
static void filter_free_subsystem_preds(struct event_subsystem *system)
|
||||
static int init_subsystem_preds(struct event_subsystem *system)
|
||||
{
|
||||
struct event_filter *filter = system->filter;
|
||||
struct ftrace_event_call *call;
|
||||
int i;
|
||||
|
||||
if (filter->n_preds) {
|
||||
for (i = 0; i < filter->n_preds; i++)
|
||||
filter_free_pred(filter->preds[i]);
|
||||
kfree(filter->preds);
|
||||
filter->preds = NULL;
|
||||
filter->n_preds = 0;
|
||||
}
|
||||
int err;
|
||||
|
||||
list_for_each_entry(call, &ftrace_events, list) {
|
||||
if (!call->define_fields)
|
||||
continue;
|
||||
|
||||
if (!strcmp(call->system, system->name)) {
|
||||
filter_disable_preds(call);
|
||||
remove_filter_string(call->filter);
|
||||
if (strcmp(call->system, system->name) != 0)
|
||||
continue;
|
||||
|
||||
err = init_preds(call);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum {
|
||||
FILTER_DISABLE_ALL,
|
||||
FILTER_INIT_NO_RESET,
|
||||
FILTER_SKIP_NO_RESET,
|
||||
};
|
||||
|
||||
static void filter_free_subsystem_preds(struct event_subsystem *system,
|
||||
int flag)
|
||||
{
|
||||
struct ftrace_event_call *call;
|
||||
|
||||
list_for_each_entry(call, &ftrace_events, list) {
|
||||
if (!call->define_fields)
|
||||
continue;
|
||||
|
||||
if (strcmp(call->system, system->name) != 0)
|
||||
continue;
|
||||
|
||||
if (flag == FILTER_INIT_NO_RESET) {
|
||||
call->filter->no_reset = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (flag == FILTER_SKIP_NO_RESET && call->filter->no_reset)
|
||||
continue;
|
||||
|
||||
filter_disable_preds(call);
|
||||
remove_filter_string(call->filter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -468,12 +514,7 @@ static int filter_add_pred_fn(struct filter_parse_state *ps,
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum {
|
||||
FILTER_STATIC_STRING = 1,
|
||||
FILTER_DYN_STRING
|
||||
};
|
||||
|
||||
static int is_string_field(const char *type)
|
||||
int filter_assign_type(const char *type)
|
||||
{
|
||||
if (strstr(type, "__data_loc") && strstr(type, "char"))
|
||||
return FILTER_DYN_STRING;
|
||||
@@ -481,12 +522,19 @@ static int is_string_field(const char *type)
|
||||
if (strchr(type, '[') && strstr(type, "char"))
|
||||
return FILTER_STATIC_STRING;
|
||||
|
||||
return 0;
|
||||
return FILTER_OTHER;
|
||||
}
|
||||
|
||||
static bool is_string_field(struct ftrace_event_field *field)
|
||||
{
|
||||
return field->filter_type == FILTER_DYN_STRING ||
|
||||
field->filter_type == FILTER_STATIC_STRING ||
|
||||
field->filter_type == FILTER_PTR_STRING;
|
||||
}
|
||||
|
||||
static int is_legal_op(struct ftrace_event_field *field, int op)
|
||||
{
|
||||
if (is_string_field(field->type) && (op != OP_EQ && op != OP_NE))
|
||||
if (is_string_field(field) && (op != OP_EQ && op != OP_NE))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
@@ -537,22 +585,24 @@ static filter_pred_fn_t select_comparison_fn(int op, int field_size,
|
||||
|
||||
static int filter_add_pred(struct filter_parse_state *ps,
|
||||
struct ftrace_event_call *call,
|
||||
struct filter_pred *pred)
|
||||
struct filter_pred *pred,
|
||||
bool dry_run)
|
||||
{
|
||||
struct ftrace_event_field *field;
|
||||
filter_pred_fn_t fn;
|
||||
unsigned long long val;
|
||||
int string_type;
|
||||
int ret;
|
||||
|
||||
pred->fn = filter_pred_none;
|
||||
|
||||
if (pred->op == OP_AND) {
|
||||
pred->pop_n = 2;
|
||||
return filter_add_pred_fn(ps, call, pred, filter_pred_and);
|
||||
fn = filter_pred_and;
|
||||
goto add_pred_fn;
|
||||
} else if (pred->op == OP_OR) {
|
||||
pred->pop_n = 2;
|
||||
return filter_add_pred_fn(ps, call, pred, filter_pred_or);
|
||||
fn = filter_pred_or;
|
||||
goto add_pred_fn;
|
||||
}
|
||||
|
||||
field = find_event_field(call, pred->field_name);
|
||||
@@ -568,16 +618,17 @@ static int filter_add_pred(struct filter_parse_state *ps,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
string_type = is_string_field(field->type);
|
||||
if (string_type) {
|
||||
if (string_type == FILTER_STATIC_STRING)
|
||||
fn = filter_pred_string;
|
||||
else
|
||||
fn = filter_pred_strloc;
|
||||
if (is_string_field(field)) {
|
||||
pred->str_len = field->size;
|
||||
if (pred->op == OP_NE)
|
||||
pred->not = 1;
|
||||
return filter_add_pred_fn(ps, call, pred, fn);
|
||||
|
||||
if (field->filter_type == FILTER_STATIC_STRING)
|
||||
fn = filter_pred_string;
|
||||
else if (field->filter_type == FILTER_DYN_STRING)
|
||||
fn = filter_pred_strloc;
|
||||
else {
|
||||
fn = filter_pred_pchar;
|
||||
pred->str_len = strlen(pred->str_val);
|
||||
}
|
||||
} else {
|
||||
if (field->is_signed)
|
||||
ret = strict_strtoll(pred->str_val, 0, &val);
|
||||
@@ -588,41 +639,33 @@ static int filter_add_pred(struct filter_parse_state *ps,
|
||||
return -EINVAL;
|
||||
}
|
||||
pred->val = val;
|
||||
}
|
||||
|
||||
fn = select_comparison_fn(pred->op, field->size, field->is_signed);
|
||||
if (!fn) {
|
||||
parse_error(ps, FILT_ERR_INVALID_OP, 0);
|
||||
return -EINVAL;
|
||||
fn = select_comparison_fn(pred->op, field->size,
|
||||
field->is_signed);
|
||||
if (!fn) {
|
||||
parse_error(ps, FILT_ERR_INVALID_OP, 0);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (pred->op == OP_NE)
|
||||
pred->not = 1;
|
||||
|
||||
return filter_add_pred_fn(ps, call, pred, fn);
|
||||
add_pred_fn:
|
||||
if (!dry_run)
|
||||
return filter_add_pred_fn(ps, call, pred, fn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int filter_add_subsystem_pred(struct filter_parse_state *ps,
|
||||
struct event_subsystem *system,
|
||||
struct filter_pred *pred,
|
||||
char *filter_string)
|
||||
char *filter_string,
|
||||
bool dry_run)
|
||||
{
|
||||
struct event_filter *filter = system->filter;
|
||||
struct ftrace_event_call *call;
|
||||
int err = 0;
|
||||
|
||||
if (!filter->preds) {
|
||||
filter->preds = kzalloc(MAX_FILTER_PRED * sizeof(pred),
|
||||
GFP_KERNEL);
|
||||
|
||||
if (!filter->preds)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (filter->n_preds == MAX_FILTER_PRED) {
|
||||
parse_error(ps, FILT_ERR_TOO_MANY_PREDS, 0);
|
||||
return -ENOSPC;
|
||||
}
|
||||
bool fail = true;
|
||||
|
||||
list_for_each_entry(call, &ftrace_events, list) {
|
||||
|
||||
@@ -632,19 +675,24 @@ static int filter_add_subsystem_pred(struct filter_parse_state *ps,
|
||||
if (strcmp(call->system, system->name))
|
||||
continue;
|
||||
|
||||
err = filter_add_pred(ps, call, pred);
|
||||
if (err) {
|
||||
filter_free_subsystem_preds(system);
|
||||
parse_error(ps, FILT_ERR_BAD_SUBSYS_FILTER, 0);
|
||||
goto out;
|
||||
}
|
||||
replace_filter_string(call->filter, filter_string);
|
||||
if (call->filter->no_reset)
|
||||
continue;
|
||||
|
||||
err = filter_add_pred(ps, call, pred, dry_run);
|
||||
if (err)
|
||||
call->filter->no_reset = true;
|
||||
else
|
||||
fail = false;
|
||||
|
||||
if (!dry_run)
|
||||
replace_filter_string(call->filter, filter_string);
|
||||
}
|
||||
|
||||
filter->preds[filter->n_preds] = pred;
|
||||
filter->n_preds++;
|
||||
out:
|
||||
return err;
|
||||
if (fail) {
|
||||
parse_error(ps, FILT_ERR_BAD_SUBSYS_FILTER, 0);
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void parse_init(struct filter_parse_state *ps,
|
||||
@@ -1003,12 +1051,14 @@ static int check_preds(struct filter_parse_state *ps)
|
||||
static int replace_preds(struct event_subsystem *system,
|
||||
struct ftrace_event_call *call,
|
||||
struct filter_parse_state *ps,
|
||||
char *filter_string)
|
||||
char *filter_string,
|
||||
bool dry_run)
|
||||
{
|
||||
char *operand1 = NULL, *operand2 = NULL;
|
||||
struct filter_pred *pred;
|
||||
struct postfix_elt *elt;
|
||||
int err;
|
||||
int n_preds = 0;
|
||||
|
||||
err = check_preds(ps);
|
||||
if (err)
|
||||
@@ -1027,24 +1077,14 @@ static int replace_preds(struct event_subsystem *system,
|
||||
continue;
|
||||
}
|
||||
|
||||
if (n_preds++ == MAX_FILTER_PRED) {
|
||||
parse_error(ps, FILT_ERR_TOO_MANY_PREDS, 0);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
if (elt->op == OP_AND || elt->op == OP_OR) {
|
||||
pred = create_logical_pred(elt->op);
|
||||
if (!pred)
|
||||
return -ENOMEM;
|
||||
if (call) {
|
||||
err = filter_add_pred(ps, call, pred);
|
||||
filter_free_pred(pred);
|
||||
} else {
|
||||
err = filter_add_subsystem_pred(ps, system,
|
||||
pred, filter_string);
|
||||
if (err)
|
||||
filter_free_pred(pred);
|
||||
}
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
operand1 = operand2 = NULL;
|
||||
continue;
|
||||
goto add_pred;
|
||||
}
|
||||
|
||||
if (!operand1 || !operand2) {
|
||||
@@ -1053,17 +1093,15 @@ static int replace_preds(struct event_subsystem *system,
|
||||
}
|
||||
|
||||
pred = create_pred(elt->op, operand1, operand2);
|
||||
add_pred:
|
||||
if (!pred)
|
||||
return -ENOMEM;
|
||||
if (call) {
|
||||
err = filter_add_pred(ps, call, pred);
|
||||
filter_free_pred(pred);
|
||||
} else {
|
||||
if (call)
|
||||
err = filter_add_pred(ps, call, pred, false);
|
||||
else
|
||||
err = filter_add_subsystem_pred(ps, system, pred,
|
||||
filter_string);
|
||||
if (err)
|
||||
filter_free_pred(pred);
|
||||
}
|
||||
filter_string, dry_run);
|
||||
filter_free_pred(pred);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -1081,6 +1119,10 @@ int apply_event_filter(struct ftrace_event_call *call, char *filter_string)
|
||||
|
||||
mutex_lock(&event_mutex);
|
||||
|
||||
err = init_preds(call);
|
||||
if (err)
|
||||
goto out_unlock;
|
||||
|
||||
if (!strcmp(strstrip(filter_string), "0")) {
|
||||
filter_disable_preds(call);
|
||||
remove_filter_string(call->filter);
|
||||
@@ -1103,7 +1145,7 @@ int apply_event_filter(struct ftrace_event_call *call, char *filter_string)
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = replace_preds(NULL, call, ps, filter_string);
|
||||
err = replace_preds(NULL, call, ps, filter_string, false);
|
||||
if (err)
|
||||
append_filter_err(ps, call->filter);
|
||||
|
||||
@@ -1126,8 +1168,12 @@ int apply_subsystem_event_filter(struct event_subsystem *system,
|
||||
|
||||
mutex_lock(&event_mutex);
|
||||
|
||||
err = init_subsystem_preds(system);
|
||||
if (err)
|
||||
goto out_unlock;
|
||||
|
||||
if (!strcmp(strstrip(filter_string), "0")) {
|
||||
filter_free_subsystem_preds(system);
|
||||
filter_free_subsystem_preds(system, FILTER_DISABLE_ALL);
|
||||
remove_filter_string(system->filter);
|
||||
mutex_unlock(&event_mutex);
|
||||
return 0;
|
||||
@@ -1138,7 +1184,6 @@ int apply_subsystem_event_filter(struct event_subsystem *system,
|
||||
if (!ps)
|
||||
goto out_unlock;
|
||||
|
||||
filter_free_subsystem_preds(system);
|
||||
replace_filter_string(system->filter, filter_string);
|
||||
|
||||
parse_init(ps, filter_ops, filter_string);
|
||||
@@ -1148,9 +1193,23 @@ int apply_subsystem_event_filter(struct event_subsystem *system,
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = replace_preds(system, NULL, ps, filter_string);
|
||||
if (err)
|
||||
filter_free_subsystem_preds(system, FILTER_INIT_NO_RESET);
|
||||
|
||||
/* try to see the filter can be applied to which events */
|
||||
err = replace_preds(system, NULL, ps, filter_string, true);
|
||||
if (err) {
|
||||
append_filter_err(ps, system->filter);
|
||||
goto out;
|
||||
}
|
||||
|
||||
filter_free_subsystem_preds(system, FILTER_SKIP_NO_RESET);
|
||||
|
||||
/* really apply the filter to the events */
|
||||
err = replace_preds(system, NULL, ps, filter_string, false);
|
||||
if (err) {
|
||||
append_filter_err(ps, system->filter);
|
||||
filter_free_subsystem_preds(system, 2);
|
||||
}
|
||||
|
||||
out:
|
||||
filter_opstack_clear(ps);
|
||||
|
@@ -60,7 +60,8 @@ extern void __bad_type_size(void);
|
||||
#undef TRACE_EVENT_FORMAT
|
||||
#define TRACE_EVENT_FORMAT(call, proto, args, fmt, tstruct, tpfmt) \
|
||||
static int \
|
||||
ftrace_format_##call(struct trace_seq *s) \
|
||||
ftrace_format_##call(struct ftrace_event_call *unused, \
|
||||
struct trace_seq *s) \
|
||||
{ \
|
||||
struct args field; \
|
||||
int ret; \
|
||||
@@ -76,7 +77,8 @@ ftrace_format_##call(struct trace_seq *s) \
|
||||
#define TRACE_EVENT_FORMAT_NOFILTER(call, proto, args, fmt, tstruct, \
|
||||
tpfmt) \
|
||||
static int \
|
||||
ftrace_format_##call(struct trace_seq *s) \
|
||||
ftrace_format_##call(struct ftrace_event_call *unused, \
|
||||
struct trace_seq *s) \
|
||||
{ \
|
||||
struct args field; \
|
||||
int ret; \
|
||||
@@ -117,7 +119,7 @@ ftrace_format_##call(struct trace_seq *s) \
|
||||
|
||||
#undef TRACE_EVENT_FORMAT
|
||||
#define TRACE_EVENT_FORMAT(call, proto, args, fmt, tstruct, tpfmt) \
|
||||
int ftrace_define_fields_##call(void); \
|
||||
int ftrace_define_fields_##call(struct ftrace_event_call *event_call); \
|
||||
static int ftrace_raw_init_event_##call(void); \
|
||||
\
|
||||
struct ftrace_event_call __used \
|
||||
@@ -133,7 +135,6 @@ __attribute__((section("_ftrace_events"))) event_##call = { \
|
||||
static int ftrace_raw_init_event_##call(void) \
|
||||
{ \
|
||||
INIT_LIST_HEAD(&event_##call.fields); \
|
||||
init_preds(&event_##call); \
|
||||
return 0; \
|
||||
} \
|
||||
|
||||
@@ -156,7 +157,8 @@ __attribute__((section("_ftrace_events"))) event_##call = { \
|
||||
#define TRACE_FIELD(type, item, assign) \
|
||||
ret = trace_define_field(event_call, #type, #item, \
|
||||
offsetof(typeof(field), item), \
|
||||
sizeof(field.item), is_signed_type(type)); \
|
||||
sizeof(field.item), \
|
||||
is_signed_type(type), FILTER_OTHER); \
|
||||
if (ret) \
|
||||
return ret;
|
||||
|
||||
@@ -164,7 +166,7 @@ __attribute__((section("_ftrace_events"))) event_##call = { \
|
||||
#define TRACE_FIELD_SPECIAL(type, item, len, cmd) \
|
||||
ret = trace_define_field(event_call, #type "[" #len "]", #item, \
|
||||
offsetof(typeof(field), item), \
|
||||
sizeof(field.item), 0); \
|
||||
sizeof(field.item), 0, FILTER_OTHER); \
|
||||
if (ret) \
|
||||
return ret;
|
||||
|
||||
@@ -172,7 +174,8 @@ __attribute__((section("_ftrace_events"))) event_##call = { \
|
||||
#define TRACE_FIELD_SIGN(type, item, assign, is_signed) \
|
||||
ret = trace_define_field(event_call, #type, #item, \
|
||||
offsetof(typeof(field), item), \
|
||||
sizeof(field.item), is_signed); \
|
||||
sizeof(field.item), is_signed, \
|
||||
FILTER_OTHER); \
|
||||
if (ret) \
|
||||
return ret;
|
||||
|
||||
@@ -182,17 +185,14 @@ __attribute__((section("_ftrace_events"))) event_##call = { \
|
||||
#undef TRACE_EVENT_FORMAT
|
||||
#define TRACE_EVENT_FORMAT(call, proto, args, fmt, tstruct, tpfmt) \
|
||||
int \
|
||||
ftrace_define_fields_##call(void) \
|
||||
ftrace_define_fields_##call(struct ftrace_event_call *event_call) \
|
||||
{ \
|
||||
struct ftrace_event_call *event_call = &event_##call; \
|
||||
struct args field; \
|
||||
int ret; \
|
||||
\
|
||||
__common_field(unsigned char, type, 0); \
|
||||
__common_field(unsigned char, flags, 0); \
|
||||
__common_field(unsigned char, preempt_count, 0); \
|
||||
__common_field(int, pid, 1); \
|
||||
__common_field(int, tgid, 1); \
|
||||
ret = trace_define_common_fields(event_call); \
|
||||
if (ret) \
|
||||
return ret; \
|
||||
\
|
||||
tstruct; \
|
||||
\
|
||||
|
@@ -288,11 +288,9 @@ static int
|
||||
ftrace_trace_onoff_print(struct seq_file *m, unsigned long ip,
|
||||
struct ftrace_probe_ops *ops, void *data)
|
||||
{
|
||||
char str[KSYM_SYMBOL_LEN];
|
||||
long count = (long)data;
|
||||
|
||||
kallsyms_lookup(ip, NULL, NULL, NULL, str);
|
||||
seq_printf(m, "%s:", str);
|
||||
seq_printf(m, "%pf:", (void *)ip);
|
||||
|
||||
if (ops == &traceon_probe_ops)
|
||||
seq_printf(m, "traceon");
|
||||
|
@@ -52,7 +52,7 @@ static struct tracer_flags tracer_flags = {
|
||||
.opts = trace_opts
|
||||
};
|
||||
|
||||
/* pid on the last trace processed */
|
||||
static struct trace_array *graph_array;
|
||||
|
||||
|
||||
/* Add a function return address to the trace stack on thread info.*/
|
||||
@@ -166,10 +166,123 @@ unsigned long ftrace_return_to_handler(unsigned long frame_pointer)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __trace_graph_entry(struct trace_array *tr,
|
||||
struct ftrace_graph_ent *trace,
|
||||
unsigned long flags,
|
||||
int pc)
|
||||
{
|
||||
struct ftrace_event_call *call = &event_funcgraph_entry;
|
||||
struct ring_buffer_event *event;
|
||||
struct ring_buffer *buffer = tr->buffer;
|
||||
struct ftrace_graph_ent_entry *entry;
|
||||
|
||||
if (unlikely(local_read(&__get_cpu_var(ftrace_cpu_disabled))))
|
||||
return 0;
|
||||
|
||||
event = trace_buffer_lock_reserve(buffer, TRACE_GRAPH_ENT,
|
||||
sizeof(*entry), flags, pc);
|
||||
if (!event)
|
||||
return 0;
|
||||
entry = ring_buffer_event_data(event);
|
||||
entry->graph_ent = *trace;
|
||||
if (!filter_current_check_discard(buffer, call, entry, event))
|
||||
ring_buffer_unlock_commit(buffer, event);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int trace_graph_entry(struct ftrace_graph_ent *trace)
|
||||
{
|
||||
struct trace_array *tr = graph_array;
|
||||
struct trace_array_cpu *data;
|
||||
unsigned long flags;
|
||||
long disabled;
|
||||
int ret;
|
||||
int cpu;
|
||||
int pc;
|
||||
|
||||
if (unlikely(!tr))
|
||||
return 0;
|
||||
|
||||
if (!ftrace_trace_task(current))
|
||||
return 0;
|
||||
|
||||
if (!ftrace_graph_addr(trace->func))
|
||||
return 0;
|
||||
|
||||
local_irq_save(flags);
|
||||
cpu = raw_smp_processor_id();
|
||||
data = tr->data[cpu];
|
||||
disabled = atomic_inc_return(&data->disabled);
|
||||
if (likely(disabled == 1)) {
|
||||
pc = preempt_count();
|
||||
ret = __trace_graph_entry(tr, trace, flags, pc);
|
||||
} else {
|
||||
ret = 0;
|
||||
}
|
||||
/* Only do the atomic if it is not already set */
|
||||
if (!test_tsk_trace_graph(current))
|
||||
set_tsk_trace_graph(current);
|
||||
|
||||
atomic_dec(&data->disabled);
|
||||
local_irq_restore(flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __trace_graph_return(struct trace_array *tr,
|
||||
struct ftrace_graph_ret *trace,
|
||||
unsigned long flags,
|
||||
int pc)
|
||||
{
|
||||
struct ftrace_event_call *call = &event_funcgraph_exit;
|
||||
struct ring_buffer_event *event;
|
||||
struct ring_buffer *buffer = tr->buffer;
|
||||
struct ftrace_graph_ret_entry *entry;
|
||||
|
||||
if (unlikely(local_read(&__get_cpu_var(ftrace_cpu_disabled))))
|
||||
return;
|
||||
|
||||
event = trace_buffer_lock_reserve(buffer, TRACE_GRAPH_RET,
|
||||
sizeof(*entry), flags, pc);
|
||||
if (!event)
|
||||
return;
|
||||
entry = ring_buffer_event_data(event);
|
||||
entry->ret = *trace;
|
||||
if (!filter_current_check_discard(buffer, call, entry, event))
|
||||
ring_buffer_unlock_commit(buffer, event);
|
||||
}
|
||||
|
||||
void trace_graph_return(struct ftrace_graph_ret *trace)
|
||||
{
|
||||
struct trace_array *tr = graph_array;
|
||||
struct trace_array_cpu *data;
|
||||
unsigned long flags;
|
||||
long disabled;
|
||||
int cpu;
|
||||
int pc;
|
||||
|
||||
local_irq_save(flags);
|
||||
cpu = raw_smp_processor_id();
|
||||
data = tr->data[cpu];
|
||||
disabled = atomic_inc_return(&data->disabled);
|
||||
if (likely(disabled == 1)) {
|
||||
pc = preempt_count();
|
||||
__trace_graph_return(tr, trace, flags, pc);
|
||||
}
|
||||
if (!trace->depth)
|
||||
clear_tsk_trace_graph(current);
|
||||
atomic_dec(&data->disabled);
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
static int graph_trace_init(struct trace_array *tr)
|
||||
{
|
||||
int ret = register_ftrace_graph(&trace_graph_return,
|
||||
&trace_graph_entry);
|
||||
int ret;
|
||||
|
||||
graph_array = tr;
|
||||
ret = register_ftrace_graph(&trace_graph_return,
|
||||
&trace_graph_entry);
|
||||
if (ret)
|
||||
return ret;
|
||||
tracing_start_cmdline_record();
|
||||
@@ -177,49 +290,30 @@ static int graph_trace_init(struct trace_array *tr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void set_graph_array(struct trace_array *tr)
|
||||
{
|
||||
graph_array = tr;
|
||||
}
|
||||
|
||||
static void graph_trace_reset(struct trace_array *tr)
|
||||
{
|
||||
tracing_stop_cmdline_record();
|
||||
unregister_ftrace_graph();
|
||||
}
|
||||
|
||||
static inline int log10_cpu(int nb)
|
||||
{
|
||||
if (nb / 100)
|
||||
return 3;
|
||||
if (nb / 10)
|
||||
return 2;
|
||||
return 1;
|
||||
}
|
||||
static int max_bytes_for_cpu;
|
||||
|
||||
static enum print_line_t
|
||||
print_graph_cpu(struct trace_seq *s, int cpu)
|
||||
{
|
||||
int i;
|
||||
int ret;
|
||||
int log10_this = log10_cpu(cpu);
|
||||
int log10_all = log10_cpu(cpumask_weight(cpu_online_mask));
|
||||
|
||||
|
||||
/*
|
||||
* Start with a space character - to make it stand out
|
||||
* to the right a bit when trace output is pasted into
|
||||
* email:
|
||||
*/
|
||||
ret = trace_seq_printf(s, " ");
|
||||
|
||||
/*
|
||||
* Tricky - we space the CPU field according to the max
|
||||
* number of online CPUs. On a 2-cpu system it would take
|
||||
* a maximum of 1 digit - on a 128 cpu system it would
|
||||
* take up to 3 digits:
|
||||
*/
|
||||
for (i = 0; i < log10_all - log10_this; i++) {
|
||||
ret = trace_seq_printf(s, " ");
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
}
|
||||
ret = trace_seq_printf(s, "%d) ", cpu);
|
||||
ret = trace_seq_printf(s, " %*d) ", max_bytes_for_cpu, cpu);
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
@@ -565,11 +659,7 @@ print_graph_entry_leaf(struct trace_iterator *iter,
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
}
|
||||
|
||||
ret = seq_print_ip_sym(s, call->func, 0);
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
ret = trace_seq_printf(s, "();\n");
|
||||
ret = trace_seq_printf(s, "%pf();\n", (void *)call->func);
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
@@ -612,11 +702,7 @@ print_graph_entry_nested(struct trace_iterator *iter,
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
}
|
||||
|
||||
ret = seq_print_ip_sym(s, call->func, 0);
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
ret = trace_seq_printf(s, "() {\n");
|
||||
ret = trace_seq_printf(s, "%pf() {\n", (void *)call->func);
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
@@ -934,6 +1020,8 @@ static struct tracer graph_trace __read_mostly = {
|
||||
|
||||
static __init int init_graph_trace(void)
|
||||
{
|
||||
max_bytes_for_cpu = snprintf(NULL, 0, "%d", nr_cpu_ids - 1);
|
||||
|
||||
return register_tracer(&graph_trace);
|
||||
}
|
||||
|
||||
|
@@ -178,7 +178,6 @@ out_unlock:
|
||||
out:
|
||||
data->critical_sequence = max_sequence;
|
||||
data->preempt_timestamp = ftrace_now(cpu);
|
||||
tracing_reset(tr, cpu);
|
||||
trace_function(tr, CALLER_ADDR0, parent_ip, flags, pc);
|
||||
}
|
||||
|
||||
@@ -208,7 +207,6 @@ start_critical_timing(unsigned long ip, unsigned long parent_ip)
|
||||
data->critical_sequence = max_sequence;
|
||||
data->preempt_timestamp = ftrace_now(cpu);
|
||||
data->critical_start = parent_ip ? : ip;
|
||||
tracing_reset(tr, cpu);
|
||||
|
||||
local_save_flags(flags);
|
||||
|
||||
@@ -379,6 +377,7 @@ static void __irqsoff_tracer_init(struct trace_array *tr)
|
||||
irqsoff_trace = tr;
|
||||
/* make sure that the tracer is visible */
|
||||
smp_wmb();
|
||||
tracing_reset_online_cpus(tr);
|
||||
start_irqsoff_tracer(tr);
|
||||
}
|
||||
|
||||
|
@@ -307,11 +307,12 @@ static void __trace_mmiotrace_rw(struct trace_array *tr,
|
||||
struct trace_array_cpu *data,
|
||||
struct mmiotrace_rw *rw)
|
||||
{
|
||||
struct ring_buffer *buffer = tr->buffer;
|
||||
struct ring_buffer_event *event;
|
||||
struct trace_mmiotrace_rw *entry;
|
||||
int pc = preempt_count();
|
||||
|
||||
event = trace_buffer_lock_reserve(tr, TRACE_MMIO_RW,
|
||||
event = trace_buffer_lock_reserve(buffer, TRACE_MMIO_RW,
|
||||
sizeof(*entry), 0, pc);
|
||||
if (!event) {
|
||||
atomic_inc(&dropped_count);
|
||||
@@ -319,7 +320,7 @@ static void __trace_mmiotrace_rw(struct trace_array *tr,
|
||||
}
|
||||
entry = ring_buffer_event_data(event);
|
||||
entry->rw = *rw;
|
||||
trace_buffer_unlock_commit(tr, event, 0, pc);
|
||||
trace_buffer_unlock_commit(buffer, event, 0, pc);
|
||||
}
|
||||
|
||||
void mmio_trace_rw(struct mmiotrace_rw *rw)
|
||||
@@ -333,11 +334,12 @@ static void __trace_mmiotrace_map(struct trace_array *tr,
|
||||
struct trace_array_cpu *data,
|
||||
struct mmiotrace_map *map)
|
||||
{
|
||||
struct ring_buffer *buffer = tr->buffer;
|
||||
struct ring_buffer_event *event;
|
||||
struct trace_mmiotrace_map *entry;
|
||||
int pc = preempt_count();
|
||||
|
||||
event = trace_buffer_lock_reserve(tr, TRACE_MMIO_MAP,
|
||||
event = trace_buffer_lock_reserve(buffer, TRACE_MMIO_MAP,
|
||||
sizeof(*entry), 0, pc);
|
||||
if (!event) {
|
||||
atomic_inc(&dropped_count);
|
||||
@@ -345,7 +347,7 @@ static void __trace_mmiotrace_map(struct trace_array *tr,
|
||||
}
|
||||
entry = ring_buffer_event_data(event);
|
||||
entry->map = *map;
|
||||
trace_buffer_unlock_commit(tr, event, 0, pc);
|
||||
trace_buffer_unlock_commit(buffer, event, 0, pc);
|
||||
}
|
||||
|
||||
void mmio_trace_mapping(struct mmiotrace_map *map)
|
||||
|
@@ -38,6 +38,7 @@ static void probe_power_end(struct power_trace *it)
|
||||
{
|
||||
struct ftrace_event_call *call = &event_power;
|
||||
struct ring_buffer_event *event;
|
||||
struct ring_buffer *buffer;
|
||||
struct trace_power *entry;
|
||||
struct trace_array_cpu *data;
|
||||
struct trace_array *tr = power_trace;
|
||||
@@ -45,18 +46,20 @@ static void probe_power_end(struct power_trace *it)
|
||||
if (!trace_power_enabled)
|
||||
return;
|
||||
|
||||
buffer = tr->buffer;
|
||||
|
||||
preempt_disable();
|
||||
it->end = ktime_get();
|
||||
data = tr->data[smp_processor_id()];
|
||||
|
||||
event = trace_buffer_lock_reserve(tr, TRACE_POWER,
|
||||
event = trace_buffer_lock_reserve(buffer, TRACE_POWER,
|
||||
sizeof(*entry), 0, 0);
|
||||
if (!event)
|
||||
goto out;
|
||||
entry = ring_buffer_event_data(event);
|
||||
entry->state_data = *it;
|
||||
if (!filter_check_discard(call, entry, tr->buffer, event))
|
||||
trace_buffer_unlock_commit(tr, event, 0, 0);
|
||||
if (!filter_check_discard(call, entry, buffer, event))
|
||||
trace_buffer_unlock_commit(buffer, event, 0, 0);
|
||||
out:
|
||||
preempt_enable();
|
||||
}
|
||||
@@ -66,6 +69,7 @@ static void probe_power_mark(struct power_trace *it, unsigned int type,
|
||||
{
|
||||
struct ftrace_event_call *call = &event_power;
|
||||
struct ring_buffer_event *event;
|
||||
struct ring_buffer *buffer;
|
||||
struct trace_power *entry;
|
||||
struct trace_array_cpu *data;
|
||||
struct trace_array *tr = power_trace;
|
||||
@@ -73,6 +77,8 @@ static void probe_power_mark(struct power_trace *it, unsigned int type,
|
||||
if (!trace_power_enabled)
|
||||
return;
|
||||
|
||||
buffer = tr->buffer;
|
||||
|
||||
memset(it, 0, sizeof(struct power_trace));
|
||||
it->state = level;
|
||||
it->type = type;
|
||||
@@ -81,14 +87,14 @@ static void probe_power_mark(struct power_trace *it, unsigned int type,
|
||||
it->end = it->stamp;
|
||||
data = tr->data[smp_processor_id()];
|
||||
|
||||
event = trace_buffer_lock_reserve(tr, TRACE_POWER,
|
||||
event = trace_buffer_lock_reserve(buffer, TRACE_POWER,
|
||||
sizeof(*entry), 0, 0);
|
||||
if (!event)
|
||||
goto out;
|
||||
entry = ring_buffer_event_data(event);
|
||||
entry->state_data = *it;
|
||||
if (!filter_check_discard(call, entry, tr->buffer, event))
|
||||
trace_buffer_unlock_commit(tr, event, 0, 0);
|
||||
if (!filter_check_discard(call, entry, buffer, event))
|
||||
trace_buffer_unlock_commit(buffer, event, 0, 0);
|
||||
out:
|
||||
preempt_enable();
|
||||
}
|
||||
@@ -144,14 +150,12 @@ static void power_trace_reset(struct trace_array *tr)
|
||||
|
||||
static int power_trace_init(struct trace_array *tr)
|
||||
{
|
||||
int cpu;
|
||||
power_trace = tr;
|
||||
|
||||
trace_power_enabled = 1;
|
||||
tracing_power_register();
|
||||
|
||||
for_each_cpu(cpu, cpu_possible_mask)
|
||||
tracing_reset(tr, cpu);
|
||||
tracing_reset_online_cpus(tr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@@ -20,6 +20,35 @@ static int sched_ref;
|
||||
static DEFINE_MUTEX(sched_register_mutex);
|
||||
static int sched_stopped;
|
||||
|
||||
|
||||
void
|
||||
tracing_sched_switch_trace(struct trace_array *tr,
|
||||
struct task_struct *prev,
|
||||
struct task_struct *next,
|
||||
unsigned long flags, int pc)
|
||||
{
|
||||
struct ftrace_event_call *call = &event_context_switch;
|
||||
struct ring_buffer *buffer = tr->buffer;
|
||||
struct ring_buffer_event *event;
|
||||
struct ctx_switch_entry *entry;
|
||||
|
||||
event = trace_buffer_lock_reserve(buffer, TRACE_CTX,
|
||||
sizeof(*entry), flags, pc);
|
||||
if (!event)
|
||||
return;
|
||||
entry = ring_buffer_event_data(event);
|
||||
entry->prev_pid = prev->pid;
|
||||
entry->prev_prio = prev->prio;
|
||||
entry->prev_state = prev->state;
|
||||
entry->next_pid = next->pid;
|
||||
entry->next_prio = next->prio;
|
||||
entry->next_state = next->state;
|
||||
entry->next_cpu = task_cpu(next);
|
||||
|
||||
if (!filter_check_discard(call, entry, buffer, event))
|
||||
trace_buffer_unlock_commit(buffer, event, flags, pc);
|
||||
}
|
||||
|
||||
static void
|
||||
probe_sched_switch(struct rq *__rq, struct task_struct *prev,
|
||||
struct task_struct *next)
|
||||
@@ -49,6 +78,36 @@ probe_sched_switch(struct rq *__rq, struct task_struct *prev,
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
void
|
||||
tracing_sched_wakeup_trace(struct trace_array *tr,
|
||||
struct task_struct *wakee,
|
||||
struct task_struct *curr,
|
||||
unsigned long flags, int pc)
|
||||
{
|
||||
struct ftrace_event_call *call = &event_wakeup;
|
||||
struct ring_buffer_event *event;
|
||||
struct ctx_switch_entry *entry;
|
||||
struct ring_buffer *buffer = tr->buffer;
|
||||
|
||||
event = trace_buffer_lock_reserve(buffer, TRACE_WAKE,
|
||||
sizeof(*entry), flags, pc);
|
||||
if (!event)
|
||||
return;
|
||||
entry = ring_buffer_event_data(event);
|
||||
entry->prev_pid = curr->pid;
|
||||
entry->prev_prio = curr->prio;
|
||||
entry->prev_state = curr->state;
|
||||
entry->next_pid = wakee->pid;
|
||||
entry->next_prio = wakee->prio;
|
||||
entry->next_state = wakee->state;
|
||||
entry->next_cpu = task_cpu(wakee);
|
||||
|
||||
if (!filter_check_discard(call, entry, buffer, event))
|
||||
ring_buffer_unlock_commit(buffer, event);
|
||||
ftrace_trace_stack(tr->buffer, flags, 6, pc);
|
||||
ftrace_trace_userstack(tr->buffer, flags, pc);
|
||||
}
|
||||
|
||||
static void
|
||||
probe_sched_wakeup(struct rq *__rq, struct task_struct *wakee, int success)
|
||||
{
|
||||
|
@@ -186,11 +186,6 @@ out:
|
||||
|
||||
static void __wakeup_reset(struct trace_array *tr)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
for_each_possible_cpu(cpu)
|
||||
tracing_reset(tr, cpu);
|
||||
|
||||
wakeup_cpu = -1;
|
||||
wakeup_prio = -1;
|
||||
|
||||
@@ -204,6 +199,8 @@ static void wakeup_reset(struct trace_array *tr)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
tracing_reset_online_cpus(tr);
|
||||
|
||||
local_irq_save(flags);
|
||||
__raw_spin_lock(&wakeup_lock);
|
||||
__wakeup_reset(tr);
|
||||
|
@@ -288,6 +288,7 @@ trace_selftest_startup_function_graph(struct tracer *trace,
|
||||
* to detect and recover from possible hangs
|
||||
*/
|
||||
tracing_reset_online_cpus(tr);
|
||||
set_graph_array(tr);
|
||||
ret = register_ftrace_graph(&trace_graph_return,
|
||||
&trace_graph_entry_watchdog);
|
||||
if (ret) {
|
||||
|
@@ -186,43 +186,33 @@ static const struct file_operations stack_max_size_fops = {
|
||||
};
|
||||
|
||||
static void *
|
||||
t_next(struct seq_file *m, void *v, loff_t *pos)
|
||||
__next(struct seq_file *m, loff_t *pos)
|
||||
{
|
||||
long i;
|
||||
long n = *pos - 1;
|
||||
|
||||
(*pos)++;
|
||||
|
||||
if (v == SEQ_START_TOKEN)
|
||||
i = 0;
|
||||
else {
|
||||
i = *(long *)v;
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i >= max_stack_trace.nr_entries ||
|
||||
stack_dump_trace[i] == ULONG_MAX)
|
||||
if (n >= max_stack_trace.nr_entries || stack_dump_trace[n] == ULONG_MAX)
|
||||
return NULL;
|
||||
|
||||
m->private = (void *)i;
|
||||
|
||||
m->private = (void *)n;
|
||||
return &m->private;
|
||||
}
|
||||
|
||||
static void *
|
||||
t_next(struct seq_file *m, void *v, loff_t *pos)
|
||||
{
|
||||
(*pos)++;
|
||||
return __next(m, pos);
|
||||
}
|
||||
|
||||
static void *t_start(struct seq_file *m, loff_t *pos)
|
||||
{
|
||||
void *t = SEQ_START_TOKEN;
|
||||
loff_t l = 0;
|
||||
|
||||
local_irq_disable();
|
||||
__raw_spin_lock(&max_stack_lock);
|
||||
|
||||
if (*pos == 0)
|
||||
return SEQ_START_TOKEN;
|
||||
|
||||
for (; t && l < *pos; t = t_next(m, t, &l))
|
||||
;
|
||||
|
||||
return t;
|
||||
return __next(m, pos);
|
||||
}
|
||||
|
||||
static void t_stop(struct seq_file *m, void *p)
|
||||
@@ -234,15 +224,8 @@ static void t_stop(struct seq_file *m, void *p)
|
||||
static int trace_lookup_stack(struct seq_file *m, long i)
|
||||
{
|
||||
unsigned long addr = stack_dump_trace[i];
|
||||
#ifdef CONFIG_KALLSYMS
|
||||
char str[KSYM_SYMBOL_LEN];
|
||||
|
||||
sprint_symbol(str, addr);
|
||||
|
||||
return seq_printf(m, "%s\n", str);
|
||||
#else
|
||||
return seq_printf(m, "%p\n", (void*)addr);
|
||||
#endif
|
||||
return seq_printf(m, "%pF\n", (void *)addr);
|
||||
}
|
||||
|
||||
static void print_disabled(struct seq_file *m)
|
||||
|
@@ -49,7 +49,8 @@ static struct dentry *stat_dir;
|
||||
* but it will at least advance closer to the next one
|
||||
* to be released.
|
||||
*/
|
||||
static struct rb_node *release_next(struct rb_node *node)
|
||||
static struct rb_node *release_next(struct tracer_stat *ts,
|
||||
struct rb_node *node)
|
||||
{
|
||||
struct stat_node *snode;
|
||||
struct rb_node *parent = rb_parent(node);
|
||||
@@ -67,6 +68,8 @@ static struct rb_node *release_next(struct rb_node *node)
|
||||
parent->rb_right = NULL;
|
||||
|
||||
snode = container_of(node, struct stat_node, node);
|
||||
if (ts->stat_release)
|
||||
ts->stat_release(snode->stat);
|
||||
kfree(snode);
|
||||
|
||||
return parent;
|
||||
@@ -78,7 +81,7 @@ static void __reset_stat_session(struct stat_session *session)
|
||||
struct rb_node *node = session->stat_root.rb_node;
|
||||
|
||||
while (node)
|
||||
node = release_next(node);
|
||||
node = release_next(session->ts, node);
|
||||
|
||||
session->stat_root = RB_ROOT;
|
||||
}
|
||||
@@ -200,17 +203,21 @@ static void *stat_seq_start(struct seq_file *s, loff_t *pos)
|
||||
{
|
||||
struct stat_session *session = s->private;
|
||||
struct rb_node *node;
|
||||
int n = *pos;
|
||||
int i;
|
||||
|
||||
/* Prevent from tracer switch or rbtree modification */
|
||||
mutex_lock(&session->stat_mutex);
|
||||
|
||||
/* If we are in the beginning of the file, print the headers */
|
||||
if (!*pos && session->ts->stat_headers)
|
||||
return SEQ_START_TOKEN;
|
||||
if (session->ts->stat_headers) {
|
||||
if (n == 0)
|
||||
return SEQ_START_TOKEN;
|
||||
n--;
|
||||
}
|
||||
|
||||
node = rb_first(&session->stat_root);
|
||||
for (i = 0; node && i < *pos; i++)
|
||||
for (i = 0; node && i < n; i++)
|
||||
node = rb_next(node);
|
||||
|
||||
return node;
|
||||
|
@@ -18,6 +18,8 @@ struct tracer_stat {
|
||||
int (*stat_cmp)(void *p1, void *p2);
|
||||
/* Print a stat entry */
|
||||
int (*stat_show)(struct seq_file *s, void *p);
|
||||
/* Release an entry */
|
||||
void (*stat_release)(void *stat);
|
||||
/* Print the headers of your stat entries */
|
||||
int (*stat_headers)(struct seq_file *s);
|
||||
};
|
||||
|
@@ -1,30 +1,18 @@
|
||||
#include <trace/syscall.h>
|
||||
#include <trace/events/syscalls.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/ftrace.h>
|
||||
#include <linux/perf_counter.h>
|
||||
#include <asm/syscall.h>
|
||||
|
||||
#include "trace_output.h"
|
||||
#include "trace.h"
|
||||
|
||||
/* Keep a counter of the syscall tracing users */
|
||||
static int refcount;
|
||||
|
||||
/* Prevent from races on thread flags toggling */
|
||||
static DEFINE_MUTEX(syscall_trace_lock);
|
||||
|
||||
/* Option to display the parameters types */
|
||||
enum {
|
||||
TRACE_SYSCALLS_OPT_TYPES = 0x1,
|
||||
};
|
||||
|
||||
static struct tracer_opt syscalls_opts[] = {
|
||||
{ TRACER_OPT(syscall_arg_type, TRACE_SYSCALLS_OPT_TYPES) },
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct tracer_flags syscalls_flags = {
|
||||
.val = 0, /* By default: no parameters types */
|
||||
.opts = syscalls_opts
|
||||
};
|
||||
static int sys_refcount_enter;
|
||||
static int sys_refcount_exit;
|
||||
static DECLARE_BITMAP(enabled_enter_syscalls, NR_syscalls);
|
||||
static DECLARE_BITMAP(enabled_exit_syscalls, NR_syscalls);
|
||||
|
||||
enum print_line_t
|
||||
print_syscall_enter(struct trace_iterator *iter, int flags)
|
||||
@@ -35,35 +23,46 @@ print_syscall_enter(struct trace_iterator *iter, int flags)
|
||||
struct syscall_metadata *entry;
|
||||
int i, ret, syscall;
|
||||
|
||||
trace_assign_type(trace, ent);
|
||||
|
||||
trace = (typeof(trace))ent;
|
||||
syscall = trace->nr;
|
||||
|
||||
entry = syscall_nr_to_meta(syscall);
|
||||
|
||||
if (!entry)
|
||||
goto end;
|
||||
|
||||
if (entry->enter_id != ent->type) {
|
||||
WARN_ON_ONCE(1);
|
||||
goto end;
|
||||
}
|
||||
|
||||
ret = trace_seq_printf(s, "%s(", entry->name);
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
for (i = 0; i < entry->nb_args; i++) {
|
||||
/* parameter types */
|
||||
if (syscalls_flags.val & TRACE_SYSCALLS_OPT_TYPES) {
|
||||
if (trace_flags & TRACE_ITER_VERBOSE) {
|
||||
ret = trace_seq_printf(s, "%s ", entry->types[i]);
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
}
|
||||
/* parameter values */
|
||||
ret = trace_seq_printf(s, "%s: %lx%s ", entry->args[i],
|
||||
ret = trace_seq_printf(s, "%s: %lx%s", entry->args[i],
|
||||
trace->args[i],
|
||||
i == entry->nb_args - 1 ? ")" : ",");
|
||||
i == entry->nb_args - 1 ? "" : ", ");
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
}
|
||||
|
||||
ret = trace_seq_putc(s, ')');
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
end:
|
||||
trace_seq_printf(s, "\n");
|
||||
ret = trace_seq_putc(s, '\n');
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
return TRACE_TYPE_HANDLED;
|
||||
}
|
||||
|
||||
@@ -77,16 +76,20 @@ print_syscall_exit(struct trace_iterator *iter, int flags)
|
||||
struct syscall_metadata *entry;
|
||||
int ret;
|
||||
|
||||
trace_assign_type(trace, ent);
|
||||
|
||||
trace = (typeof(trace))ent;
|
||||
syscall = trace->nr;
|
||||
|
||||
entry = syscall_nr_to_meta(syscall);
|
||||
|
||||
if (!entry) {
|
||||
trace_seq_printf(s, "\n");
|
||||
return TRACE_TYPE_HANDLED;
|
||||
}
|
||||
|
||||
if (entry->exit_id != ent->type) {
|
||||
WARN_ON_ONCE(1);
|
||||
return TRACE_TYPE_UNHANDLED;
|
||||
}
|
||||
|
||||
ret = trace_seq_printf(s, "%s -> 0x%lx\n", entry->name,
|
||||
trace->ret);
|
||||
if (!ret)
|
||||
@@ -95,62 +98,140 @@ print_syscall_exit(struct trace_iterator *iter, int flags)
|
||||
return TRACE_TYPE_HANDLED;
|
||||
}
|
||||
|
||||
void start_ftrace_syscalls(void)
|
||||
extern char *__bad_type_size(void);
|
||||
|
||||
#define SYSCALL_FIELD(type, name) \
|
||||
sizeof(type) != sizeof(trace.name) ? \
|
||||
__bad_type_size() : \
|
||||
#type, #name, offsetof(typeof(trace), name), sizeof(trace.name)
|
||||
|
||||
int syscall_enter_format(struct ftrace_event_call *call, struct trace_seq *s)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct task_struct *g, *t;
|
||||
int i;
|
||||
int nr;
|
||||
int ret;
|
||||
struct syscall_metadata *entry;
|
||||
struct syscall_trace_enter trace;
|
||||
int offset = offsetof(struct syscall_trace_enter, args);
|
||||
|
||||
mutex_lock(&syscall_trace_lock);
|
||||
nr = syscall_name_to_nr(call->data);
|
||||
entry = syscall_nr_to_meta(nr);
|
||||
|
||||
/* Don't enable the flag on the tasks twice */
|
||||
if (++refcount != 1)
|
||||
goto unlock;
|
||||
if (!entry)
|
||||
return 0;
|
||||
|
||||
arch_init_ftrace_syscalls();
|
||||
read_lock_irqsave(&tasklist_lock, flags);
|
||||
ret = trace_seq_printf(s, "\tfield:%s %s;\toffset:%zu;\tsize:%zu;\n",
|
||||
SYSCALL_FIELD(int, nr));
|
||||
if (!ret)
|
||||
return 0;
|
||||
|
||||
do_each_thread(g, t) {
|
||||
set_tsk_thread_flag(t, TIF_SYSCALL_FTRACE);
|
||||
} while_each_thread(g, t);
|
||||
for (i = 0; i < entry->nb_args; i++) {
|
||||
ret = trace_seq_printf(s, "\tfield:%s %s;", entry->types[i],
|
||||
entry->args[i]);
|
||||
if (!ret)
|
||||
return 0;
|
||||
ret = trace_seq_printf(s, "\toffset:%d;\tsize:%zu;\n", offset,
|
||||
sizeof(unsigned long));
|
||||
if (!ret)
|
||||
return 0;
|
||||
offset += sizeof(unsigned long);
|
||||
}
|
||||
|
||||
read_unlock_irqrestore(&tasklist_lock, flags);
|
||||
trace_seq_puts(s, "\nprint fmt: \"");
|
||||
for (i = 0; i < entry->nb_args; i++) {
|
||||
ret = trace_seq_printf(s, "%s: 0x%%0%zulx%s", entry->args[i],
|
||||
sizeof(unsigned long),
|
||||
i == entry->nb_args - 1 ? "" : ", ");
|
||||
if (!ret)
|
||||
return 0;
|
||||
}
|
||||
trace_seq_putc(s, '"');
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&syscall_trace_lock);
|
||||
for (i = 0; i < entry->nb_args; i++) {
|
||||
ret = trace_seq_printf(s, ", ((unsigned long)(REC->%s))",
|
||||
entry->args[i]);
|
||||
if (!ret)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return trace_seq_putc(s, '\n');
|
||||
}
|
||||
|
||||
void stop_ftrace_syscalls(void)
|
||||
int syscall_exit_format(struct ftrace_event_call *call, struct trace_seq *s)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct task_struct *g, *t;
|
||||
int ret;
|
||||
struct syscall_trace_exit trace;
|
||||
|
||||
mutex_lock(&syscall_trace_lock);
|
||||
ret = trace_seq_printf(s,
|
||||
"\tfield:%s %s;\toffset:%zu;\tsize:%zu;\n"
|
||||
"\tfield:%s %s;\toffset:%zu;\tsize:%zu;\n",
|
||||
SYSCALL_FIELD(int, nr),
|
||||
SYSCALL_FIELD(unsigned long, ret));
|
||||
if (!ret)
|
||||
return 0;
|
||||
|
||||
/* There are perhaps still some users */
|
||||
if (--refcount)
|
||||
goto unlock;
|
||||
|
||||
read_lock_irqsave(&tasklist_lock, flags);
|
||||
|
||||
do_each_thread(g, t) {
|
||||
clear_tsk_thread_flag(t, TIF_SYSCALL_FTRACE);
|
||||
} while_each_thread(g, t);
|
||||
|
||||
read_unlock_irqrestore(&tasklist_lock, flags);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&syscall_trace_lock);
|
||||
return trace_seq_printf(s, "\nprint fmt: \"0x%%lx\", REC->ret\n");
|
||||
}
|
||||
|
||||
void ftrace_syscall_enter(struct pt_regs *regs)
|
||||
int syscall_enter_define_fields(struct ftrace_event_call *call)
|
||||
{
|
||||
struct syscall_trace_enter trace;
|
||||
struct syscall_metadata *meta;
|
||||
int ret;
|
||||
int nr;
|
||||
int i;
|
||||
int offset = offsetof(typeof(trace), args);
|
||||
|
||||
nr = syscall_name_to_nr(call->data);
|
||||
meta = syscall_nr_to_meta(nr);
|
||||
|
||||
if (!meta)
|
||||
return 0;
|
||||
|
||||
ret = trace_define_common_fields(call);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < meta->nb_args; i++) {
|
||||
ret = trace_define_field(call, meta->types[i],
|
||||
meta->args[i], offset,
|
||||
sizeof(unsigned long), 0,
|
||||
FILTER_OTHER);
|
||||
offset += sizeof(unsigned long);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int syscall_exit_define_fields(struct ftrace_event_call *call)
|
||||
{
|
||||
struct syscall_trace_exit trace;
|
||||
int ret;
|
||||
|
||||
ret = trace_define_common_fields(call);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = trace_define_field(call, SYSCALL_FIELD(unsigned long, ret), 0,
|
||||
FILTER_OTHER);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ftrace_syscall_enter(struct pt_regs *regs, long id)
|
||||
{
|
||||
struct syscall_trace_enter *entry;
|
||||
struct syscall_metadata *sys_data;
|
||||
struct ring_buffer_event *event;
|
||||
struct ring_buffer *buffer;
|
||||
int size;
|
||||
int syscall_nr;
|
||||
|
||||
syscall_nr = syscall_get_nr(current, regs);
|
||||
if (syscall_nr < 0)
|
||||
return;
|
||||
if (!test_bit(syscall_nr, enabled_enter_syscalls))
|
||||
return;
|
||||
|
||||
sys_data = syscall_nr_to_meta(syscall_nr);
|
||||
if (!sys_data)
|
||||
@@ -158,8 +239,8 @@ void ftrace_syscall_enter(struct pt_regs *regs)
|
||||
|
||||
size = sizeof(*entry) + sizeof(unsigned long) * sys_data->nb_args;
|
||||
|
||||
event = trace_current_buffer_lock_reserve(TRACE_SYSCALL_ENTER, size,
|
||||
0, 0);
|
||||
event = trace_current_buffer_lock_reserve(&buffer, sys_data->enter_id,
|
||||
size, 0, 0);
|
||||
if (!event)
|
||||
return;
|
||||
|
||||
@@ -167,24 +248,30 @@ void ftrace_syscall_enter(struct pt_regs *regs)
|
||||
entry->nr = syscall_nr;
|
||||
syscall_get_arguments(current, regs, 0, sys_data->nb_args, entry->args);
|
||||
|
||||
trace_current_buffer_unlock_commit(event, 0, 0);
|
||||
trace_wake_up();
|
||||
if (!filter_current_check_discard(buffer, sys_data->enter_event,
|
||||
entry, event))
|
||||
trace_current_buffer_unlock_commit(buffer, event, 0, 0);
|
||||
}
|
||||
|
||||
void ftrace_syscall_exit(struct pt_regs *regs)
|
||||
void ftrace_syscall_exit(struct pt_regs *regs, long ret)
|
||||
{
|
||||
struct syscall_trace_exit *entry;
|
||||
struct syscall_metadata *sys_data;
|
||||
struct ring_buffer_event *event;
|
||||
struct ring_buffer *buffer;
|
||||
int syscall_nr;
|
||||
|
||||
syscall_nr = syscall_get_nr(current, regs);
|
||||
if (syscall_nr < 0)
|
||||
return;
|
||||
if (!test_bit(syscall_nr, enabled_exit_syscalls))
|
||||
return;
|
||||
|
||||
sys_data = syscall_nr_to_meta(syscall_nr);
|
||||
if (!sys_data)
|
||||
return;
|
||||
|
||||
event = trace_current_buffer_lock_reserve(TRACE_SYSCALL_EXIT,
|
||||
event = trace_current_buffer_lock_reserve(&buffer, sys_data->exit_id,
|
||||
sizeof(*entry), 0, 0);
|
||||
if (!event)
|
||||
return;
|
||||
@@ -193,58 +280,244 @@ void ftrace_syscall_exit(struct pt_regs *regs)
|
||||
entry->nr = syscall_nr;
|
||||
entry->ret = syscall_get_return_value(current, regs);
|
||||
|
||||
trace_current_buffer_unlock_commit(event, 0, 0);
|
||||
trace_wake_up();
|
||||
if (!filter_current_check_discard(buffer, sys_data->exit_event,
|
||||
entry, event))
|
||||
trace_current_buffer_unlock_commit(buffer, event, 0, 0);
|
||||
}
|
||||
|
||||
static int init_syscall_tracer(struct trace_array *tr)
|
||||
int reg_event_syscall_enter(void *ptr)
|
||||
{
|
||||
start_ftrace_syscalls();
|
||||
int ret = 0;
|
||||
int num;
|
||||
char *name;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void reset_syscall_tracer(struct trace_array *tr)
|
||||
{
|
||||
stop_ftrace_syscalls();
|
||||
tracing_reset_online_cpus(tr);
|
||||
}
|
||||
|
||||
static struct trace_event syscall_enter_event = {
|
||||
.type = TRACE_SYSCALL_ENTER,
|
||||
.trace = print_syscall_enter,
|
||||
};
|
||||
|
||||
static struct trace_event syscall_exit_event = {
|
||||
.type = TRACE_SYSCALL_EXIT,
|
||||
.trace = print_syscall_exit,
|
||||
};
|
||||
|
||||
static struct tracer syscall_tracer __read_mostly = {
|
||||
.name = "syscall",
|
||||
.init = init_syscall_tracer,
|
||||
.reset = reset_syscall_tracer,
|
||||
.flags = &syscalls_flags,
|
||||
};
|
||||
|
||||
__init int register_ftrace_syscalls(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = register_ftrace_event(&syscall_enter_event);
|
||||
if (!ret) {
|
||||
printk(KERN_WARNING "event %d failed to register\n",
|
||||
syscall_enter_event.type);
|
||||
WARN_ON_ONCE(1);
|
||||
name = (char *)ptr;
|
||||
num = syscall_name_to_nr(name);
|
||||
if (num < 0 || num >= NR_syscalls)
|
||||
return -ENOSYS;
|
||||
mutex_lock(&syscall_trace_lock);
|
||||
if (!sys_refcount_enter)
|
||||
ret = register_trace_sys_enter(ftrace_syscall_enter);
|
||||
if (ret) {
|
||||
pr_info("event trace: Could not activate"
|
||||
"syscall entry trace point");
|
||||
} else {
|
||||
set_bit(num, enabled_enter_syscalls);
|
||||
sys_refcount_enter++;
|
||||
}
|
||||
|
||||
ret = register_ftrace_event(&syscall_exit_event);
|
||||
if (!ret) {
|
||||
printk(KERN_WARNING "event %d failed to register\n",
|
||||
syscall_exit_event.type);
|
||||
WARN_ON_ONCE(1);
|
||||
}
|
||||
|
||||
return register_tracer(&syscall_tracer);
|
||||
mutex_unlock(&syscall_trace_lock);
|
||||
return ret;
|
||||
}
|
||||
device_initcall(register_ftrace_syscalls);
|
||||
|
||||
void unreg_event_syscall_enter(void *ptr)
|
||||
{
|
||||
int num;
|
||||
char *name;
|
||||
|
||||
name = (char *)ptr;
|
||||
num = syscall_name_to_nr(name);
|
||||
if (num < 0 || num >= NR_syscalls)
|
||||
return;
|
||||
mutex_lock(&syscall_trace_lock);
|
||||
sys_refcount_enter--;
|
||||
clear_bit(num, enabled_enter_syscalls);
|
||||
if (!sys_refcount_enter)
|
||||
unregister_trace_sys_enter(ftrace_syscall_enter);
|
||||
mutex_unlock(&syscall_trace_lock);
|
||||
}
|
||||
|
||||
int reg_event_syscall_exit(void *ptr)
|
||||
{
|
||||
int ret = 0;
|
||||
int num;
|
||||
char *name;
|
||||
|
||||
name = (char *)ptr;
|
||||
num = syscall_name_to_nr(name);
|
||||
if (num < 0 || num >= NR_syscalls)
|
||||
return -ENOSYS;
|
||||
mutex_lock(&syscall_trace_lock);
|
||||
if (!sys_refcount_exit)
|
||||
ret = register_trace_sys_exit(ftrace_syscall_exit);
|
||||
if (ret) {
|
||||
pr_info("event trace: Could not activate"
|
||||
"syscall exit trace point");
|
||||
} else {
|
||||
set_bit(num, enabled_exit_syscalls);
|
||||
sys_refcount_exit++;
|
||||
}
|
||||
mutex_unlock(&syscall_trace_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void unreg_event_syscall_exit(void *ptr)
|
||||
{
|
||||
int num;
|
||||
char *name;
|
||||
|
||||
name = (char *)ptr;
|
||||
num = syscall_name_to_nr(name);
|
||||
if (num < 0 || num >= NR_syscalls)
|
||||
return;
|
||||
mutex_lock(&syscall_trace_lock);
|
||||
sys_refcount_exit--;
|
||||
clear_bit(num, enabled_exit_syscalls);
|
||||
if (!sys_refcount_exit)
|
||||
unregister_trace_sys_exit(ftrace_syscall_exit);
|
||||
mutex_unlock(&syscall_trace_lock);
|
||||
}
|
||||
|
||||
struct trace_event event_syscall_enter = {
|
||||
.trace = print_syscall_enter,
|
||||
};
|
||||
|
||||
struct trace_event event_syscall_exit = {
|
||||
.trace = print_syscall_exit,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_EVENT_PROFILE
|
||||
|
||||
static DECLARE_BITMAP(enabled_prof_enter_syscalls, NR_syscalls);
|
||||
static DECLARE_BITMAP(enabled_prof_exit_syscalls, NR_syscalls);
|
||||
static int sys_prof_refcount_enter;
|
||||
static int sys_prof_refcount_exit;
|
||||
|
||||
static void prof_syscall_enter(struct pt_regs *regs, long id)
|
||||
{
|
||||
struct syscall_trace_enter *rec;
|
||||
struct syscall_metadata *sys_data;
|
||||
int syscall_nr;
|
||||
int size;
|
||||
|
||||
syscall_nr = syscall_get_nr(current, regs);
|
||||
if (!test_bit(syscall_nr, enabled_prof_enter_syscalls))
|
||||
return;
|
||||
|
||||
sys_data = syscall_nr_to_meta(syscall_nr);
|
||||
if (!sys_data)
|
||||
return;
|
||||
|
||||
/* get the size after alignment with the u32 buffer size field */
|
||||
size = sizeof(unsigned long) * sys_data->nb_args + sizeof(*rec);
|
||||
size = ALIGN(size + sizeof(u32), sizeof(u64));
|
||||
size -= sizeof(u32);
|
||||
|
||||
do {
|
||||
char raw_data[size];
|
||||
|
||||
/* zero the dead bytes from align to not leak stack to user */
|
||||
*(u64 *)(&raw_data[size - sizeof(u64)]) = 0ULL;
|
||||
|
||||
rec = (struct syscall_trace_enter *) raw_data;
|
||||
tracing_generic_entry_update(&rec->ent, 0, 0);
|
||||
rec->ent.type = sys_data->enter_id;
|
||||
rec->nr = syscall_nr;
|
||||
syscall_get_arguments(current, regs, 0, sys_data->nb_args,
|
||||
(unsigned long *)&rec->args);
|
||||
perf_tpcounter_event(sys_data->enter_id, 0, 1, rec, size);
|
||||
} while(0);
|
||||
}
|
||||
|
||||
int reg_prof_syscall_enter(char *name)
|
||||
{
|
||||
int ret = 0;
|
||||
int num;
|
||||
|
||||
num = syscall_name_to_nr(name);
|
||||
if (num < 0 || num >= NR_syscalls)
|
||||
return -ENOSYS;
|
||||
|
||||
mutex_lock(&syscall_trace_lock);
|
||||
if (!sys_prof_refcount_enter)
|
||||
ret = register_trace_sys_enter(prof_syscall_enter);
|
||||
if (ret) {
|
||||
pr_info("event trace: Could not activate"
|
||||
"syscall entry trace point");
|
||||
} else {
|
||||
set_bit(num, enabled_prof_enter_syscalls);
|
||||
sys_prof_refcount_enter++;
|
||||
}
|
||||
mutex_unlock(&syscall_trace_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void unreg_prof_syscall_enter(char *name)
|
||||
{
|
||||
int num;
|
||||
|
||||
num = syscall_name_to_nr(name);
|
||||
if (num < 0 || num >= NR_syscalls)
|
||||
return;
|
||||
|
||||
mutex_lock(&syscall_trace_lock);
|
||||
sys_prof_refcount_enter--;
|
||||
clear_bit(num, enabled_prof_enter_syscalls);
|
||||
if (!sys_prof_refcount_enter)
|
||||
unregister_trace_sys_enter(prof_syscall_enter);
|
||||
mutex_unlock(&syscall_trace_lock);
|
||||
}
|
||||
|
||||
static void prof_syscall_exit(struct pt_regs *regs, long ret)
|
||||
{
|
||||
struct syscall_metadata *sys_data;
|
||||
struct syscall_trace_exit rec;
|
||||
int syscall_nr;
|
||||
|
||||
syscall_nr = syscall_get_nr(current, regs);
|
||||
if (!test_bit(syscall_nr, enabled_prof_exit_syscalls))
|
||||
return;
|
||||
|
||||
sys_data = syscall_nr_to_meta(syscall_nr);
|
||||
if (!sys_data)
|
||||
return;
|
||||
|
||||
tracing_generic_entry_update(&rec.ent, 0, 0);
|
||||
rec.ent.type = sys_data->exit_id;
|
||||
rec.nr = syscall_nr;
|
||||
rec.ret = syscall_get_return_value(current, regs);
|
||||
|
||||
perf_tpcounter_event(sys_data->exit_id, 0, 1, &rec, sizeof(rec));
|
||||
}
|
||||
|
||||
int reg_prof_syscall_exit(char *name)
|
||||
{
|
||||
int ret = 0;
|
||||
int num;
|
||||
|
||||
num = syscall_name_to_nr(name);
|
||||
if (num < 0 || num >= NR_syscalls)
|
||||
return -ENOSYS;
|
||||
|
||||
mutex_lock(&syscall_trace_lock);
|
||||
if (!sys_prof_refcount_exit)
|
||||
ret = register_trace_sys_exit(prof_syscall_exit);
|
||||
if (ret) {
|
||||
pr_info("event trace: Could not activate"
|
||||
"syscall entry trace point");
|
||||
} else {
|
||||
set_bit(num, enabled_prof_exit_syscalls);
|
||||
sys_prof_refcount_exit++;
|
||||
}
|
||||
mutex_unlock(&syscall_trace_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void unreg_prof_syscall_exit(char *name)
|
||||
{
|
||||
int num;
|
||||
|
||||
num = syscall_name_to_nr(name);
|
||||
if (num < 0 || num >= NR_syscalls)
|
||||
return;
|
||||
|
||||
mutex_lock(&syscall_trace_lock);
|
||||
sys_prof_refcount_exit--;
|
||||
clear_bit(num, enabled_prof_exit_syscalls);
|
||||
if (!sys_prof_refcount_exit)
|
||||
unregister_trace_sys_exit(prof_syscall_exit);
|
||||
mutex_unlock(&syscall_trace_lock);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
@@ -9,6 +9,7 @@
|
||||
#include <trace/events/workqueue.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/kref.h>
|
||||
#include "trace_stat.h"
|
||||
#include "trace.h"
|
||||
|
||||
@@ -16,6 +17,7 @@
|
||||
/* A cpu workqueue thread */
|
||||
struct cpu_workqueue_stats {
|
||||
struct list_head list;
|
||||
struct kref kref;
|
||||
int cpu;
|
||||
pid_t pid;
|
||||
/* Can be inserted from interrupt or user context, need to be atomic */
|
||||
@@ -39,6 +41,11 @@ struct workqueue_global_stats {
|
||||
static DEFINE_PER_CPU(struct workqueue_global_stats, all_workqueue_stat);
|
||||
#define workqueue_cpu_stat(cpu) (&per_cpu(all_workqueue_stat, cpu))
|
||||
|
||||
static void cpu_workqueue_stat_free(struct kref *kref)
|
||||
{
|
||||
kfree(container_of(kref, struct cpu_workqueue_stats, kref));
|
||||
}
|
||||
|
||||
/* Insertion of a work */
|
||||
static void
|
||||
probe_workqueue_insertion(struct task_struct *wq_thread,
|
||||
@@ -96,8 +103,8 @@ static void probe_workqueue_creation(struct task_struct *wq_thread, int cpu)
|
||||
return;
|
||||
}
|
||||
INIT_LIST_HEAD(&cws->list);
|
||||
kref_init(&cws->kref);
|
||||
cws->cpu = cpu;
|
||||
|
||||
cws->pid = wq_thread->pid;
|
||||
|
||||
spin_lock_irqsave(&workqueue_cpu_stat(cpu)->lock, flags);
|
||||
@@ -118,7 +125,7 @@ static void probe_workqueue_destruction(struct task_struct *wq_thread)
|
||||
list) {
|
||||
if (node->pid == wq_thread->pid) {
|
||||
list_del(&node->list);
|
||||
kfree(node);
|
||||
kref_put(&node->kref, cpu_workqueue_stat_free);
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
@@ -137,9 +144,11 @@ static struct cpu_workqueue_stats *workqueue_stat_start_cpu(int cpu)
|
||||
|
||||
spin_lock_irqsave(&workqueue_cpu_stat(cpu)->lock, flags);
|
||||
|
||||
if (!list_empty(&workqueue_cpu_stat(cpu)->list))
|
||||
if (!list_empty(&workqueue_cpu_stat(cpu)->list)) {
|
||||
ret = list_entry(workqueue_cpu_stat(cpu)->list.next,
|
||||
struct cpu_workqueue_stats, list);
|
||||
kref_get(&ret->kref);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&workqueue_cpu_stat(cpu)->lock, flags);
|
||||
|
||||
@@ -162,9 +171,9 @@ static void *workqueue_stat_start(struct tracer_stat *trace)
|
||||
static void *workqueue_stat_next(void *prev, int idx)
|
||||
{
|
||||
struct cpu_workqueue_stats *prev_cws = prev;
|
||||
struct cpu_workqueue_stats *ret;
|
||||
int cpu = prev_cws->cpu;
|
||||
unsigned long flags;
|
||||
void *ret = NULL;
|
||||
|
||||
spin_lock_irqsave(&workqueue_cpu_stat(cpu)->lock, flags);
|
||||
if (list_is_last(&prev_cws->list, &workqueue_cpu_stat(cpu)->list)) {
|
||||
@@ -175,11 +184,14 @@ static void *workqueue_stat_next(void *prev, int idx)
|
||||
return NULL;
|
||||
} while (!(ret = workqueue_stat_start_cpu(cpu)));
|
||||
return ret;
|
||||
} else {
|
||||
ret = list_entry(prev_cws->list.next,
|
||||
struct cpu_workqueue_stats, list);
|
||||
kref_get(&ret->kref);
|
||||
}
|
||||
spin_unlock_irqrestore(&workqueue_cpu_stat(cpu)->lock, flags);
|
||||
|
||||
return list_entry(prev_cws->list.next, struct cpu_workqueue_stats,
|
||||
list);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int workqueue_stat_show(struct seq_file *s, void *p)
|
||||
@@ -203,6 +215,13 @@ static int workqueue_stat_show(struct seq_file *s, void *p)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void workqueue_stat_release(void *stat)
|
||||
{
|
||||
struct cpu_workqueue_stats *node = stat;
|
||||
|
||||
kref_put(&node->kref, cpu_workqueue_stat_free);
|
||||
}
|
||||
|
||||
static int workqueue_stat_headers(struct seq_file *s)
|
||||
{
|
||||
seq_printf(s, "# CPU INSERTED EXECUTED NAME\n");
|
||||
@@ -215,6 +234,7 @@ struct tracer_stat workqueue_stats __read_mostly = {
|
||||
.stat_start = workqueue_stat_start,
|
||||
.stat_next = workqueue_stat_next,
|
||||
.stat_show = workqueue_stat_show,
|
||||
.stat_release = workqueue_stat_release,
|
||||
.stat_headers = workqueue_stat_headers
|
||||
};
|
||||
|
||||
|
@@ -24,6 +24,7 @@
|
||||
#include <linux/tracepoint.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
extern struct tracepoint __start___tracepoints[];
|
||||
extern struct tracepoint __stop___tracepoints[];
|
||||
@@ -242,6 +243,11 @@ static void set_tracepoint(struct tracepoint_entry **entry,
|
||||
{
|
||||
WARN_ON(strcmp((*entry)->name, elem->name) != 0);
|
||||
|
||||
if (elem->regfunc && !elem->state && active)
|
||||
elem->regfunc();
|
||||
else if (elem->unregfunc && elem->state && !active)
|
||||
elem->unregfunc();
|
||||
|
||||
/*
|
||||
* rcu_assign_pointer has a smp_wmb() which makes sure that the new
|
||||
* probe callbacks array is consistent before setting a pointer to it.
|
||||
@@ -261,6 +267,9 @@ static void set_tracepoint(struct tracepoint_entry **entry,
|
||||
*/
|
||||
static void disable_tracepoint(struct tracepoint *elem)
|
||||
{
|
||||
if (elem->unregfunc && elem->state)
|
||||
elem->unregfunc();
|
||||
|
||||
elem->state = 0;
|
||||
rcu_assign_pointer(elem->funcs, NULL);
|
||||
}
|
||||
@@ -554,9 +563,6 @@ int tracepoint_module_notify(struct notifier_block *self,
|
||||
|
||||
switch (val) {
|
||||
case MODULE_STATE_COMING:
|
||||
tracepoint_update_probe_range(mod->tracepoints,
|
||||
mod->tracepoints + mod->num_tracepoints);
|
||||
break;
|
||||
case MODULE_STATE_GOING:
|
||||
tracepoint_update_probe_range(mod->tracepoints,
|
||||
mod->tracepoints + mod->num_tracepoints);
|
||||
@@ -577,3 +583,41 @@ static int init_tracepoints(void)
|
||||
__initcall(init_tracepoints);
|
||||
|
||||
#endif /* CONFIG_MODULES */
|
||||
|
||||
#ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS
|
||||
|
||||
/* NB: reg/unreg are called while guarded with the tracepoints_mutex */
|
||||
static int sys_tracepoint_refcount;
|
||||
|
||||
void syscall_regfunc(void)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct task_struct *g, *t;
|
||||
|
||||
if (!sys_tracepoint_refcount) {
|
||||
read_lock_irqsave(&tasklist_lock, flags);
|
||||
do_each_thread(g, t) {
|
||||
/* Skip kernel threads. */
|
||||
if (t->mm)
|
||||
set_tsk_thread_flag(t, TIF_SYSCALL_TRACEPOINT);
|
||||
} while_each_thread(g, t);
|
||||
read_unlock_irqrestore(&tasklist_lock, flags);
|
||||
}
|
||||
sys_tracepoint_refcount++;
|
||||
}
|
||||
|
||||
void syscall_unregfunc(void)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct task_struct *g, *t;
|
||||
|
||||
sys_tracepoint_refcount--;
|
||||
if (!sys_tracepoint_refcount) {
|
||||
read_lock_irqsave(&tasklist_lock, flags);
|
||||
do_each_thread(g, t) {
|
||||
clear_tsk_thread_flag(t, TIF_SYSCALL_TRACEPOINT);
|
||||
} while_each_thread(g, t);
|
||||
read_unlock_irqrestore(&tasklist_lock, flags);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@@ -317,8 +317,6 @@ static int worker_thread(void *__cwq)
|
||||
if (cwq->wq->freezeable)
|
||||
set_freezable();
|
||||
|
||||
set_user_nice(current, -5);
|
||||
|
||||
for (;;) {
|
||||
prepare_to_wait(&cwq->more_work, &wait, TASK_INTERRUPTIBLE);
|
||||
if (!freezing(current) &&
|
||||
@@ -600,7 +598,12 @@ static struct workqueue_struct *keventd_wq __read_mostly;
|
||||
* schedule_work - put work task in global workqueue
|
||||
* @work: job to be done
|
||||
*
|
||||
* This puts a job in the kernel-global workqueue.
|
||||
* Returns zero if @work was already on the kernel-global workqueue and
|
||||
* non-zero otherwise.
|
||||
*
|
||||
* This puts a job in the kernel-global workqueue if it was not already
|
||||
* queued and leaves it in the same position on the kernel-global
|
||||
* workqueue otherwise.
|
||||
*/
|
||||
int schedule_work(struct work_struct *work)
|
||||
{
|
||||
|
Reference in New Issue
Block a user