Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
This commit is contained in:
+2
-3
@@ -9,7 +9,7 @@ obj-y = sched.o fork.o exec_domain.o panic.o printk.o \
|
||||
rcupdate.o extable.o params.o posix-timers.o \
|
||||
kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o mutex.o \
|
||||
hrtimer.o rwsem.o nsproxy.o srcu.o semaphore.o \
|
||||
notifier.o ksysfs.o pm_qos_params.o sched_clock.o
|
||||
notifier.o ksysfs.o pm_qos_params.o sched_clock.o cred.o
|
||||
|
||||
ifdef CONFIG_FUNCTION_TRACER
|
||||
# Do not trace debug files and internal ftrace files
|
||||
@@ -19,7 +19,6 @@ CFLAGS_REMOVE_mutex-debug.o = -pg
|
||||
CFLAGS_REMOVE_rtmutex-debug.o = -pg
|
||||
CFLAGS_REMOVE_cgroup-debug.o = -pg
|
||||
CFLAGS_REMOVE_sched_clock.o = -pg
|
||||
CFLAGS_REMOVE_sched.o = -pg
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_FREEZER) += freezer.o
|
||||
@@ -90,7 +89,7 @@ obj-$(CONFIG_FUNCTION_TRACER) += trace/
|
||||
obj-$(CONFIG_TRACING) += trace/
|
||||
obj-$(CONFIG_SMP) += sched_cpupri.o
|
||||
|
||||
ifneq ($(CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER),y)
|
||||
ifneq ($(CONFIG_SCHED_OMIT_FRAME_POINTER),y)
|
||||
# According to Alan Modra <alan@linuxcare.com.au>, the -fno-omit-frame-pointer is
|
||||
# needed for x86 only. Why this used to be enabled for all architectures is beyond
|
||||
# me. I suspect most platforms don't need this, but until we know that for sure
|
||||
|
||||
+3
-4
@@ -530,15 +530,14 @@ static void do_acct_process(struct bsd_acct_struct *acct,
|
||||
do_div(elapsed, AHZ);
|
||||
ac.ac_btime = get_seconds() - elapsed;
|
||||
/* we really need to bite the bullet and change layout */
|
||||
ac.ac_uid = current->uid;
|
||||
ac.ac_gid = current->gid;
|
||||
current_uid_gid(&ac.ac_uid, &ac.ac_gid);
|
||||
#if ACCT_VERSION==2
|
||||
ac.ac_ahz = AHZ;
|
||||
#endif
|
||||
#if ACCT_VERSION==1 || ACCT_VERSION==2
|
||||
/* backward-compatible 16 bit fields */
|
||||
ac.ac_uid16 = current->uid;
|
||||
ac.ac_gid16 = current->gid;
|
||||
ac.ac_uid16 = ac.ac_uid;
|
||||
ac.ac_gid16 = ac.ac_gid;
|
||||
#endif
|
||||
#if ACCT_VERSION==3
|
||||
ac.ac_pid = task_tgid_nr_ns(current, ns);
|
||||
|
||||
+226
-29
@@ -65,6 +65,7 @@
|
||||
#include <linux/highmem.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/inotify.h>
|
||||
#include <linux/capability.h>
|
||||
|
||||
#include "audit.h"
|
||||
|
||||
@@ -84,6 +85,15 @@ int audit_n_rules;
|
||||
/* determines whether we collect data for signals sent */
|
||||
int audit_signals;
|
||||
|
||||
struct audit_cap_data {
|
||||
kernel_cap_t permitted;
|
||||
kernel_cap_t inheritable;
|
||||
union {
|
||||
unsigned int fE; /* effective bit of a file capability */
|
||||
kernel_cap_t effective; /* effective set of a process */
|
||||
};
|
||||
};
|
||||
|
||||
/* When fs/namei.c:getname() is called, we store the pointer in name and
|
||||
* we don't let putname() free it (instead we free all of the saved
|
||||
* pointers at syscall exit time).
|
||||
@@ -100,6 +110,8 @@ struct audit_names {
|
||||
gid_t gid;
|
||||
dev_t rdev;
|
||||
u32 osid;
|
||||
struct audit_cap_data fcap;
|
||||
unsigned int fcap_ver;
|
||||
};
|
||||
|
||||
struct audit_aux_data {
|
||||
@@ -184,6 +196,20 @@ struct audit_aux_data_pids {
|
||||
int pid_count;
|
||||
};
|
||||
|
||||
struct audit_aux_data_bprm_fcaps {
|
||||
struct audit_aux_data d;
|
||||
struct audit_cap_data fcap;
|
||||
unsigned int fcap_ver;
|
||||
struct audit_cap_data old_pcap;
|
||||
struct audit_cap_data new_pcap;
|
||||
};
|
||||
|
||||
struct audit_aux_data_capset {
|
||||
struct audit_aux_data d;
|
||||
pid_t pid;
|
||||
struct audit_cap_data cap;
|
||||
};
|
||||
|
||||
struct audit_tree_refs {
|
||||
struct audit_tree_refs *next;
|
||||
struct audit_chunk *c[31];
|
||||
@@ -421,6 +447,7 @@ static int audit_filter_rules(struct task_struct *tsk,
|
||||
struct audit_names *name,
|
||||
enum audit_state *state)
|
||||
{
|
||||
const struct cred *cred = get_task_cred(tsk);
|
||||
int i, j, need_sid = 1;
|
||||
u32 sid;
|
||||
|
||||
@@ -440,28 +467,28 @@ static int audit_filter_rules(struct task_struct *tsk,
|
||||
}
|
||||
break;
|
||||
case AUDIT_UID:
|
||||
result = audit_comparator(tsk->uid, f->op, f->val);
|
||||
result = audit_comparator(cred->uid, f->op, f->val);
|
||||
break;
|
||||
case AUDIT_EUID:
|
||||
result = audit_comparator(tsk->euid, f->op, f->val);
|
||||
result = audit_comparator(cred->euid, f->op, f->val);
|
||||
break;
|
||||
case AUDIT_SUID:
|
||||
result = audit_comparator(tsk->suid, f->op, f->val);
|
||||
result = audit_comparator(cred->suid, f->op, f->val);
|
||||
break;
|
||||
case AUDIT_FSUID:
|
||||
result = audit_comparator(tsk->fsuid, f->op, f->val);
|
||||
result = audit_comparator(cred->fsuid, f->op, f->val);
|
||||
break;
|
||||
case AUDIT_GID:
|
||||
result = audit_comparator(tsk->gid, f->op, f->val);
|
||||
result = audit_comparator(cred->gid, f->op, f->val);
|
||||
break;
|
||||
case AUDIT_EGID:
|
||||
result = audit_comparator(tsk->egid, f->op, f->val);
|
||||
result = audit_comparator(cred->egid, f->op, f->val);
|
||||
break;
|
||||
case AUDIT_SGID:
|
||||
result = audit_comparator(tsk->sgid, f->op, f->val);
|
||||
result = audit_comparator(cred->sgid, f->op, f->val);
|
||||
break;
|
||||
case AUDIT_FSGID:
|
||||
result = audit_comparator(tsk->fsgid, f->op, f->val);
|
||||
result = audit_comparator(cred->fsgid, f->op, f->val);
|
||||
break;
|
||||
case AUDIT_PERS:
|
||||
result = audit_comparator(tsk->personality, f->op, f->val);
|
||||
@@ -615,8 +642,10 @@ static int audit_filter_rules(struct task_struct *tsk,
|
||||
break;
|
||||
}
|
||||
|
||||
if (!result)
|
||||
if (!result) {
|
||||
put_cred(cred);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (rule->filterkey && ctx)
|
||||
ctx->filterkey = kstrdup(rule->filterkey, GFP_ATOMIC);
|
||||
@@ -624,6 +653,7 @@ static int audit_filter_rules(struct task_struct *tsk,
|
||||
case AUDIT_NEVER: *state = AUDIT_DISABLED; break;
|
||||
case AUDIT_ALWAYS: *state = AUDIT_RECORD_CONTEXT; break;
|
||||
}
|
||||
put_cred(cred);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1171,8 +1201,38 @@ static void audit_log_execve_info(struct audit_context *context,
|
||||
kfree(buf);
|
||||
}
|
||||
|
||||
static void audit_log_cap(struct audit_buffer *ab, char *prefix, kernel_cap_t *cap)
|
||||
{
|
||||
int i;
|
||||
|
||||
audit_log_format(ab, " %s=", prefix);
|
||||
CAP_FOR_EACH_U32(i) {
|
||||
audit_log_format(ab, "%08x", cap->cap[(_KERNEL_CAPABILITY_U32S-1) - i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void audit_log_fcaps(struct audit_buffer *ab, struct audit_names *name)
|
||||
{
|
||||
kernel_cap_t *perm = &name->fcap.permitted;
|
||||
kernel_cap_t *inh = &name->fcap.inheritable;
|
||||
int log = 0;
|
||||
|
||||
if (!cap_isclear(*perm)) {
|
||||
audit_log_cap(ab, "cap_fp", perm);
|
||||
log = 1;
|
||||
}
|
||||
if (!cap_isclear(*inh)) {
|
||||
audit_log_cap(ab, "cap_fi", inh);
|
||||
log = 1;
|
||||
}
|
||||
|
||||
if (log)
|
||||
audit_log_format(ab, " cap_fe=%d cap_fver=%x", name->fcap.fE, name->fcap_ver);
|
||||
}
|
||||
|
||||
static void audit_log_exit(struct audit_context *context, struct task_struct *tsk)
|
||||
{
|
||||
const struct cred *cred;
|
||||
int i, call_panic = 0;
|
||||
struct audit_buffer *ab;
|
||||
struct audit_aux_data *aux;
|
||||
@@ -1182,14 +1242,15 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
|
||||
context->pid = tsk->pid;
|
||||
if (!context->ppid)
|
||||
context->ppid = sys_getppid();
|
||||
context->uid = tsk->uid;
|
||||
context->gid = tsk->gid;
|
||||
context->euid = tsk->euid;
|
||||
context->suid = tsk->suid;
|
||||
context->fsuid = tsk->fsuid;
|
||||
context->egid = tsk->egid;
|
||||
context->sgid = tsk->sgid;
|
||||
context->fsgid = tsk->fsgid;
|
||||
cred = current_cred();
|
||||
context->uid = cred->uid;
|
||||
context->gid = cred->gid;
|
||||
context->euid = cred->euid;
|
||||
context->suid = cred->suid;
|
||||
context->fsuid = cred->fsuid;
|
||||
context->egid = cred->egid;
|
||||
context->sgid = cred->sgid;
|
||||
context->fsgid = cred->fsgid;
|
||||
context->personality = tsk->personality;
|
||||
|
||||
ab = audit_log_start(context, GFP_KERNEL, AUDIT_SYSCALL);
|
||||
@@ -1334,6 +1395,28 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
|
||||
audit_log_format(ab, "fd0=%d fd1=%d", axs->fd[0], axs->fd[1]);
|
||||
break; }
|
||||
|
||||
case AUDIT_BPRM_FCAPS: {
|
||||
struct audit_aux_data_bprm_fcaps *axs = (void *)aux;
|
||||
audit_log_format(ab, "fver=%x", axs->fcap_ver);
|
||||
audit_log_cap(ab, "fp", &axs->fcap.permitted);
|
||||
audit_log_cap(ab, "fi", &axs->fcap.inheritable);
|
||||
audit_log_format(ab, " fe=%d", axs->fcap.fE);
|
||||
audit_log_cap(ab, "old_pp", &axs->old_pcap.permitted);
|
||||
audit_log_cap(ab, "old_pi", &axs->old_pcap.inheritable);
|
||||
audit_log_cap(ab, "old_pe", &axs->old_pcap.effective);
|
||||
audit_log_cap(ab, "new_pp", &axs->new_pcap.permitted);
|
||||
audit_log_cap(ab, "new_pi", &axs->new_pcap.inheritable);
|
||||
audit_log_cap(ab, "new_pe", &axs->new_pcap.effective);
|
||||
break; }
|
||||
|
||||
case AUDIT_CAPSET: {
|
||||
struct audit_aux_data_capset *axs = (void *)aux;
|
||||
audit_log_format(ab, "pid=%d", axs->pid);
|
||||
audit_log_cap(ab, "cap_pi", &axs->cap.inheritable);
|
||||
audit_log_cap(ab, "cap_pp", &axs->cap.permitted);
|
||||
audit_log_cap(ab, "cap_pe", &axs->cap.effective);
|
||||
break; }
|
||||
|
||||
}
|
||||
audit_log_end(ab);
|
||||
}
|
||||
@@ -1421,6 +1504,8 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
|
||||
}
|
||||
}
|
||||
|
||||
audit_log_fcaps(ab, n);
|
||||
|
||||
audit_log_end(ab);
|
||||
}
|
||||
|
||||
@@ -1802,8 +1887,36 @@ static int audit_inc_name_count(struct audit_context *context,
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static inline int audit_copy_fcaps(struct audit_names *name, const struct dentry *dentry)
|
||||
{
|
||||
struct cpu_vfs_cap_data caps;
|
||||
int rc;
|
||||
|
||||
memset(&name->fcap.permitted, 0, sizeof(kernel_cap_t));
|
||||
memset(&name->fcap.inheritable, 0, sizeof(kernel_cap_t));
|
||||
name->fcap.fE = 0;
|
||||
name->fcap_ver = 0;
|
||||
|
||||
if (!dentry)
|
||||
return 0;
|
||||
|
||||
rc = get_vfs_caps_from_disk(dentry, &caps);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
name->fcap.permitted = caps.permitted;
|
||||
name->fcap.inheritable = caps.inheritable;
|
||||
name->fcap.fE = !!(caps.magic_etc & VFS_CAP_FLAGS_EFFECTIVE);
|
||||
name->fcap_ver = (caps.magic_etc & VFS_CAP_REVISION_MASK) >> VFS_CAP_REVISION_SHIFT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Copy inode data into an audit_names. */
|
||||
static void audit_copy_inode(struct audit_names *name, const struct inode *inode)
|
||||
static void audit_copy_inode(struct audit_names *name, const struct dentry *dentry,
|
||||
const struct inode *inode)
|
||||
{
|
||||
name->ino = inode->i_ino;
|
||||
name->dev = inode->i_sb->s_dev;
|
||||
@@ -1812,6 +1925,7 @@ static void audit_copy_inode(struct audit_names *name, const struct inode *inode
|
||||
name->gid = inode->i_gid;
|
||||
name->rdev = inode->i_rdev;
|
||||
security_inode_getsecid(inode, &name->osid);
|
||||
audit_copy_fcaps(name, dentry);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1846,7 +1960,7 @@ void __audit_inode(const char *name, const struct dentry *dentry)
|
||||
context->names[idx].name = NULL;
|
||||
}
|
||||
handle_path(dentry);
|
||||
audit_copy_inode(&context->names[idx], inode);
|
||||
audit_copy_inode(&context->names[idx], dentry, inode);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1907,7 +2021,7 @@ void __audit_inode_child(const char *dname, const struct dentry *dentry,
|
||||
if (!strcmp(dname, n->name) ||
|
||||
!audit_compare_dname_path(dname, n->name, &dirlen)) {
|
||||
if (inode)
|
||||
audit_copy_inode(n, inode);
|
||||
audit_copy_inode(n, NULL, inode);
|
||||
else
|
||||
n->ino = (unsigned long)-1;
|
||||
found_child = n->name;
|
||||
@@ -1921,7 +2035,7 @@ add_names:
|
||||
return;
|
||||
idx = context->name_count - 1;
|
||||
context->names[idx].name = NULL;
|
||||
audit_copy_inode(&context->names[idx], parent);
|
||||
audit_copy_inode(&context->names[idx], NULL, parent);
|
||||
}
|
||||
|
||||
if (!found_child) {
|
||||
@@ -1942,7 +2056,7 @@ add_names:
|
||||
}
|
||||
|
||||
if (inode)
|
||||
audit_copy_inode(&context->names[idx], inode);
|
||||
audit_copy_inode(&context->names[idx], NULL, inode);
|
||||
else
|
||||
context->names[idx].ino = (unsigned long)-1;
|
||||
}
|
||||
@@ -1996,7 +2110,7 @@ int audit_set_loginuid(struct task_struct *task, uid_t loginuid)
|
||||
audit_log_format(ab, "login pid=%d uid=%u "
|
||||
"old auid=%u new auid=%u"
|
||||
" old ses=%u new ses=%u",
|
||||
task->pid, task->uid,
|
||||
task->pid, task_uid(task),
|
||||
task->loginuid, loginuid,
|
||||
task->sessionid, sessionid);
|
||||
audit_log_end(ab);
|
||||
@@ -2379,7 +2493,7 @@ void __audit_ptrace(struct task_struct *t)
|
||||
|
||||
context->target_pid = t->pid;
|
||||
context->target_auid = audit_get_loginuid(t);
|
||||
context->target_uid = t->uid;
|
||||
context->target_uid = task_uid(t);
|
||||
context->target_sessionid = audit_get_sessionid(t);
|
||||
security_task_getsecid(t, &context->target_sid);
|
||||
memcpy(context->target_comm, t->comm, TASK_COMM_LEN);
|
||||
@@ -2398,6 +2512,7 @@ int __audit_signal_info(int sig, struct task_struct *t)
|
||||
struct audit_aux_data_pids *axp;
|
||||
struct task_struct *tsk = current;
|
||||
struct audit_context *ctx = tsk->audit_context;
|
||||
uid_t uid = current_uid(), t_uid = task_uid(t);
|
||||
|
||||
if (audit_pid && t->tgid == audit_pid) {
|
||||
if (sig == SIGTERM || sig == SIGHUP || sig == SIGUSR1 || sig == SIGUSR2) {
|
||||
@@ -2405,7 +2520,7 @@ int __audit_signal_info(int sig, struct task_struct *t)
|
||||
if (tsk->loginuid != -1)
|
||||
audit_sig_uid = tsk->loginuid;
|
||||
else
|
||||
audit_sig_uid = tsk->uid;
|
||||
audit_sig_uid = uid;
|
||||
security_task_getsecid(tsk, &audit_sig_sid);
|
||||
}
|
||||
if (!audit_signals || audit_dummy_context())
|
||||
@@ -2417,7 +2532,7 @@ int __audit_signal_info(int sig, struct task_struct *t)
|
||||
if (!ctx->target_pid) {
|
||||
ctx->target_pid = t->tgid;
|
||||
ctx->target_auid = audit_get_loginuid(t);
|
||||
ctx->target_uid = t->uid;
|
||||
ctx->target_uid = t_uid;
|
||||
ctx->target_sessionid = audit_get_sessionid(t);
|
||||
security_task_getsecid(t, &ctx->target_sid);
|
||||
memcpy(ctx->target_comm, t->comm, TASK_COMM_LEN);
|
||||
@@ -2438,7 +2553,7 @@ int __audit_signal_info(int sig, struct task_struct *t)
|
||||
|
||||
axp->target_pid[axp->pid_count] = t->tgid;
|
||||
axp->target_auid[axp->pid_count] = audit_get_loginuid(t);
|
||||
axp->target_uid[axp->pid_count] = t->uid;
|
||||
axp->target_uid[axp->pid_count] = t_uid;
|
||||
axp->target_sessionid[axp->pid_count] = audit_get_sessionid(t);
|
||||
security_task_getsecid(t, &axp->target_sid[axp->pid_count]);
|
||||
memcpy(axp->target_comm[axp->pid_count], t->comm, TASK_COMM_LEN);
|
||||
@@ -2447,6 +2562,86 @@ int __audit_signal_info(int sig, struct task_struct *t)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* __audit_log_bprm_fcaps - store information about a loading bprm and relevant fcaps
|
||||
* @bprm: pointer to the bprm being processed
|
||||
* @new: the proposed new credentials
|
||||
* @old: the old credentials
|
||||
*
|
||||
* Simply check if the proc already has the caps given by the file and if not
|
||||
* store the priv escalation info for later auditing at the end of the syscall
|
||||
*
|
||||
* -Eric
|
||||
*/
|
||||
int __audit_log_bprm_fcaps(struct linux_binprm *bprm,
|
||||
const struct cred *new, const struct cred *old)
|
||||
{
|
||||
struct audit_aux_data_bprm_fcaps *ax;
|
||||
struct audit_context *context = current->audit_context;
|
||||
struct cpu_vfs_cap_data vcaps;
|
||||
struct dentry *dentry;
|
||||
|
||||
ax = kmalloc(sizeof(*ax), GFP_KERNEL);
|
||||
if (!ax)
|
||||
return -ENOMEM;
|
||||
|
||||
ax->d.type = AUDIT_BPRM_FCAPS;
|
||||
ax->d.next = context->aux;
|
||||
context->aux = (void *)ax;
|
||||
|
||||
dentry = dget(bprm->file->f_dentry);
|
||||
get_vfs_caps_from_disk(dentry, &vcaps);
|
||||
dput(dentry);
|
||||
|
||||
ax->fcap.permitted = vcaps.permitted;
|
||||
ax->fcap.inheritable = vcaps.inheritable;
|
||||
ax->fcap.fE = !!(vcaps.magic_etc & VFS_CAP_FLAGS_EFFECTIVE);
|
||||
ax->fcap_ver = (vcaps.magic_etc & VFS_CAP_REVISION_MASK) >> VFS_CAP_REVISION_SHIFT;
|
||||
|
||||
ax->old_pcap.permitted = old->cap_permitted;
|
||||
ax->old_pcap.inheritable = old->cap_inheritable;
|
||||
ax->old_pcap.effective = old->cap_effective;
|
||||
|
||||
ax->new_pcap.permitted = new->cap_permitted;
|
||||
ax->new_pcap.inheritable = new->cap_inheritable;
|
||||
ax->new_pcap.effective = new->cap_effective;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* __audit_log_capset - store information about the arguments to the capset syscall
|
||||
* @pid: target pid of the capset call
|
||||
* @new: the new credentials
|
||||
* @old: the old (current) credentials
|
||||
*
|
||||
* Record the aguments userspace sent to sys_capset for later printing by the
|
||||
* audit system if applicable
|
||||
*/
|
||||
int __audit_log_capset(pid_t pid,
|
||||
const struct cred *new, const struct cred *old)
|
||||
{
|
||||
struct audit_aux_data_capset *ax;
|
||||
struct audit_context *context = current->audit_context;
|
||||
|
||||
if (likely(!audit_enabled || !context || context->dummy))
|
||||
return 0;
|
||||
|
||||
ax = kmalloc(sizeof(*ax), GFP_KERNEL);
|
||||
if (!ax)
|
||||
return -ENOMEM;
|
||||
|
||||
ax->d.type = AUDIT_CAPSET;
|
||||
ax->d.next = context->aux;
|
||||
context->aux = (void *)ax;
|
||||
|
||||
ax->pid = pid;
|
||||
ax->cap.effective = new->cap_effective;
|
||||
ax->cap.inheritable = new->cap_effective;
|
||||
ax->cap.permitted = new->cap_permitted;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* audit_core_dumps - record information about processes that end abnormally
|
||||
* @signr: signal value
|
||||
@@ -2458,7 +2653,8 @@ void audit_core_dumps(long signr)
|
||||
{
|
||||
struct audit_buffer *ab;
|
||||
u32 sid;
|
||||
uid_t auid = audit_get_loginuid(current);
|
||||
uid_t auid = audit_get_loginuid(current), uid;
|
||||
gid_t gid;
|
||||
unsigned int sessionid = audit_get_sessionid(current);
|
||||
|
||||
if (!audit_enabled)
|
||||
@@ -2468,8 +2664,9 @@ void audit_core_dumps(long signr)
|
||||
return;
|
||||
|
||||
ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_ANOM_ABEND);
|
||||
current_uid_gid(&uid, &gid);
|
||||
audit_log_format(ab, "auid=%u uid=%u gid=%u ses=%u",
|
||||
auid, current->uid, current->gid, sessionid);
|
||||
auid, uid, gid, sessionid);
|
||||
security_task_getsecid(current, &sid);
|
||||
if (sid) {
|
||||
char *ctx = NULL;
|
||||
|
||||
+49
-239
@@ -7,6 +7,7 @@
|
||||
* 30 May 2002: Cleanup, Robert M. Love <rml@tech9.net>
|
||||
*/
|
||||
|
||||
#include <linux/audit.h>
|
||||
#include <linux/capability.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/module.h>
|
||||
@@ -14,12 +15,7 @@
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/pid_namespace.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
/*
|
||||
* This lock protects task->cap_* for all tasks including current.
|
||||
* Locking rule: acquire this prior to tasklist_lock.
|
||||
*/
|
||||
static DEFINE_SPINLOCK(task_capability_lock);
|
||||
#include "cred-internals.h"
|
||||
|
||||
/*
|
||||
* Leveraged for setting/resetting capabilities
|
||||
@@ -33,6 +29,17 @@ EXPORT_SYMBOL(__cap_empty_set);
|
||||
EXPORT_SYMBOL(__cap_full_set);
|
||||
EXPORT_SYMBOL(__cap_init_eff_set);
|
||||
|
||||
#ifdef CONFIG_SECURITY_FILE_CAPABILITIES
|
||||
int file_caps_enabled = 1;
|
||||
|
||||
static int __init file_caps_disable(char *str)
|
||||
{
|
||||
file_caps_enabled = 0;
|
||||
return 1;
|
||||
}
|
||||
__setup("no_file_caps", file_caps_disable);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* More recent versions of libcap are available from:
|
||||
*
|
||||
@@ -115,167 +122,12 @@ static int cap_validate_magic(cap_user_header_t header, unsigned *tocopy)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_SECURITY_FILE_CAPABILITIES
|
||||
|
||||
/*
|
||||
* Without filesystem capability support, we nominally support one process
|
||||
* setting the capabilities of another
|
||||
*/
|
||||
static inline int cap_get_target_pid(pid_t pid, kernel_cap_t *pEp,
|
||||
kernel_cap_t *pIp, kernel_cap_t *pPp)
|
||||
{
|
||||
struct task_struct *target;
|
||||
int ret;
|
||||
|
||||
spin_lock(&task_capability_lock);
|
||||
read_lock(&tasklist_lock);
|
||||
|
||||
if (pid && pid != task_pid_vnr(current)) {
|
||||
target = find_task_by_vpid(pid);
|
||||
if (!target) {
|
||||
ret = -ESRCH;
|
||||
goto out;
|
||||
}
|
||||
} else
|
||||
target = current;
|
||||
|
||||
ret = security_capget(target, pEp, pIp, pPp);
|
||||
|
||||
out:
|
||||
read_unlock(&tasklist_lock);
|
||||
spin_unlock(&task_capability_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* cap_set_pg - set capabilities for all processes in a given process
|
||||
* group. We call this holding task_capability_lock and tasklist_lock.
|
||||
*/
|
||||
static inline int cap_set_pg(int pgrp_nr, kernel_cap_t *effective,
|
||||
kernel_cap_t *inheritable,
|
||||
kernel_cap_t *permitted)
|
||||
{
|
||||
struct task_struct *g, *target;
|
||||
int ret = -EPERM;
|
||||
int found = 0;
|
||||
struct pid *pgrp;
|
||||
|
||||
spin_lock(&task_capability_lock);
|
||||
read_lock(&tasklist_lock);
|
||||
|
||||
pgrp = find_vpid(pgrp_nr);
|
||||
do_each_pid_task(pgrp, PIDTYPE_PGID, g) {
|
||||
target = g;
|
||||
while_each_thread(g, target) {
|
||||
if (!security_capset_check(target, effective,
|
||||
inheritable, permitted)) {
|
||||
security_capset_set(target, effective,
|
||||
inheritable, permitted);
|
||||
ret = 0;
|
||||
}
|
||||
found = 1;
|
||||
}
|
||||
} while_each_pid_task(pgrp, PIDTYPE_PGID, g);
|
||||
|
||||
read_unlock(&tasklist_lock);
|
||||
spin_unlock(&task_capability_lock);
|
||||
|
||||
if (!found)
|
||||
ret = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* cap_set_all - set capabilities for all processes other than init
|
||||
* and self. We call this holding task_capability_lock and tasklist_lock.
|
||||
*/
|
||||
static inline int cap_set_all(kernel_cap_t *effective,
|
||||
kernel_cap_t *inheritable,
|
||||
kernel_cap_t *permitted)
|
||||
{
|
||||
struct task_struct *g, *target;
|
||||
int ret = -EPERM;
|
||||
int found = 0;
|
||||
|
||||
spin_lock(&task_capability_lock);
|
||||
read_lock(&tasklist_lock);
|
||||
|
||||
do_each_thread(g, target) {
|
||||
if (target == current
|
||||
|| is_container_init(target->group_leader))
|
||||
continue;
|
||||
found = 1;
|
||||
if (security_capset_check(target, effective, inheritable,
|
||||
permitted))
|
||||
continue;
|
||||
ret = 0;
|
||||
security_capset_set(target, effective, inheritable, permitted);
|
||||
} while_each_thread(g, target);
|
||||
|
||||
read_unlock(&tasklist_lock);
|
||||
spin_unlock(&task_capability_lock);
|
||||
|
||||
if (!found)
|
||||
ret = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Given the target pid does not refer to the current process we
|
||||
* need more elaborate support... (This support is not present when
|
||||
* filesystem capabilities are configured.)
|
||||
*/
|
||||
static inline int do_sys_capset_other_tasks(pid_t pid, kernel_cap_t *effective,
|
||||
kernel_cap_t *inheritable,
|
||||
kernel_cap_t *permitted)
|
||||
{
|
||||
struct task_struct *target;
|
||||
int ret;
|
||||
|
||||
if (!capable(CAP_SETPCAP))
|
||||
return -EPERM;
|
||||
|
||||
if (pid == -1) /* all procs other than current and init */
|
||||
return cap_set_all(effective, inheritable, permitted);
|
||||
|
||||
else if (pid < 0) /* all procs in process group */
|
||||
return cap_set_pg(-pid, effective, inheritable, permitted);
|
||||
|
||||
/* target != current */
|
||||
spin_lock(&task_capability_lock);
|
||||
read_lock(&tasklist_lock);
|
||||
|
||||
target = find_task_by_vpid(pid);
|
||||
if (!target)
|
||||
ret = -ESRCH;
|
||||
else {
|
||||
ret = security_capset_check(target, effective, inheritable,
|
||||
permitted);
|
||||
|
||||
/* having verified that the proposed changes are legal,
|
||||
we now put them into effect. */
|
||||
if (!ret)
|
||||
security_capset_set(target, effective, inheritable,
|
||||
permitted);
|
||||
}
|
||||
|
||||
read_unlock(&tasklist_lock);
|
||||
spin_unlock(&task_capability_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#else /* ie., def CONFIG_SECURITY_FILE_CAPABILITIES */
|
||||
|
||||
/*
|
||||
* If we have configured with filesystem capability support, then the
|
||||
* only thing that can change the capabilities of the current process
|
||||
* is the current process. As such, we can't be in this code at the
|
||||
* same time as we are in the process of setting capabilities in this
|
||||
* process. The net result is that we can limit our use of locks to
|
||||
* when we are reading the caps of another process.
|
||||
* The only thing that can change the capabilities of the current
|
||||
* process is the current process. As such, we can't be in this code
|
||||
* at the same time as we are in the process of setting capabilities
|
||||
* in this process. The net result is that we can limit our use of
|
||||
* locks to when we are reading the caps of another process.
|
||||
*/
|
||||
static inline int cap_get_target_pid(pid_t pid, kernel_cap_t *pEp,
|
||||
kernel_cap_t *pIp, kernel_cap_t *pPp)
|
||||
@@ -285,7 +137,6 @@ static inline int cap_get_target_pid(pid_t pid, kernel_cap_t *pEp,
|
||||
if (pid && (pid != task_pid_vnr(current))) {
|
||||
struct task_struct *target;
|
||||
|
||||
spin_lock(&task_capability_lock);
|
||||
read_lock(&tasklist_lock);
|
||||
|
||||
target = find_task_by_vpid(pid);
|
||||
@@ -295,50 +146,12 @@ static inline int cap_get_target_pid(pid_t pid, kernel_cap_t *pEp,
|
||||
ret = security_capget(target, pEp, pIp, pPp);
|
||||
|
||||
read_unlock(&tasklist_lock);
|
||||
spin_unlock(&task_capability_lock);
|
||||
} else
|
||||
ret = security_capget(current, pEp, pIp, pPp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* With filesystem capability support configured, the kernel does not
|
||||
* permit the changing of capabilities in one process by another
|
||||
* process. (CAP_SETPCAP has much less broad semantics when configured
|
||||
* this way.)
|
||||
*/
|
||||
static inline int do_sys_capset_other_tasks(pid_t pid,
|
||||
kernel_cap_t *effective,
|
||||
kernel_cap_t *inheritable,
|
||||
kernel_cap_t *permitted)
|
||||
{
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
#endif /* ie., ndef CONFIG_SECURITY_FILE_CAPABILITIES */
|
||||
|
||||
/*
|
||||
* Atomically modify the effective capabilities returning the original
|
||||
* value. No permission check is performed here - it is assumed that the
|
||||
* caller is permitted to set the desired effective capabilities.
|
||||
*/
|
||||
kernel_cap_t cap_set_effective(const kernel_cap_t pE_new)
|
||||
{
|
||||
kernel_cap_t pE_old;
|
||||
|
||||
spin_lock(&task_capability_lock);
|
||||
|
||||
pE_old = current->cap_effective;
|
||||
current->cap_effective = pE_new;
|
||||
|
||||
spin_unlock(&task_capability_lock);
|
||||
|
||||
return pE_old;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(cap_set_effective);
|
||||
|
||||
/**
|
||||
* sys_capget - get the capabilities of a given process.
|
||||
* @header: pointer to struct that contains capability version and
|
||||
@@ -366,7 +179,6 @@ asmlinkage long sys_capget(cap_user_header_t header, cap_user_data_t dataptr)
|
||||
return -EINVAL;
|
||||
|
||||
ret = cap_get_target_pid(pid, &pE, &pI, &pP);
|
||||
|
||||
if (!ret) {
|
||||
struct __user_cap_data_struct kdata[_KERNEL_CAPABILITY_U32S];
|
||||
unsigned i;
|
||||
@@ -412,16 +224,14 @@ asmlinkage long sys_capget(cap_user_header_t header, cap_user_data_t dataptr)
|
||||
* @data: pointer to struct that contains the effective, permitted,
|
||||
* and inheritable capabilities
|
||||
*
|
||||
* Set capabilities for a given process, all processes, or all
|
||||
* processes in a given process group.
|
||||
* Set capabilities for the current process only. The ability to any other
|
||||
* process(es) has been deprecated and removed.
|
||||
*
|
||||
* The restrictions on setting capabilities are specified as:
|
||||
*
|
||||
* [pid is for the 'target' task. 'current' is the calling task.]
|
||||
*
|
||||
* I: any raised capabilities must be a subset of the (old current) permitted
|
||||
* P: any raised capabilities must be a subset of the (old current) permitted
|
||||
* E: must be set to a subset of (new target) permitted
|
||||
* I: any raised capabilities must be a subset of the old permitted
|
||||
* P: any raised capabilities must be a subset of the old permitted
|
||||
* E: must be set to a subset of new permitted
|
||||
*
|
||||
* Returns 0 on success and < 0 on error.
|
||||
*/
|
||||
@@ -430,6 +240,7 @@ asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data)
|
||||
struct __user_cap_data_struct kdata[_KERNEL_CAPABILITY_U32S];
|
||||
unsigned i, tocopy;
|
||||
kernel_cap_t inheritable, permitted, effective;
|
||||
struct cred *new;
|
||||
int ret;
|
||||
pid_t pid;
|
||||
|
||||
@@ -440,10 +251,13 @@ asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data)
|
||||
if (get_user(pid, &header->pid))
|
||||
return -EFAULT;
|
||||
|
||||
if (copy_from_user(&kdata, data, tocopy
|
||||
* sizeof(struct __user_cap_data_struct))) {
|
||||
/* may only affect current now */
|
||||
if (pid != 0 && pid != task_pid_vnr(current))
|
||||
return -EPERM;
|
||||
|
||||
if (copy_from_user(&kdata, data,
|
||||
tocopy * sizeof(struct __user_cap_data_struct)))
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
for (i = 0; i < tocopy; i++) {
|
||||
effective.cap[i] = kdata[i].effective;
|
||||
@@ -457,32 +271,23 @@ asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data)
|
||||
i++;
|
||||
}
|
||||
|
||||
if (pid && (pid != task_pid_vnr(current)))
|
||||
ret = do_sys_capset_other_tasks(pid, &effective, &inheritable,
|
||||
&permitted);
|
||||
else {
|
||||
/*
|
||||
* This lock is required even when filesystem
|
||||
* capability support is configured - it protects the
|
||||
* sys_capget() call from returning incorrect data in
|
||||
* the case that the targeted process is not the
|
||||
* current one.
|
||||
*/
|
||||
spin_lock(&task_capability_lock);
|
||||
new = prepare_creds();
|
||||
if (!new)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = security_capset_check(current, &effective, &inheritable,
|
||||
&permitted);
|
||||
/*
|
||||
* Having verified that the proposed changes are
|
||||
* legal, we now put them into effect.
|
||||
*/
|
||||
if (!ret)
|
||||
security_capset_set(current, &effective, &inheritable,
|
||||
&permitted);
|
||||
spin_unlock(&task_capability_lock);
|
||||
}
|
||||
ret = security_capset(new, current_cred(),
|
||||
&effective, &inheritable, &permitted);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
ret = audit_log_capset(pid, new, current_cred());
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return commit_creds(new);
|
||||
|
||||
error:
|
||||
abort_creds(new);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -498,6 +303,11 @@ asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data)
|
||||
*/
|
||||
int capable(int cap)
|
||||
{
|
||||
if (unlikely(!cap_valid(cap))) {
|
||||
printk(KERN_CRIT "capable() called with invalid cap=%u\n", cap);
|
||||
BUG();
|
||||
}
|
||||
|
||||
if (has_capability(current, cap)) {
|
||||
current->flags |= PF_SUPERPRIV;
|
||||
return 1;
|
||||
|
||||
+14
-13
@@ -571,8 +571,8 @@ static struct inode *cgroup_new_inode(mode_t mode, struct super_block *sb)
|
||||
|
||||
if (inode) {
|
||||
inode->i_mode = mode;
|
||||
inode->i_uid = current->fsuid;
|
||||
inode->i_gid = current->fsgid;
|
||||
inode->i_uid = current_fsuid();
|
||||
inode->i_gid = current_fsgid();
|
||||
inode->i_blocks = 0;
|
||||
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
|
||||
inode->i_mapping->backing_dev_info = &cgroup_backing_dev_info;
|
||||
@@ -702,7 +702,7 @@ static int rebind_subsystems(struct cgroupfs_root *root,
|
||||
* any child cgroups exist. This is theoretically supportable
|
||||
* but involves complex error handling, so it's being left until
|
||||
* later */
|
||||
if (!list_empty(&cgrp->children))
|
||||
if (root->number_of_cgroups > 1)
|
||||
return -EBUSY;
|
||||
|
||||
/* Process each subsystem */
|
||||
@@ -1024,7 +1024,7 @@ static int cgroup_get_sb(struct file_system_type *fs_type,
|
||||
if (ret == -EBUSY) {
|
||||
mutex_unlock(&cgroup_mutex);
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
goto drop_new_super;
|
||||
goto free_cg_links;
|
||||
}
|
||||
|
||||
/* EBUSY should be the only error here */
|
||||
@@ -1073,10 +1073,11 @@ static int cgroup_get_sb(struct file_system_type *fs_type,
|
||||
|
||||
return simple_set_mnt(mnt, sb);
|
||||
|
||||
free_cg_links:
|
||||
free_cg_links(&tmp_cg_links);
|
||||
drop_new_super:
|
||||
up_write(&sb->s_umount);
|
||||
deactivate_super(sb);
|
||||
free_cg_links(&tmp_cg_links);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1279,6 +1280,7 @@ int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk)
|
||||
static int attach_task_by_pid(struct cgroup *cgrp, u64 pid)
|
||||
{
|
||||
struct task_struct *tsk;
|
||||
const struct cred *cred = current_cred(), *tcred;
|
||||
int ret;
|
||||
|
||||
if (pid) {
|
||||
@@ -1288,14 +1290,16 @@ static int attach_task_by_pid(struct cgroup *cgrp, u64 pid)
|
||||
rcu_read_unlock();
|
||||
return -ESRCH;
|
||||
}
|
||||
get_task_struct(tsk);
|
||||
rcu_read_unlock();
|
||||
|
||||
if ((current->euid) && (current->euid != tsk->uid)
|
||||
&& (current->euid != tsk->suid)) {
|
||||
put_task_struct(tsk);
|
||||
tcred = __task_cred(tsk);
|
||||
if (cred->euid &&
|
||||
cred->euid != tcred->uid &&
|
||||
cred->euid != tcred->suid) {
|
||||
rcu_read_unlock();
|
||||
return -EACCES;
|
||||
}
|
||||
get_task_struct(tsk);
|
||||
rcu_read_unlock();
|
||||
} else {
|
||||
tsk = current;
|
||||
get_task_struct(tsk);
|
||||
@@ -2934,9 +2938,6 @@ int cgroup_clone(struct task_struct *tsk, struct cgroup_subsys *subsys,
|
||||
again:
|
||||
root = subsys->root;
|
||||
if (root == &rootnode) {
|
||||
printk(KERN_INFO
|
||||
"Not cloning cgroup for unused subsystem %s\n",
|
||||
subsys->name);
|
||||
mutex_unlock(&cgroup_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/* Internal credentials stuff
|
||||
*
|
||||
* Copyright (C) 2008 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public Licence
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the Licence, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
/*
|
||||
* user.c
|
||||
*/
|
||||
static inline void sched_switch_user(struct task_struct *p)
|
||||
{
|
||||
#ifdef CONFIG_USER_SCHED
|
||||
sched_move_task(p);
|
||||
#endif /* CONFIG_USER_SCHED */
|
||||
}
|
||||
|
||||
+588
@@ -0,0 +1,588 @@
|
||||
/* Task credentials management - see Documentation/credentials.txt
|
||||
*
|
||||
* Copyright (C) 2008 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public Licence
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the Licence, or (at your option) any later version.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/cred.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/key.h>
|
||||
#include <linux/keyctl.h>
|
||||
#include <linux/init_task.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/cn_proc.h>
|
||||
#include "cred-internals.h"
|
||||
|
||||
static struct kmem_cache *cred_jar;
|
||||
|
||||
/*
|
||||
* The common credentials for the initial task's thread group
|
||||
*/
|
||||
#ifdef CONFIG_KEYS
|
||||
static struct thread_group_cred init_tgcred = {
|
||||
.usage = ATOMIC_INIT(2),
|
||||
.tgid = 0,
|
||||
.lock = SPIN_LOCK_UNLOCKED,
|
||||
};
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The initial credentials for the initial task
|
||||
*/
|
||||
struct cred init_cred = {
|
||||
.usage = ATOMIC_INIT(4),
|
||||
.securebits = SECUREBITS_DEFAULT,
|
||||
.cap_inheritable = CAP_INIT_INH_SET,
|
||||
.cap_permitted = CAP_FULL_SET,
|
||||
.cap_effective = CAP_INIT_EFF_SET,
|
||||
.cap_bset = CAP_INIT_BSET,
|
||||
.user = INIT_USER,
|
||||
.group_info = &init_groups,
|
||||
#ifdef CONFIG_KEYS
|
||||
.tgcred = &init_tgcred,
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
* Dispose of the shared task group credentials
|
||||
*/
|
||||
#ifdef CONFIG_KEYS
|
||||
static void release_tgcred_rcu(struct rcu_head *rcu)
|
||||
{
|
||||
struct thread_group_cred *tgcred =
|
||||
container_of(rcu, struct thread_group_cred, rcu);
|
||||
|
||||
BUG_ON(atomic_read(&tgcred->usage) != 0);
|
||||
|
||||
key_put(tgcred->session_keyring);
|
||||
key_put(tgcred->process_keyring);
|
||||
kfree(tgcred);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Release a set of thread group credentials.
|
||||
*/
|
||||
static void release_tgcred(struct cred *cred)
|
||||
{
|
||||
#ifdef CONFIG_KEYS
|
||||
struct thread_group_cred *tgcred = cred->tgcred;
|
||||
|
||||
if (atomic_dec_and_test(&tgcred->usage))
|
||||
call_rcu(&tgcred->rcu, release_tgcred_rcu);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* The RCU callback to actually dispose of a set of credentials
|
||||
*/
|
||||
static void put_cred_rcu(struct rcu_head *rcu)
|
||||
{
|
||||
struct cred *cred = container_of(rcu, struct cred, rcu);
|
||||
|
||||
if (atomic_read(&cred->usage) != 0)
|
||||
panic("CRED: put_cred_rcu() sees %p with usage %d\n",
|
||||
cred, atomic_read(&cred->usage));
|
||||
|
||||
security_cred_free(cred);
|
||||
key_put(cred->thread_keyring);
|
||||
key_put(cred->request_key_auth);
|
||||
release_tgcred(cred);
|
||||
put_group_info(cred->group_info);
|
||||
free_uid(cred->user);
|
||||
kmem_cache_free(cred_jar, cred);
|
||||
}
|
||||
|
||||
/**
|
||||
* __put_cred - Destroy a set of credentials
|
||||
* @cred: The record to release
|
||||
*
|
||||
* Destroy a set of credentials on which no references remain.
|
||||
*/
|
||||
void __put_cred(struct cred *cred)
|
||||
{
|
||||
BUG_ON(atomic_read(&cred->usage) != 0);
|
||||
|
||||
call_rcu(&cred->rcu, put_cred_rcu);
|
||||
}
|
||||
EXPORT_SYMBOL(__put_cred);
|
||||
|
||||
/**
|
||||
* prepare_creds - Prepare a new set of credentials for modification
|
||||
*
|
||||
* Prepare a new set of task credentials for modification. A task's creds
|
||||
* shouldn't generally be modified directly, therefore this function is used to
|
||||
* prepare a new copy, which the caller then modifies and then commits by
|
||||
* calling commit_creds().
|
||||
*
|
||||
* Preparation involves making a copy of the objective creds for modification.
|
||||
*
|
||||
* Returns a pointer to the new creds-to-be if successful, NULL otherwise.
|
||||
*
|
||||
* Call commit_creds() or abort_creds() to clean up.
|
||||
*/
|
||||
struct cred *prepare_creds(void)
|
||||
{
|
||||
struct task_struct *task = current;
|
||||
const struct cred *old;
|
||||
struct cred *new;
|
||||
|
||||
BUG_ON(atomic_read(&task->real_cred->usage) < 1);
|
||||
|
||||
new = kmem_cache_alloc(cred_jar, GFP_KERNEL);
|
||||
if (!new)
|
||||
return NULL;
|
||||
|
||||
old = task->cred;
|
||||
memcpy(new, old, sizeof(struct cred));
|
||||
|
||||
atomic_set(&new->usage, 1);
|
||||
get_group_info(new->group_info);
|
||||
get_uid(new->user);
|
||||
|
||||
#ifdef CONFIG_KEYS
|
||||
key_get(new->thread_keyring);
|
||||
key_get(new->request_key_auth);
|
||||
atomic_inc(&new->tgcred->usage);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SECURITY
|
||||
new->security = NULL;
|
||||
#endif
|
||||
|
||||
if (security_prepare_creds(new, old, GFP_KERNEL) < 0)
|
||||
goto error;
|
||||
return new;
|
||||
|
||||
error:
|
||||
abort_creds(new);
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(prepare_creds);
|
||||
|
||||
/*
|
||||
* Prepare credentials for current to perform an execve()
|
||||
* - The caller must hold current->cred_exec_mutex
|
||||
*/
|
||||
struct cred *prepare_exec_creds(void)
|
||||
{
|
||||
struct thread_group_cred *tgcred = NULL;
|
||||
struct cred *new;
|
||||
|
||||
#ifdef CONFIG_KEYS
|
||||
tgcred = kmalloc(sizeof(*tgcred), GFP_KERNEL);
|
||||
if (!tgcred)
|
||||
return NULL;
|
||||
#endif
|
||||
|
||||
new = prepare_creds();
|
||||
if (!new) {
|
||||
kfree(tgcred);
|
||||
return new;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KEYS
|
||||
/* newly exec'd tasks don't get a thread keyring */
|
||||
key_put(new->thread_keyring);
|
||||
new->thread_keyring = NULL;
|
||||
|
||||
/* create a new per-thread-group creds for all this set of threads to
|
||||
* share */
|
||||
memcpy(tgcred, new->tgcred, sizeof(struct thread_group_cred));
|
||||
|
||||
atomic_set(&tgcred->usage, 1);
|
||||
spin_lock_init(&tgcred->lock);
|
||||
|
||||
/* inherit the session keyring; new process keyring */
|
||||
key_get(tgcred->session_keyring);
|
||||
tgcred->process_keyring = NULL;
|
||||
|
||||
release_tgcred(new);
|
||||
new->tgcred = tgcred;
|
||||
#endif
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
/*
|
||||
* prepare new credentials for the usermode helper dispatcher
|
||||
*/
|
||||
struct cred *prepare_usermodehelper_creds(void)
|
||||
{
|
||||
#ifdef CONFIG_KEYS
|
||||
struct thread_group_cred *tgcred = NULL;
|
||||
#endif
|
||||
struct cred *new;
|
||||
|
||||
#ifdef CONFIG_KEYS
|
||||
tgcred = kzalloc(sizeof(*new->tgcred), GFP_ATOMIC);
|
||||
if (!tgcred)
|
||||
return NULL;
|
||||
#endif
|
||||
|
||||
new = kmem_cache_alloc(cred_jar, GFP_ATOMIC);
|
||||
if (!new)
|
||||
return NULL;
|
||||
|
||||
memcpy(new, &init_cred, sizeof(struct cred));
|
||||
|
||||
atomic_set(&new->usage, 1);
|
||||
get_group_info(new->group_info);
|
||||
get_uid(new->user);
|
||||
|
||||
#ifdef CONFIG_KEYS
|
||||
new->thread_keyring = NULL;
|
||||
new->request_key_auth = NULL;
|
||||
new->jit_keyring = KEY_REQKEY_DEFL_DEFAULT;
|
||||
|
||||
atomic_set(&tgcred->usage, 1);
|
||||
spin_lock_init(&tgcred->lock);
|
||||
new->tgcred = tgcred;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SECURITY
|
||||
new->security = NULL;
|
||||
#endif
|
||||
if (security_prepare_creds(new, &init_cred, GFP_ATOMIC) < 0)
|
||||
goto error;
|
||||
|
||||
BUG_ON(atomic_read(&new->usage) != 1);
|
||||
return new;
|
||||
|
||||
error:
|
||||
put_cred(new);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy credentials for the new process created by fork()
|
||||
*
|
||||
* We share if we can, but under some circumstances we have to generate a new
|
||||
* set.
|
||||
*
|
||||
* The new process gets the current process's subjective credentials as its
|
||||
* objective and subjective credentials
|
||||
*/
|
||||
int copy_creds(struct task_struct *p, unsigned long clone_flags)
|
||||
{
|
||||
#ifdef CONFIG_KEYS
|
||||
struct thread_group_cred *tgcred;
|
||||
#endif
|
||||
struct cred *new;
|
||||
int ret;
|
||||
|
||||
mutex_init(&p->cred_exec_mutex);
|
||||
|
||||
if (
|
||||
#ifdef CONFIG_KEYS
|
||||
!p->cred->thread_keyring &&
|
||||
#endif
|
||||
clone_flags & CLONE_THREAD
|
||||
) {
|
||||
p->real_cred = get_cred(p->cred);
|
||||
get_cred(p->cred);
|
||||
atomic_inc(&p->cred->user->processes);
|
||||
return 0;
|
||||
}
|
||||
|
||||
new = prepare_creds();
|
||||
if (!new)
|
||||
return -ENOMEM;
|
||||
|
||||
if (clone_flags & CLONE_NEWUSER) {
|
||||
ret = create_user_ns(new);
|
||||
if (ret < 0)
|
||||
goto error_put;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KEYS
|
||||
/* new threads get their own thread keyrings if their parent already
|
||||
* had one */
|
||||
if (new->thread_keyring) {
|
||||
key_put(new->thread_keyring);
|
||||
new->thread_keyring = NULL;
|
||||
if (clone_flags & CLONE_THREAD)
|
||||
install_thread_keyring_to_cred(new);
|
||||
}
|
||||
|
||||
/* we share the process and session keyrings between all the threads in
|
||||
* a process - this is slightly icky as we violate COW credentials a
|
||||
* bit */
|
||||
if (!(clone_flags & CLONE_THREAD)) {
|
||||
tgcred = kmalloc(sizeof(*tgcred), GFP_KERNEL);
|
||||
if (!tgcred) {
|
||||
ret = -ENOMEM;
|
||||
goto error_put;
|
||||
}
|
||||
atomic_set(&tgcred->usage, 1);
|
||||
spin_lock_init(&tgcred->lock);
|
||||
tgcred->process_keyring = NULL;
|
||||
tgcred->session_keyring = key_get(new->tgcred->session_keyring);
|
||||
|
||||
release_tgcred(new);
|
||||
new->tgcred = tgcred;
|
||||
}
|
||||
#endif
|
||||
|
||||
atomic_inc(&new->user->processes);
|
||||
p->cred = p->real_cred = get_cred(new);
|
||||
return 0;
|
||||
|
||||
error_put:
|
||||
put_cred(new);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* commit_creds - Install new credentials upon the current task
|
||||
* @new: The credentials to be assigned
|
||||
*
|
||||
* Install a new set of credentials to the current task, using RCU to replace
|
||||
* the old set. Both the objective and the subjective credentials pointers are
|
||||
* updated. This function may not be called if the subjective credentials are
|
||||
* in an overridden state.
|
||||
*
|
||||
* This function eats the caller's reference to the new credentials.
|
||||
*
|
||||
* Always returns 0 thus allowing this function to be tail-called at the end
|
||||
* of, say, sys_setgid().
|
||||
*/
|
||||
int commit_creds(struct cred *new)
|
||||
{
|
||||
struct task_struct *task = current;
|
||||
const struct cred *old;
|
||||
|
||||
BUG_ON(task->cred != task->real_cred);
|
||||
BUG_ON(atomic_read(&task->real_cred->usage) < 2);
|
||||
BUG_ON(atomic_read(&new->usage) < 1);
|
||||
|
||||
old = task->real_cred;
|
||||
security_commit_creds(new, old);
|
||||
|
||||
get_cred(new); /* we will require a ref for the subj creds too */
|
||||
|
||||
/* dumpability changes */
|
||||
if (old->euid != new->euid ||
|
||||
old->egid != new->egid ||
|
||||
old->fsuid != new->fsuid ||
|
||||
old->fsgid != new->fsgid ||
|
||||
!cap_issubset(new->cap_permitted, old->cap_permitted)) {
|
||||
set_dumpable(task->mm, suid_dumpable);
|
||||
task->pdeath_signal = 0;
|
||||
smp_wmb();
|
||||
}
|
||||
|
||||
/* alter the thread keyring */
|
||||
if (new->fsuid != old->fsuid)
|
||||
key_fsuid_changed(task);
|
||||
if (new->fsgid != old->fsgid)
|
||||
key_fsgid_changed(task);
|
||||
|
||||
/* do it
|
||||
* - What if a process setreuid()'s and this brings the
|
||||
* new uid over his NPROC rlimit? We can check this now
|
||||
* cheaply with the new uid cache, so if it matters
|
||||
* we should be checking for it. -DaveM
|
||||
*/
|
||||
if (new->user != old->user)
|
||||
atomic_inc(&new->user->processes);
|
||||
rcu_assign_pointer(task->real_cred, new);
|
||||
rcu_assign_pointer(task->cred, new);
|
||||
if (new->user != old->user)
|
||||
atomic_dec(&old->user->processes);
|
||||
|
||||
sched_switch_user(task);
|
||||
|
||||
/* send notifications */
|
||||
if (new->uid != old->uid ||
|
||||
new->euid != old->euid ||
|
||||
new->suid != old->suid ||
|
||||
new->fsuid != old->fsuid)
|
||||
proc_id_connector(task, PROC_EVENT_UID);
|
||||
|
||||
if (new->gid != old->gid ||
|
||||
new->egid != old->egid ||
|
||||
new->sgid != old->sgid ||
|
||||
new->fsgid != old->fsgid)
|
||||
proc_id_connector(task, PROC_EVENT_GID);
|
||||
|
||||
/* release the old obj and subj refs both */
|
||||
put_cred(old);
|
||||
put_cred(old);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(commit_creds);
|
||||
|
||||
/**
|
||||
* abort_creds - Discard a set of credentials and unlock the current task
|
||||
* @new: The credentials that were going to be applied
|
||||
*
|
||||
* Discard a set of credentials that were under construction and unlock the
|
||||
* current task.
|
||||
*/
|
||||
void abort_creds(struct cred *new)
|
||||
{
|
||||
BUG_ON(atomic_read(&new->usage) < 1);
|
||||
put_cred(new);
|
||||
}
|
||||
EXPORT_SYMBOL(abort_creds);
|
||||
|
||||
/**
|
||||
* override_creds - Override the current process's subjective credentials
|
||||
* @new: The credentials to be assigned
|
||||
*
|
||||
* Install a set of temporary override subjective credentials on the current
|
||||
* process, returning the old set for later reversion.
|
||||
*/
|
||||
const struct cred *override_creds(const struct cred *new)
|
||||
{
|
||||
const struct cred *old = current->cred;
|
||||
|
||||
rcu_assign_pointer(current->cred, get_cred(new));
|
||||
return old;
|
||||
}
|
||||
EXPORT_SYMBOL(override_creds);
|
||||
|
||||
/**
|
||||
* revert_creds - Revert a temporary subjective credentials override
|
||||
* @old: The credentials to be restored
|
||||
*
|
||||
* Revert a temporary set of override subjective credentials to an old set,
|
||||
* discarding the override set.
|
||||
*/
|
||||
void revert_creds(const struct cred *old)
|
||||
{
|
||||
const struct cred *override = current->cred;
|
||||
|
||||
rcu_assign_pointer(current->cred, old);
|
||||
put_cred(override);
|
||||
}
|
||||
EXPORT_SYMBOL(revert_creds);
|
||||
|
||||
/*
|
||||
* initialise the credentials stuff
|
||||
*/
|
||||
void __init cred_init(void)
|
||||
{
|
||||
/* allocate a slab in which we can store credentials */
|
||||
cred_jar = kmem_cache_create("cred_jar", sizeof(struct cred),
|
||||
0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* prepare_kernel_cred - Prepare a set of credentials for a kernel service
|
||||
* @daemon: A userspace daemon to be used as a reference
|
||||
*
|
||||
* Prepare a set of credentials for a kernel service. This can then be used to
|
||||
* override a task's own credentials so that work can be done on behalf of that
|
||||
* task that requires a different subjective context.
|
||||
*
|
||||
* @daemon is used to provide a base for the security record, but can be NULL.
|
||||
* If @daemon is supplied, then the security data will be derived from that;
|
||||
* otherwise they'll be set to 0 and no groups, full capabilities and no keys.
|
||||
*
|
||||
* The caller may change these controls afterwards if desired.
|
||||
*
|
||||
* Returns the new credentials or NULL if out of memory.
|
||||
*
|
||||
* Does not take, and does not return holding current->cred_replace_mutex.
|
||||
*/
|
||||
struct cred *prepare_kernel_cred(struct task_struct *daemon)
|
||||
{
|
||||
const struct cred *old;
|
||||
struct cred *new;
|
||||
|
||||
new = kmem_cache_alloc(cred_jar, GFP_KERNEL);
|
||||
if (!new)
|
||||
return NULL;
|
||||
|
||||
if (daemon)
|
||||
old = get_task_cred(daemon);
|
||||
else
|
||||
old = get_cred(&init_cred);
|
||||
|
||||
get_uid(new->user);
|
||||
get_group_info(new->group_info);
|
||||
|
||||
#ifdef CONFIG_KEYS
|
||||
atomic_inc(&init_tgcred.usage);
|
||||
new->tgcred = &init_tgcred;
|
||||
new->request_key_auth = NULL;
|
||||
new->thread_keyring = NULL;
|
||||
new->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SECURITY
|
||||
new->security = NULL;
|
||||
#endif
|
||||
if (security_prepare_creds(new, old, GFP_KERNEL) < 0)
|
||||
goto error;
|
||||
|
||||
atomic_set(&new->usage, 1);
|
||||
put_cred(old);
|
||||
return new;
|
||||
|
||||
error:
|
||||
put_cred(new);
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(prepare_kernel_cred);
|
||||
|
||||
/**
|
||||
* set_security_override - Set the security ID in a set of credentials
|
||||
* @new: The credentials to alter
|
||||
* @secid: The LSM security ID to set
|
||||
*
|
||||
* Set the LSM security ID in a set of credentials so that the subjective
|
||||
* security is overridden when an alternative set of credentials is used.
|
||||
*/
|
||||
int set_security_override(struct cred *new, u32 secid)
|
||||
{
|
||||
return security_kernel_act_as(new, secid);
|
||||
}
|
||||
EXPORT_SYMBOL(set_security_override);
|
||||
|
||||
/**
|
||||
* set_security_override_from_ctx - Set the security ID in a set of credentials
|
||||
* @new: The credentials to alter
|
||||
* @secctx: The LSM security context to generate the security ID from.
|
||||
*
|
||||
* Set the LSM security ID in a set of credentials so that the subjective
|
||||
* security is overridden when an alternative set of credentials is used. The
|
||||
* security ID is specified in string form as a security context to be
|
||||
* interpreted by the LSM.
|
||||
*/
|
||||
int set_security_override_from_ctx(struct cred *new, const char *secctx)
|
||||
{
|
||||
u32 secid;
|
||||
int ret;
|
||||
|
||||
ret = security_secctx_to_secid(secctx, strlen(secctx), &secid);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return set_security_override(new, secid);
|
||||
}
|
||||
EXPORT_SYMBOL(set_security_override_from_ctx);
|
||||
|
||||
/**
|
||||
* set_create_files_as - Set the LSM file create context in a set of credentials
|
||||
* @new: The credentials to alter
|
||||
* @inode: The inode to take the context from
|
||||
*
|
||||
* Change the LSM file creation context in a set of credentials to be the same
|
||||
* as the object context of the specified inode, so that the new inodes have
|
||||
* the same MAC context as that inode.
|
||||
*/
|
||||
int set_create_files_as(struct cred *new, struct inode *inode)
|
||||
{
|
||||
new->fsuid = inode->i_uid;
|
||||
new->fsgid = inode->i_gid;
|
||||
return security_kernel_create_files_as(new, inode);
|
||||
}
|
||||
EXPORT_SYMBOL(set_create_files_as);
|
||||
+1
-1
@@ -127,7 +127,7 @@ int __delayacct_add_tsk(struct taskstats *d, struct task_struct *tsk)
|
||||
*/
|
||||
t1 = tsk->sched_info.pcount;
|
||||
t2 = tsk->sched_info.run_delay;
|
||||
t3 = tsk->sched_info.cpu_time;
|
||||
t3 = tsk->se.sum_exec_runtime;
|
||||
|
||||
d->cpu_count += t1;
|
||||
|
||||
|
||||
+18
-10
@@ -46,12 +46,18 @@
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/task_io_accounting_ops.h>
|
||||
#include <linux/tracehook.h>
|
||||
#include <linux/init_task.h>
|
||||
#include <trace/sched.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/unistd.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/mmu_context.h>
|
||||
#include "cred-internals.h"
|
||||
|
||||
DEFINE_TRACE(sched_process_free);
|
||||
DEFINE_TRACE(sched_process_exit);
|
||||
DEFINE_TRACE(sched_process_wait);
|
||||
|
||||
static void exit_mm(struct task_struct * tsk);
|
||||
|
||||
@@ -164,7 +170,10 @@ void release_task(struct task_struct * p)
|
||||
int zap_leader;
|
||||
repeat:
|
||||
tracehook_prepare_release_task(p);
|
||||
atomic_dec(&p->user->processes);
|
||||
/* don't need to get the RCU readlock here - the process is dead and
|
||||
* can't be modifying its own credentials */
|
||||
atomic_dec(&__task_cred(p)->user->processes);
|
||||
|
||||
proc_flush_task(p);
|
||||
write_lock_irq(&tasklist_lock);
|
||||
tracehook_finish_release_task(p);
|
||||
@@ -339,12 +348,12 @@ static void reparent_to_kthreadd(void)
|
||||
/* cpus_allowed? */
|
||||
/* rt_priority? */
|
||||
/* signals? */
|
||||
security_task_reparent_to_init(current);
|
||||
memcpy(current->signal->rlim, init_task.signal->rlim,
|
||||
sizeof(current->signal->rlim));
|
||||
atomic_inc(&(INIT_USER->__count));
|
||||
|
||||
atomic_inc(&init_cred.usage);
|
||||
commit_creds(&init_cred);
|
||||
write_unlock_irq(&tasklist_lock);
|
||||
switch_uid(INIT_USER);
|
||||
}
|
||||
|
||||
void __set_special_pids(struct pid *pid)
|
||||
@@ -1078,7 +1087,6 @@ NORET_TYPE void do_exit(long code)
|
||||
check_stack_usage();
|
||||
exit_thread();
|
||||
cgroup_exit(tsk, 1);
|
||||
exit_keys(tsk);
|
||||
|
||||
if (group_dead && tsk->signal->leader)
|
||||
disassociate_ctty(1);
|
||||
@@ -1123,7 +1131,6 @@ NORET_TYPE void do_exit(long code)
|
||||
preempt_disable();
|
||||
/* causes final put_task_struct in finish_task_switch(). */
|
||||
tsk->state = TASK_DEAD;
|
||||
|
||||
schedule();
|
||||
BUG();
|
||||
/* Avoid "noreturn function does return". */
|
||||
@@ -1263,12 +1270,12 @@ static int wait_task_zombie(struct task_struct *p, int options,
|
||||
unsigned long state;
|
||||
int retval, status, traced;
|
||||
pid_t pid = task_pid_vnr(p);
|
||||
uid_t uid = __task_cred(p)->uid;
|
||||
|
||||
if (!likely(options & WEXITED))
|
||||
return 0;
|
||||
|
||||
if (unlikely(options & WNOWAIT)) {
|
||||
uid_t uid = p->uid;
|
||||
int exit_code = p->exit_code;
|
||||
int why, status;
|
||||
|
||||
@@ -1389,7 +1396,7 @@ static int wait_task_zombie(struct task_struct *p, int options,
|
||||
if (!retval && infop)
|
||||
retval = put_user(pid, &infop->si_pid);
|
||||
if (!retval && infop)
|
||||
retval = put_user(p->uid, &infop->si_uid);
|
||||
retval = put_user(uid, &infop->si_uid);
|
||||
if (!retval)
|
||||
retval = pid;
|
||||
|
||||
@@ -1454,7 +1461,8 @@ static int wait_task_stopped(int ptrace, struct task_struct *p,
|
||||
if (!unlikely(options & WNOWAIT))
|
||||
p->exit_code = 0;
|
||||
|
||||
uid = p->uid;
|
||||
/* don't need the RCU readlock here as we're holding a spinlock */
|
||||
uid = __task_cred(p)->uid;
|
||||
unlock_sig:
|
||||
spin_unlock_irq(&p->sighand->siglock);
|
||||
if (!exit_code)
|
||||
@@ -1528,10 +1536,10 @@ static int wait_task_continued(struct task_struct *p, int options,
|
||||
}
|
||||
if (!unlikely(options & WNOWAIT))
|
||||
p->signal->flags &= ~SIGNAL_STOP_CONTINUED;
|
||||
uid = __task_cred(p)->uid;
|
||||
spin_unlock_irq(&p->sighand->siglock);
|
||||
|
||||
pid = task_pid_vnr(p);
|
||||
uid = p->uid;
|
||||
get_task_struct(p);
|
||||
read_unlock(&tasklist_lock);
|
||||
|
||||
|
||||
+3
-2
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ftrace.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/sections.h>
|
||||
|
||||
@@ -40,7 +41,7 @@ const struct exception_table_entry *search_exception_tables(unsigned long addr)
|
||||
return e;
|
||||
}
|
||||
|
||||
int core_kernel_text(unsigned long addr)
|
||||
__notrace_funcgraph int core_kernel_text(unsigned long addr)
|
||||
{
|
||||
if (addr >= (unsigned long)_stext &&
|
||||
addr <= (unsigned long)_etext)
|
||||
@@ -53,7 +54,7 @@ int core_kernel_text(unsigned long addr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __kernel_text_address(unsigned long addr)
|
||||
__notrace_funcgraph int __kernel_text_address(unsigned long addr)
|
||||
{
|
||||
if (core_kernel_text(addr))
|
||||
return 1;
|
||||
|
||||
+40
-36
@@ -47,6 +47,7 @@
|
||||
#include <linux/mount.h>
|
||||
#include <linux/audit.h>
|
||||
#include <linux/memcontrol.h>
|
||||
#include <linux/ftrace.h>
|
||||
#include <linux/profile.h>
|
||||
#include <linux/rmap.h>
|
||||
#include <linux/acct.h>
|
||||
@@ -80,6 +81,8 @@ DEFINE_PER_CPU(unsigned long, process_counts) = 0;
|
||||
|
||||
__cacheline_aligned DEFINE_RWLOCK(tasklist_lock); /* outer */
|
||||
|
||||
DEFINE_TRACE(sched_process_fork);
|
||||
|
||||
int nr_processes(void)
|
||||
{
|
||||
int cpu;
|
||||
@@ -137,6 +140,7 @@ void free_task(struct task_struct *tsk)
|
||||
prop_local_destroy_single(&tsk->dirties);
|
||||
free_thread_info(tsk->stack);
|
||||
rt_mutex_debug_task_free(tsk);
|
||||
ftrace_graph_exit_task(tsk);
|
||||
free_task_struct(tsk);
|
||||
}
|
||||
EXPORT_SYMBOL(free_task);
|
||||
@@ -147,9 +151,8 @@ void __put_task_struct(struct task_struct *tsk)
|
||||
WARN_ON(atomic_read(&tsk->usage));
|
||||
WARN_ON(tsk == current);
|
||||
|
||||
security_task_free(tsk);
|
||||
free_uid(tsk->user);
|
||||
put_group_info(tsk->group_info);
|
||||
put_cred(tsk->real_cred);
|
||||
put_cred(tsk->cred);
|
||||
delayacct_tsk_free(tsk);
|
||||
|
||||
if (!profile_handoff_task(tsk))
|
||||
@@ -818,12 +821,6 @@ static int copy_signal(unsigned long clone_flags, struct task_struct *tsk)
|
||||
if (!sig)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = copy_thread_group_keys(tsk);
|
||||
if (ret < 0) {
|
||||
kmem_cache_free(signal_cachep, sig);
|
||||
return ret;
|
||||
}
|
||||
|
||||
atomic_set(&sig->count, 1);
|
||||
atomic_set(&sig->live, 1);
|
||||
init_waitqueue_head(&sig->wait_chldexit);
|
||||
@@ -868,7 +865,6 @@ static int copy_signal(unsigned long clone_flags, struct task_struct *tsk)
|
||||
void __cleanup_signal(struct signal_struct *sig)
|
||||
{
|
||||
thread_group_cputime_free(sig);
|
||||
exit_thread_group_keys(sig);
|
||||
tty_kref_put(sig->tty);
|
||||
kmem_cache_free(signal_cachep, sig);
|
||||
}
|
||||
@@ -984,16 +980,16 @@ static struct task_struct *copy_process(unsigned long clone_flags,
|
||||
DEBUG_LOCKS_WARN_ON(!p->softirqs_enabled);
|
||||
#endif
|
||||
retval = -EAGAIN;
|
||||
if (atomic_read(&p->user->processes) >=
|
||||
if (atomic_read(&p->real_cred->user->processes) >=
|
||||
p->signal->rlim[RLIMIT_NPROC].rlim_cur) {
|
||||
if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RESOURCE) &&
|
||||
p->user != current->nsproxy->user_ns->root_user)
|
||||
p->real_cred->user != INIT_USER)
|
||||
goto bad_fork_free;
|
||||
}
|
||||
|
||||
atomic_inc(&p->user->__count);
|
||||
atomic_inc(&p->user->processes);
|
||||
get_group_info(p->group_info);
|
||||
retval = copy_creds(p, clone_flags);
|
||||
if (retval < 0)
|
||||
goto bad_fork_free;
|
||||
|
||||
/*
|
||||
* If multiple threads are within copy_process(), then this check
|
||||
@@ -1048,10 +1044,6 @@ static struct task_struct *copy_process(unsigned long clone_flags,
|
||||
do_posix_clock_monotonic_gettime(&p->start_time);
|
||||
p->real_start_time = p->start_time;
|
||||
monotonic_to_bootbased(&p->real_start_time);
|
||||
#ifdef CONFIG_SECURITY
|
||||
p->security = NULL;
|
||||
#endif
|
||||
p->cap_bset = current->cap_bset;
|
||||
p->io_context = NULL;
|
||||
p->audit_context = NULL;
|
||||
cgroup_fork(p);
|
||||
@@ -1092,14 +1084,14 @@ static struct task_struct *copy_process(unsigned long clone_flags,
|
||||
#ifdef CONFIG_DEBUG_MUTEXES
|
||||
p->blocked_on = NULL; /* not blocked yet */
|
||||
#endif
|
||||
if (unlikely(ptrace_reparented(current)))
|
||||
ptrace_fork(p, clone_flags);
|
||||
|
||||
/* Perform scheduler related setup. Assign this task to a CPU. */
|
||||
sched_fork(p, clone_flags);
|
||||
|
||||
if ((retval = security_task_alloc(p)))
|
||||
goto bad_fork_cleanup_policy;
|
||||
if ((retval = audit_alloc(p)))
|
||||
goto bad_fork_cleanup_security;
|
||||
goto bad_fork_cleanup_policy;
|
||||
/* copy all the process information */
|
||||
if ((retval = copy_semundo(clone_flags, p)))
|
||||
goto bad_fork_cleanup_audit;
|
||||
@@ -1113,10 +1105,8 @@ static struct task_struct *copy_process(unsigned long clone_flags,
|
||||
goto bad_fork_cleanup_sighand;
|
||||
if ((retval = copy_mm(clone_flags, p)))
|
||||
goto bad_fork_cleanup_signal;
|
||||
if ((retval = copy_keys(clone_flags, p)))
|
||||
goto bad_fork_cleanup_mm;
|
||||
if ((retval = copy_namespaces(clone_flags, p)))
|
||||
goto bad_fork_cleanup_keys;
|
||||
goto bad_fork_cleanup_mm;
|
||||
if ((retval = copy_io(clone_flags, p)))
|
||||
goto bad_fork_cleanup_namespaces;
|
||||
retval = copy_thread(0, clone_flags, stack_start, stack_size, p, regs);
|
||||
@@ -1136,6 +1126,8 @@ static struct task_struct *copy_process(unsigned long clone_flags,
|
||||
}
|
||||
}
|
||||
|
||||
ftrace_graph_init_task(p);
|
||||
|
||||
p->pid = pid_nr(pid);
|
||||
p->tgid = p->pid;
|
||||
if (clone_flags & CLONE_THREAD)
|
||||
@@ -1144,7 +1136,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
|
||||
if (current->nsproxy != p->nsproxy) {
|
||||
retval = ns_cgroup_clone(p, pid);
|
||||
if (retval)
|
||||
goto bad_fork_free_pid;
|
||||
goto bad_fork_free_graph;
|
||||
}
|
||||
|
||||
p->set_child_tid = (clone_flags & CLONE_CHILD_SETTID) ? child_tidptr : NULL;
|
||||
@@ -1237,7 +1229,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
|
||||
spin_unlock(¤t->sighand->siglock);
|
||||
write_unlock_irq(&tasklist_lock);
|
||||
retval = -ERESTARTNOINTR;
|
||||
goto bad_fork_free_pid;
|
||||
goto bad_fork_free_graph;
|
||||
}
|
||||
|
||||
if (clone_flags & CLONE_THREAD) {
|
||||
@@ -1274,6 +1266,8 @@ static struct task_struct *copy_process(unsigned long clone_flags,
|
||||
cgroup_post_fork(p);
|
||||
return p;
|
||||
|
||||
bad_fork_free_graph:
|
||||
ftrace_graph_exit_task(p);
|
||||
bad_fork_free_pid:
|
||||
if (pid != &init_struct_pid)
|
||||
free_pid(pid);
|
||||
@@ -1281,8 +1275,6 @@ bad_fork_cleanup_io:
|
||||
put_io_context(p->io_context);
|
||||
bad_fork_cleanup_namespaces:
|
||||
exit_task_namespaces(p);
|
||||
bad_fork_cleanup_keys:
|
||||
exit_keys(p);
|
||||
bad_fork_cleanup_mm:
|
||||
if (p->mm)
|
||||
mmput(p->mm);
|
||||
@@ -1298,8 +1290,6 @@ bad_fork_cleanup_semundo:
|
||||
exit_sem(p);
|
||||
bad_fork_cleanup_audit:
|
||||
audit_free(p);
|
||||
bad_fork_cleanup_security:
|
||||
security_task_free(p);
|
||||
bad_fork_cleanup_policy:
|
||||
#ifdef CONFIG_NUMA
|
||||
mpol_put(p->mempolicy);
|
||||
@@ -1312,9 +1302,9 @@ bad_fork_cleanup_cgroup:
|
||||
bad_fork_cleanup_put_domain:
|
||||
module_put(task_thread_info(p)->exec_domain->module);
|
||||
bad_fork_cleanup_count:
|
||||
put_group_info(p->group_info);
|
||||
atomic_dec(&p->user->processes);
|
||||
free_uid(p->user);
|
||||
atomic_dec(&p->cred->user->processes);
|
||||
put_cred(p->real_cred);
|
||||
put_cred(p->cred);
|
||||
bad_fork_free:
|
||||
free_task(p);
|
||||
fork_out:
|
||||
@@ -1357,6 +1347,21 @@ long do_fork(unsigned long clone_flags,
|
||||
int trace = 0;
|
||||
long nr;
|
||||
|
||||
/*
|
||||
* Do some preliminary argument and permissions checking before we
|
||||
* actually start allocating stuff
|
||||
*/
|
||||
if (clone_flags & CLONE_NEWUSER) {
|
||||
if (clone_flags & CLONE_THREAD)
|
||||
return -EINVAL;
|
||||
/* hopefully this check will go away when userns support is
|
||||
* complete
|
||||
*/
|
||||
if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SETUID) ||
|
||||
!capable(CAP_SETGID))
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
/*
|
||||
* We hope to recycle these flags after 2.6.26
|
||||
*/
|
||||
@@ -1605,8 +1610,7 @@ asmlinkage long sys_unshare(unsigned long unshare_flags)
|
||||
err = -EINVAL;
|
||||
if (unshare_flags & ~(CLONE_THREAD|CLONE_FS|CLONE_NEWNS|CLONE_SIGHAND|
|
||||
CLONE_VM|CLONE_FILES|CLONE_SYSVSEM|
|
||||
CLONE_NEWUTS|CLONE_NEWIPC|CLONE_NEWUSER|
|
||||
CLONE_NEWNET))
|
||||
CLONE_NEWUTS|CLONE_NEWIPC|CLONE_NEWNET))
|
||||
goto bad_unshare_out;
|
||||
|
||||
/*
|
||||
|
||||
+15
-5
@@ -439,13 +439,20 @@ static void free_pi_state(struct futex_pi_state *pi_state)
|
||||
static struct task_struct * futex_find_get_task(pid_t pid)
|
||||
{
|
||||
struct task_struct *p;
|
||||
const struct cred *cred = current_cred(), *pcred;
|
||||
|
||||
rcu_read_lock();
|
||||
p = find_task_by_vpid(pid);
|
||||
if (!p || ((current->euid != p->euid) && (current->euid != p->uid)))
|
||||
if (!p) {
|
||||
p = ERR_PTR(-ESRCH);
|
||||
else
|
||||
get_task_struct(p);
|
||||
} else {
|
||||
pcred = __task_cred(p);
|
||||
if (cred->euid != pcred->euid &&
|
||||
cred->euid != pcred->uid)
|
||||
p = ERR_PTR(-ESRCH);
|
||||
else
|
||||
get_task_struct(p);
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
@@ -1829,6 +1836,7 @@ sys_get_robust_list(int pid, struct robust_list_head __user * __user *head_ptr,
|
||||
{
|
||||
struct robust_list_head __user *head;
|
||||
unsigned long ret;
|
||||
const struct cred *cred = current_cred(), *pcred;
|
||||
|
||||
if (!futex_cmpxchg_enabled)
|
||||
return -ENOSYS;
|
||||
@@ -1844,8 +1852,10 @@ sys_get_robust_list(int pid, struct robust_list_head __user * __user *head_ptr,
|
||||
if (!p)
|
||||
goto err_unlock;
|
||||
ret = -EPERM;
|
||||
if ((current->euid != p->euid) && (current->euid != p->uid) &&
|
||||
!capable(CAP_SYS_PTRACE))
|
||||
pcred = __task_cred(p);
|
||||
if (cred->euid != pcred->euid &&
|
||||
cred->euid != pcred->uid &&
|
||||
!capable(CAP_SYS_PTRACE))
|
||||
goto err_unlock;
|
||||
head = p->robust_list;
|
||||
rcu_read_unlock();
|
||||
|
||||
@@ -135,6 +135,7 @@ compat_sys_get_robust_list(int pid, compat_uptr_t __user *head_ptr,
|
||||
{
|
||||
struct compat_robust_list_head __user *head;
|
||||
unsigned long ret;
|
||||
const struct cred *cred = current_cred(), *pcred;
|
||||
|
||||
if (!futex_cmpxchg_enabled)
|
||||
return -ENOSYS;
|
||||
@@ -150,8 +151,10 @@ compat_sys_get_robust_list(int pid, compat_uptr_t __user *head_ptr,
|
||||
if (!p)
|
||||
goto err_unlock;
|
||||
ret = -EPERM;
|
||||
if ((current->euid != p->euid) && (current->euid != p->uid) &&
|
||||
!capable(CAP_SYS_PTRACE))
|
||||
pcred = __task_cred(p);
|
||||
if (cred->euid != pcred->euid &&
|
||||
cred->euid != pcred->uid &&
|
||||
!capable(CAP_SYS_PTRACE))
|
||||
goto err_unlock;
|
||||
head = p->compat_robust_list;
|
||||
read_unlock(&tasklist_lock);
|
||||
|
||||
+6
-10
@@ -30,20 +30,19 @@
|
||||
#define all_var 0
|
||||
#endif
|
||||
|
||||
/* These will be re-linked against their real values during the second link stage */
|
||||
extern const unsigned long kallsyms_addresses[] __attribute__((weak));
|
||||
extern const u8 kallsyms_names[] __attribute__((weak));
|
||||
extern const unsigned long kallsyms_addresses[];
|
||||
extern const u8 kallsyms_names[];
|
||||
|
||||
/* tell the compiler that the count isn't in the small data section if the arch
|
||||
* has one (eg: FRV)
|
||||
*/
|
||||
extern const unsigned long kallsyms_num_syms
|
||||
__attribute__((weak, section(".rodata")));
|
||||
__attribute__((__section__(".rodata")));
|
||||
|
||||
extern const u8 kallsyms_token_table[] __attribute__((weak));
|
||||
extern const u16 kallsyms_token_index[] __attribute__((weak));
|
||||
extern const u8 kallsyms_token_table[];
|
||||
extern const u16 kallsyms_token_index[];
|
||||
|
||||
extern const unsigned long kallsyms_markers[] __attribute__((weak));
|
||||
extern const unsigned long kallsyms_markers[];
|
||||
|
||||
static inline int is_kernel_inittext(unsigned long addr)
|
||||
{
|
||||
@@ -168,9 +167,6 @@ static unsigned long get_symbol_pos(unsigned long addr,
|
||||
unsigned long symbol_start = 0, symbol_end = 0;
|
||||
unsigned long i, low, high, mid;
|
||||
|
||||
/* This kernel should never had been booted. */
|
||||
BUG_ON(!kallsyms_addresses);
|
||||
|
||||
/* do a binary search on the sorted kallsyms_addresses array */
|
||||
low = 0;
|
||||
high = kallsyms_num_syms;
|
||||
|
||||
+23
-7
@@ -118,10 +118,10 @@ EXPORT_SYMBOL(request_module);
|
||||
struct subprocess_info {
|
||||
struct work_struct work;
|
||||
struct completion *complete;
|
||||
struct cred *cred;
|
||||
char *path;
|
||||
char **argv;
|
||||
char **envp;
|
||||
struct key *ring;
|
||||
enum umh_wait wait;
|
||||
int retval;
|
||||
struct file *stdin;
|
||||
@@ -134,19 +134,20 @@ struct subprocess_info {
|
||||
static int ____call_usermodehelper(void *data)
|
||||
{
|
||||
struct subprocess_info *sub_info = data;
|
||||
struct key *new_session, *old_session;
|
||||
int retval;
|
||||
|
||||
/* Unblock all signals and set the session keyring. */
|
||||
new_session = key_get(sub_info->ring);
|
||||
BUG_ON(atomic_read(&sub_info->cred->usage) != 1);
|
||||
|
||||
/* Unblock all signals */
|
||||
spin_lock_irq(¤t->sighand->siglock);
|
||||
old_session = __install_session_keyring(current, new_session);
|
||||
flush_signal_handlers(current, 1);
|
||||
sigemptyset(¤t->blocked);
|
||||
recalc_sigpending();
|
||||
spin_unlock_irq(¤t->sighand->siglock);
|
||||
|
||||
key_put(old_session);
|
||||
/* Install the credentials */
|
||||
commit_creds(sub_info->cred);
|
||||
sub_info->cred = NULL;
|
||||
|
||||
/* Install input pipe when needed */
|
||||
if (sub_info->stdin) {
|
||||
@@ -185,6 +186,8 @@ void call_usermodehelper_freeinfo(struct subprocess_info *info)
|
||||
{
|
||||
if (info->cleanup)
|
||||
(*info->cleanup)(info->argv, info->envp);
|
||||
if (info->cred)
|
||||
put_cred(info->cred);
|
||||
kfree(info);
|
||||
}
|
||||
EXPORT_SYMBOL(call_usermodehelper_freeinfo);
|
||||
@@ -240,6 +243,8 @@ static void __call_usermodehelper(struct work_struct *work)
|
||||
pid_t pid;
|
||||
enum umh_wait wait = sub_info->wait;
|
||||
|
||||
BUG_ON(atomic_read(&sub_info->cred->usage) != 1);
|
||||
|
||||
/* CLONE_VFORK: wait until the usermode helper has execve'd
|
||||
* successfully We need the data structures to stay around
|
||||
* until that is done. */
|
||||
@@ -362,6 +367,9 @@ struct subprocess_info *call_usermodehelper_setup(char *path, char **argv,
|
||||
sub_info->path = path;
|
||||
sub_info->argv = argv;
|
||||
sub_info->envp = envp;
|
||||
sub_info->cred = prepare_usermodehelper_creds();
|
||||
if (!sub_info->cred)
|
||||
return NULL;
|
||||
|
||||
out:
|
||||
return sub_info;
|
||||
@@ -376,7 +384,13 @@ EXPORT_SYMBOL(call_usermodehelper_setup);
|
||||
void call_usermodehelper_setkeys(struct subprocess_info *info,
|
||||
struct key *session_keyring)
|
||||
{
|
||||
info->ring = session_keyring;
|
||||
#ifdef CONFIG_KEYS
|
||||
struct thread_group_cred *tgcred = info->cred->tgcred;
|
||||
key_put(tgcred->session_keyring);
|
||||
tgcred->session_keyring = key_get(session_keyring);
|
||||
#else
|
||||
BUG();
|
||||
#endif
|
||||
}
|
||||
EXPORT_SYMBOL(call_usermodehelper_setkeys);
|
||||
|
||||
@@ -444,6 +458,8 @@ int call_usermodehelper_exec(struct subprocess_info *sub_info,
|
||||
DECLARE_COMPLETION_ONSTACK(done);
|
||||
int retval = 0;
|
||||
|
||||
BUG_ON(atomic_read(&sub_info->cred->usage) != 1);
|
||||
|
||||
helper_lock();
|
||||
if (sub_info->path[0] == '\0')
|
||||
goto out;
|
||||
|
||||
@@ -21,6 +21,9 @@ static DEFINE_SPINLOCK(kthread_create_lock);
|
||||
static LIST_HEAD(kthread_create_list);
|
||||
struct task_struct *kthreadd_task;
|
||||
|
||||
DEFINE_TRACE(sched_kthread_stop);
|
||||
DEFINE_TRACE(sched_kthread_stop_ret);
|
||||
|
||||
struct kthread_create_info
|
||||
{
|
||||
/* Information passed to kthread() from kthreadd. */
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
* Thanks to Arjan van de Ven for coming up with the initial idea of
|
||||
* mapping lock dependencies runtime.
|
||||
*/
|
||||
#define DISABLE_BRANCH_PROFILING
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
+127
-65
@@ -43,6 +43,7 @@ static DEFINE_MUTEX(markers_mutex);
|
||||
*/
|
||||
#define MARKER_HASH_BITS 6
|
||||
#define MARKER_TABLE_SIZE (1 << MARKER_HASH_BITS)
|
||||
static struct hlist_head marker_table[MARKER_TABLE_SIZE];
|
||||
|
||||
/*
|
||||
* Note about RCU :
|
||||
@@ -64,11 +65,10 @@ struct marker_entry {
|
||||
void *oldptr;
|
||||
int rcu_pending;
|
||||
unsigned char ptype:1;
|
||||
unsigned char format_allocated:1;
|
||||
char name[0]; /* Contains name'\0'format'\0' */
|
||||
};
|
||||
|
||||
static struct hlist_head marker_table[MARKER_TABLE_SIZE];
|
||||
|
||||
/**
|
||||
* __mark_empty_function - Empty probe callback
|
||||
* @probe_private: probe private data
|
||||
@@ -81,7 +81,7 @@ static struct hlist_head marker_table[MARKER_TABLE_SIZE];
|
||||
* though the function pointer change and the marker enabling are two distinct
|
||||
* operations that modifies the execution flow of preemptible code.
|
||||
*/
|
||||
void __mark_empty_function(void *probe_private, void *call_private,
|
||||
notrace void __mark_empty_function(void *probe_private, void *call_private,
|
||||
const char *fmt, va_list *args)
|
||||
{
|
||||
}
|
||||
@@ -97,7 +97,8 @@ EXPORT_SYMBOL_GPL(__mark_empty_function);
|
||||
* need to put a full smp_rmb() in this branch. This is why we do not use
|
||||
* rcu_dereference() for the pointer read.
|
||||
*/
|
||||
void marker_probe_cb(const struct marker *mdata, void *call_private, ...)
|
||||
notrace void marker_probe_cb(const struct marker *mdata,
|
||||
void *call_private, ...)
|
||||
{
|
||||
va_list args;
|
||||
char ptype;
|
||||
@@ -107,7 +108,7 @@ void marker_probe_cb(const struct marker *mdata, void *call_private, ...)
|
||||
* sure the teardown of the callbacks can be done correctly when they
|
||||
* are in modules and they insure RCU read coherency.
|
||||
*/
|
||||
rcu_read_lock_sched();
|
||||
rcu_read_lock_sched_notrace();
|
||||
ptype = mdata->ptype;
|
||||
if (likely(!ptype)) {
|
||||
marker_probe_func *func;
|
||||
@@ -145,7 +146,7 @@ void marker_probe_cb(const struct marker *mdata, void *call_private, ...)
|
||||
va_end(args);
|
||||
}
|
||||
}
|
||||
rcu_read_unlock_sched();
|
||||
rcu_read_unlock_sched_notrace();
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(marker_probe_cb);
|
||||
|
||||
@@ -157,12 +158,13 @@ EXPORT_SYMBOL_GPL(marker_probe_cb);
|
||||
*
|
||||
* Should be connected to markers "MARK_NOARGS".
|
||||
*/
|
||||
void marker_probe_cb_noarg(const struct marker *mdata, void *call_private, ...)
|
||||
static notrace void marker_probe_cb_noarg(const struct marker *mdata,
|
||||
void *call_private, ...)
|
||||
{
|
||||
va_list args; /* not initialized */
|
||||
char ptype;
|
||||
|
||||
rcu_read_lock_sched();
|
||||
rcu_read_lock_sched_notrace();
|
||||
ptype = mdata->ptype;
|
||||
if (likely(!ptype)) {
|
||||
marker_probe_func *func;
|
||||
@@ -195,9 +197,8 @@ void marker_probe_cb_noarg(const struct marker *mdata, void *call_private, ...)
|
||||
multi[i].func(multi[i].probe_private, call_private,
|
||||
mdata->format, &args);
|
||||
}
|
||||
rcu_read_unlock_sched();
|
||||
rcu_read_unlock_sched_notrace();
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(marker_probe_cb_noarg);
|
||||
|
||||
static void free_old_closure(struct rcu_head *head)
|
||||
{
|
||||
@@ -416,6 +417,7 @@ static struct marker_entry *add_marker(const char *name, const char *format)
|
||||
e->single.probe_private = NULL;
|
||||
e->multi = NULL;
|
||||
e->ptype = 0;
|
||||
e->format_allocated = 0;
|
||||
e->refcount = 0;
|
||||
e->rcu_pending = 0;
|
||||
hlist_add_head(&e->hlist, head);
|
||||
@@ -447,6 +449,8 @@ static int remove_marker(const char *name)
|
||||
if (e->single.func != __mark_empty_function)
|
||||
return -EBUSY;
|
||||
hlist_del(&e->hlist);
|
||||
if (e->format_allocated)
|
||||
kfree(e->format);
|
||||
/* Make sure the call_rcu has been executed */
|
||||
if (e->rcu_pending)
|
||||
rcu_barrier_sched();
|
||||
@@ -457,57 +461,34 @@ static int remove_marker(const char *name)
|
||||
/*
|
||||
* Set the mark_entry format to the format found in the element.
|
||||
*/
|
||||
static int marker_set_format(struct marker_entry **entry, const char *format)
|
||||
static int marker_set_format(struct marker_entry *entry, const char *format)
|
||||
{
|
||||
struct marker_entry *e;
|
||||
size_t name_len = strlen((*entry)->name) + 1;
|
||||
size_t format_len = strlen(format) + 1;
|
||||
|
||||
|
||||
e = kmalloc(sizeof(struct marker_entry) + name_len + format_len,
|
||||
GFP_KERNEL);
|
||||
if (!e)
|
||||
entry->format = kstrdup(format, GFP_KERNEL);
|
||||
if (!entry->format)
|
||||
return -ENOMEM;
|
||||
memcpy(&e->name[0], (*entry)->name, name_len);
|
||||
e->format = &e->name[name_len];
|
||||
memcpy(e->format, format, format_len);
|
||||
if (strcmp(e->format, MARK_NOARGS) == 0)
|
||||
e->call = marker_probe_cb_noarg;
|
||||
else
|
||||
e->call = marker_probe_cb;
|
||||
e->single = (*entry)->single;
|
||||
e->multi = (*entry)->multi;
|
||||
e->ptype = (*entry)->ptype;
|
||||
e->refcount = (*entry)->refcount;
|
||||
e->rcu_pending = 0;
|
||||
hlist_add_before(&e->hlist, &(*entry)->hlist);
|
||||
hlist_del(&(*entry)->hlist);
|
||||
/* Make sure the call_rcu has been executed */
|
||||
if ((*entry)->rcu_pending)
|
||||
rcu_barrier_sched();
|
||||
kfree(*entry);
|
||||
*entry = e;
|
||||
entry->format_allocated = 1;
|
||||
|
||||
trace_mark(core_marker_format, "name %s format %s",
|
||||
e->name, e->format);
|
||||
entry->name, entry->format);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets the probe callback corresponding to one marker.
|
||||
*/
|
||||
static int set_marker(struct marker_entry **entry, struct marker *elem,
|
||||
static int set_marker(struct marker_entry *entry, struct marker *elem,
|
||||
int active)
|
||||
{
|
||||
int ret;
|
||||
WARN_ON(strcmp((*entry)->name, elem->name) != 0);
|
||||
int ret = 0;
|
||||
WARN_ON(strcmp(entry->name, elem->name) != 0);
|
||||
|
||||
if ((*entry)->format) {
|
||||
if (strcmp((*entry)->format, elem->format) != 0) {
|
||||
if (entry->format) {
|
||||
if (strcmp(entry->format, elem->format) != 0) {
|
||||
printk(KERN_NOTICE
|
||||
"Format mismatch for probe %s "
|
||||
"(%s), marker (%s)\n",
|
||||
(*entry)->name,
|
||||
(*entry)->format,
|
||||
entry->name,
|
||||
entry->format,
|
||||
elem->format);
|
||||
return -EPERM;
|
||||
}
|
||||
@@ -523,37 +504,67 @@ static int set_marker(struct marker_entry **entry, struct marker *elem,
|
||||
* pass from a "safe" callback (with argument) to an "unsafe"
|
||||
* callback (does not set arguments).
|
||||
*/
|
||||
elem->call = (*entry)->call;
|
||||
elem->call = entry->call;
|
||||
/*
|
||||
* Sanity check :
|
||||
* We only update the single probe private data when the ptr is
|
||||
* set to a _non_ single probe! (0 -> 1 and N -> 1, N != 1)
|
||||
*/
|
||||
WARN_ON(elem->single.func != __mark_empty_function
|
||||
&& elem->single.probe_private
|
||||
!= (*entry)->single.probe_private &&
|
||||
!elem->ptype);
|
||||
elem->single.probe_private = (*entry)->single.probe_private;
|
||||
&& elem->single.probe_private != entry->single.probe_private
|
||||
&& !elem->ptype);
|
||||
elem->single.probe_private = entry->single.probe_private;
|
||||
/*
|
||||
* Make sure the private data is valid when we update the
|
||||
* single probe ptr.
|
||||
*/
|
||||
smp_wmb();
|
||||
elem->single.func = (*entry)->single.func;
|
||||
elem->single.func = entry->single.func;
|
||||
/*
|
||||
* We also make sure that the new probe callbacks array is consistent
|
||||
* before setting a pointer to it.
|
||||
*/
|
||||
rcu_assign_pointer(elem->multi, (*entry)->multi);
|
||||
rcu_assign_pointer(elem->multi, entry->multi);
|
||||
/*
|
||||
* Update the function or multi probe array pointer before setting the
|
||||
* ptype.
|
||||
*/
|
||||
smp_wmb();
|
||||
elem->ptype = (*entry)->ptype;
|
||||
elem->ptype = entry->ptype;
|
||||
|
||||
if (elem->tp_name && (active ^ elem->state)) {
|
||||
WARN_ON(!elem->tp_cb);
|
||||
/*
|
||||
* It is ok to directly call the probe registration because type
|
||||
* checking has been done in the __trace_mark_tp() macro.
|
||||
*/
|
||||
|
||||
if (active) {
|
||||
/*
|
||||
* try_module_get should always succeed because we hold
|
||||
* lock_module() to get the tp_cb address.
|
||||
*/
|
||||
ret = try_module_get(__module_text_address(
|
||||
(unsigned long)elem->tp_cb));
|
||||
BUG_ON(!ret);
|
||||
ret = tracepoint_probe_register_noupdate(
|
||||
elem->tp_name,
|
||||
elem->tp_cb);
|
||||
} else {
|
||||
ret = tracepoint_probe_unregister_noupdate(
|
||||
elem->tp_name,
|
||||
elem->tp_cb);
|
||||
/*
|
||||
* tracepoint_probe_update_all() must be called
|
||||
* before the module containing tp_cb is unloaded.
|
||||
*/
|
||||
module_put(__module_text_address(
|
||||
(unsigned long)elem->tp_cb));
|
||||
}
|
||||
}
|
||||
elem->state = active;
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -564,7 +575,24 @@ static int set_marker(struct marker_entry **entry, struct marker *elem,
|
||||
*/
|
||||
static void disable_marker(struct marker *elem)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* leave "call" as is. It is known statically. */
|
||||
if (elem->tp_name && elem->state) {
|
||||
WARN_ON(!elem->tp_cb);
|
||||
/*
|
||||
* It is ok to directly call the probe registration because type
|
||||
* checking has been done in the __trace_mark_tp() macro.
|
||||
*/
|
||||
ret = tracepoint_probe_unregister_noupdate(elem->tp_name,
|
||||
elem->tp_cb);
|
||||
WARN_ON(ret);
|
||||
/*
|
||||
* tracepoint_probe_update_all() must be called
|
||||
* before the module containing tp_cb is unloaded.
|
||||
*/
|
||||
module_put(__module_text_address((unsigned long)elem->tp_cb));
|
||||
}
|
||||
elem->state = 0;
|
||||
elem->single.func = __mark_empty_function;
|
||||
/* Update the function before setting the ptype */
|
||||
@@ -594,8 +622,7 @@ void marker_update_probe_range(struct marker *begin,
|
||||
for (iter = begin; iter < end; iter++) {
|
||||
mark_entry = get_marker(iter->name);
|
||||
if (mark_entry) {
|
||||
set_marker(&mark_entry, iter,
|
||||
!!mark_entry->refcount);
|
||||
set_marker(mark_entry, iter, !!mark_entry->refcount);
|
||||
/*
|
||||
* ignore error, continue
|
||||
*/
|
||||
@@ -629,6 +656,7 @@ static void marker_update_probes(void)
|
||||
marker_update_probe_range(__start___markers, __stop___markers);
|
||||
/* Markers in modules. */
|
||||
module_update_markers();
|
||||
tracepoint_probe_update_all();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -657,7 +685,7 @@ int marker_probe_register(const char *name, const char *format,
|
||||
ret = PTR_ERR(entry);
|
||||
} else if (format) {
|
||||
if (!entry->format)
|
||||
ret = marker_set_format(&entry, format);
|
||||
ret = marker_set_format(entry, format);
|
||||
else if (strcmp(entry->format, format))
|
||||
ret = -EPERM;
|
||||
}
|
||||
@@ -676,10 +704,11 @@ int marker_probe_register(const char *name, const char *format,
|
||||
goto end;
|
||||
}
|
||||
mutex_unlock(&markers_mutex);
|
||||
marker_update_probes(); /* may update entry */
|
||||
marker_update_probes();
|
||||
mutex_lock(&markers_mutex);
|
||||
entry = get_marker(name);
|
||||
WARN_ON(!entry);
|
||||
if (!entry)
|
||||
goto end;
|
||||
if (entry->rcu_pending)
|
||||
rcu_barrier_sched();
|
||||
entry->oldptr = old;
|
||||
@@ -720,7 +749,7 @@ int marker_probe_unregister(const char *name,
|
||||
rcu_barrier_sched();
|
||||
old = marker_entry_remove_probe(entry, probe, probe_private);
|
||||
mutex_unlock(&markers_mutex);
|
||||
marker_update_probes(); /* may update entry */
|
||||
marker_update_probes();
|
||||
mutex_lock(&markers_mutex);
|
||||
entry = get_marker(name);
|
||||
if (!entry)
|
||||
@@ -801,10 +830,11 @@ int marker_probe_unregister_private_data(marker_probe_func *probe,
|
||||
rcu_barrier_sched();
|
||||
old = marker_entry_remove_probe(entry, NULL, probe_private);
|
||||
mutex_unlock(&markers_mutex);
|
||||
marker_update_probes(); /* may update entry */
|
||||
marker_update_probes();
|
||||
mutex_lock(&markers_mutex);
|
||||
entry = get_marker_from_private_data(probe, probe_private);
|
||||
WARN_ON(!entry);
|
||||
if (!entry)
|
||||
goto end;
|
||||
if (entry->rcu_pending)
|
||||
rcu_barrier_sched();
|
||||
entry->oldptr = old;
|
||||
@@ -848,8 +878,6 @@ void *marker_get_private_data(const char *name, marker_probe_func *probe,
|
||||
if (!e->ptype) {
|
||||
if (num == 0 && e->single.func == probe)
|
||||
return e->single.probe_private;
|
||||
else
|
||||
break;
|
||||
} else {
|
||||
struct marker_probe_closure *closure;
|
||||
int match = 0;
|
||||
@@ -861,8 +889,42 @@ void *marker_get_private_data(const char *name, marker_probe_func *probe,
|
||||
return closure[i].probe_private;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ERR_PTR(-ENOENT);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(marker_get_private_data);
|
||||
|
||||
#ifdef CONFIG_MODULES
|
||||
|
||||
int marker_module_notify(struct notifier_block *self,
|
||||
unsigned long val, void *data)
|
||||
{
|
||||
struct module *mod = data;
|
||||
|
||||
switch (val) {
|
||||
case MODULE_STATE_COMING:
|
||||
marker_update_probe_range(mod->markers,
|
||||
mod->markers + mod->num_markers);
|
||||
break;
|
||||
case MODULE_STATE_GOING:
|
||||
marker_update_probe_range(mod->markers,
|
||||
mod->markers + mod->num_markers);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct notifier_block marker_module_nb = {
|
||||
.notifier_call = marker_module_notify,
|
||||
.priority = 0,
|
||||
};
|
||||
|
||||
static int init_markers(void)
|
||||
{
|
||||
return register_module_notifier(&marker_module_nb);
|
||||
}
|
||||
__initcall(init_markers);
|
||||
|
||||
#endif /* CONFIG_MODULES */
|
||||
|
||||
+2
-11
@@ -2184,24 +2184,15 @@ static noinline struct module *load_module(void __user *umod,
|
||||
struct mod_debug *debug;
|
||||
unsigned int num_debug;
|
||||
|
||||
#ifdef CONFIG_MARKERS
|
||||
marker_update_probe_range(mod->markers,
|
||||
mod->markers + mod->num_markers);
|
||||
#endif
|
||||
debug = section_objs(hdr, sechdrs, secstrings, "__verbose",
|
||||
sizeof(*debug), &num_debug);
|
||||
dynamic_printk_setup(debug, num_debug);
|
||||
|
||||
#ifdef CONFIG_TRACEPOINTS
|
||||
tracepoint_update_probe_range(mod->tracepoints,
|
||||
mod->tracepoints + mod->num_tracepoints);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* sechdrs[0].sh_size is always zero */
|
||||
mseg = section_objs(hdr, sechdrs, secstrings, "__mcount_loc",
|
||||
sizeof(*mseg), &num_mcount);
|
||||
ftrace_init_module(mseg, mseg + num_mcount);
|
||||
ftrace_init_module(mod, mseg, mseg + num_mcount);
|
||||
|
||||
err = module_finalize(hdr, sechdrs, mod);
|
||||
if (err < 0)
|
||||
@@ -2713,7 +2704,7 @@ int is_module_address(unsigned long addr)
|
||||
|
||||
|
||||
/* Is this a valid kernel address? */
|
||||
struct module *__module_text_address(unsigned long addr)
|
||||
__notrace_funcgraph struct module *__module_text_address(unsigned long addr)
|
||||
{
|
||||
struct module *mod;
|
||||
|
||||
|
||||
+2
-13
@@ -80,12 +80,6 @@ static struct nsproxy *create_new_namespaces(unsigned long flags,
|
||||
goto out_pid;
|
||||
}
|
||||
|
||||
new_nsp->user_ns = copy_user_ns(flags, tsk->nsproxy->user_ns);
|
||||
if (IS_ERR(new_nsp->user_ns)) {
|
||||
err = PTR_ERR(new_nsp->user_ns);
|
||||
goto out_user;
|
||||
}
|
||||
|
||||
new_nsp->net_ns = copy_net_ns(flags, tsk->nsproxy->net_ns);
|
||||
if (IS_ERR(new_nsp->net_ns)) {
|
||||
err = PTR_ERR(new_nsp->net_ns);
|
||||
@@ -95,9 +89,6 @@ static struct nsproxy *create_new_namespaces(unsigned long flags,
|
||||
return new_nsp;
|
||||
|
||||
out_net:
|
||||
if (new_nsp->user_ns)
|
||||
put_user_ns(new_nsp->user_ns);
|
||||
out_user:
|
||||
if (new_nsp->pid_ns)
|
||||
put_pid_ns(new_nsp->pid_ns);
|
||||
out_pid:
|
||||
@@ -130,7 +121,7 @@ int copy_namespaces(unsigned long flags, struct task_struct *tsk)
|
||||
get_nsproxy(old_ns);
|
||||
|
||||
if (!(flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC |
|
||||
CLONE_NEWUSER | CLONE_NEWPID | CLONE_NEWNET)))
|
||||
CLONE_NEWPID | CLONE_NEWNET)))
|
||||
return 0;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN)) {
|
||||
@@ -173,8 +164,6 @@ void free_nsproxy(struct nsproxy *ns)
|
||||
put_ipc_ns(ns->ipc_ns);
|
||||
if (ns->pid_ns)
|
||||
put_pid_ns(ns->pid_ns);
|
||||
if (ns->user_ns)
|
||||
put_user_ns(ns->user_ns);
|
||||
put_net(ns->net_ns);
|
||||
kmem_cache_free(nsproxy_cachep, ns);
|
||||
}
|
||||
@@ -189,7 +178,7 @@ int unshare_nsproxy_namespaces(unsigned long unshare_flags,
|
||||
int err = 0;
|
||||
|
||||
if (!(unshare_flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC |
|
||||
CLONE_NEWUSER | CLONE_NEWNET)))
|
||||
CLONE_NEWNET)))
|
||||
return 0;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
|
||||
@@ -197,6 +197,11 @@ static int common_timer_create(struct k_itimer *new_timer)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int no_timer_create(struct k_itimer *new_timer)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return nonzero if we know a priori this clockid_t value is bogus.
|
||||
*/
|
||||
@@ -248,6 +253,7 @@ static __init int init_posix_timers(void)
|
||||
.clock_getres = hrtimer_get_res,
|
||||
.clock_get = posix_get_monotonic_raw,
|
||||
.clock_set = do_posix_clock_nosettime,
|
||||
.timer_create = no_timer_create,
|
||||
};
|
||||
|
||||
register_posix_clock(CLOCK_REALTIME, &clock_realtime);
|
||||
|
||||
+3
-10
@@ -22,7 +22,6 @@
|
||||
#include <linux/console.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/freezer.h>
|
||||
#include <linux/ftrace.h>
|
||||
|
||||
#include "power.h"
|
||||
|
||||
@@ -257,7 +256,7 @@ static int create_image(int platform_mode)
|
||||
|
||||
int hibernation_snapshot(int platform_mode)
|
||||
{
|
||||
int error, ftrace_save;
|
||||
int error;
|
||||
|
||||
/* Free memory before shutting down devices. */
|
||||
error = swsusp_shrink_memory();
|
||||
@@ -269,7 +268,6 @@ int hibernation_snapshot(int platform_mode)
|
||||
goto Close;
|
||||
|
||||
suspend_console();
|
||||
ftrace_save = __ftrace_enabled_save();
|
||||
error = device_suspend(PMSG_FREEZE);
|
||||
if (error)
|
||||
goto Recover_platform;
|
||||
@@ -299,7 +297,6 @@ int hibernation_snapshot(int platform_mode)
|
||||
Resume_devices:
|
||||
device_resume(in_suspend ?
|
||||
(error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE);
|
||||
__ftrace_enabled_restore(ftrace_save);
|
||||
resume_console();
|
||||
Close:
|
||||
platform_end(platform_mode);
|
||||
@@ -370,11 +367,10 @@ static int resume_target_kernel(void)
|
||||
|
||||
int hibernation_restore(int platform_mode)
|
||||
{
|
||||
int error, ftrace_save;
|
||||
int error;
|
||||
|
||||
pm_prepare_console();
|
||||
suspend_console();
|
||||
ftrace_save = __ftrace_enabled_save();
|
||||
error = device_suspend(PMSG_QUIESCE);
|
||||
if (error)
|
||||
goto Finish;
|
||||
@@ -389,7 +385,6 @@ int hibernation_restore(int platform_mode)
|
||||
platform_restore_cleanup(platform_mode);
|
||||
device_resume(PMSG_RECOVER);
|
||||
Finish:
|
||||
__ftrace_enabled_restore(ftrace_save);
|
||||
resume_console();
|
||||
pm_restore_console();
|
||||
return error;
|
||||
@@ -402,7 +397,7 @@ int hibernation_restore(int platform_mode)
|
||||
|
||||
int hibernation_platform_enter(void)
|
||||
{
|
||||
int error, ftrace_save;
|
||||
int error;
|
||||
|
||||
if (!hibernation_ops)
|
||||
return -ENOSYS;
|
||||
@@ -417,7 +412,6 @@ int hibernation_platform_enter(void)
|
||||
goto Close;
|
||||
|
||||
suspend_console();
|
||||
ftrace_save = __ftrace_enabled_save();
|
||||
error = device_suspend(PMSG_HIBERNATE);
|
||||
if (error) {
|
||||
if (hibernation_ops->recover)
|
||||
@@ -452,7 +446,6 @@ int hibernation_platform_enter(void)
|
||||
hibernation_ops->finish();
|
||||
Resume_devices:
|
||||
device_resume(PMSG_RESTORE);
|
||||
__ftrace_enabled_restore(ftrace_save);
|
||||
resume_console();
|
||||
Close:
|
||||
hibernation_ops->end();
|
||||
|
||||
+1
-4
@@ -22,7 +22,6 @@
|
||||
#include <linux/freezer.h>
|
||||
#include <linux/vmstat.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/ftrace.h>
|
||||
|
||||
#include "power.h"
|
||||
|
||||
@@ -317,7 +316,7 @@ static int suspend_enter(suspend_state_t state)
|
||||
*/
|
||||
int suspend_devices_and_enter(suspend_state_t state)
|
||||
{
|
||||
int error, ftrace_save;
|
||||
int error;
|
||||
|
||||
if (!suspend_ops)
|
||||
return -ENOSYS;
|
||||
@@ -328,7 +327,6 @@ int suspend_devices_and_enter(suspend_state_t state)
|
||||
goto Close;
|
||||
}
|
||||
suspend_console();
|
||||
ftrace_save = __ftrace_enabled_save();
|
||||
suspend_test_start();
|
||||
error = device_suspend(PMSG_SUSPEND);
|
||||
if (error) {
|
||||
@@ -360,7 +358,6 @@ int suspend_devices_and_enter(suspend_state_t state)
|
||||
suspend_test_start();
|
||||
device_resume(PMSG_RESUME);
|
||||
suspend_test_finish("resume devices");
|
||||
__ftrace_enabled_restore(ftrace_save);
|
||||
resume_console();
|
||||
Close:
|
||||
if (suspend_ops->end)
|
||||
|
||||
+1
-1
@@ -544,7 +544,7 @@ static const struct file_operations proc_profile_operations = {
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
static inline void profile_nop(void *unused)
|
||||
static void profile_nop(void *unused)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
+35
-6
@@ -25,6 +25,17 @@
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
|
||||
/*
|
||||
* Initialize a new task whose father had been ptraced.
|
||||
*
|
||||
* Called from copy_process().
|
||||
*/
|
||||
void ptrace_fork(struct task_struct *child, unsigned long clone_flags)
|
||||
{
|
||||
arch_ptrace_fork(child, clone_flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* ptrace a task: make the debugger its new parent and
|
||||
* move it to the ptrace list.
|
||||
@@ -72,6 +83,7 @@ void __ptrace_unlink(struct task_struct *child)
|
||||
child->parent = child->real_parent;
|
||||
list_del_init(&child->ptrace_entry);
|
||||
|
||||
arch_ptrace_untrace(child);
|
||||
if (task_is_traced(child))
|
||||
ptrace_untrace(child);
|
||||
}
|
||||
@@ -115,6 +127,8 @@ int ptrace_check_attach(struct task_struct *child, int kill)
|
||||
|
||||
int __ptrace_may_access(struct task_struct *task, unsigned int mode)
|
||||
{
|
||||
const struct cred *cred = current_cred(), *tcred;
|
||||
|
||||
/* May we inspect the given task?
|
||||
* This check is used both for attaching with ptrace
|
||||
* and for allowing access to sensitive information in /proc.
|
||||
@@ -127,13 +141,19 @@ int __ptrace_may_access(struct task_struct *task, unsigned int mode)
|
||||
/* Don't let security modules deny introspection */
|
||||
if (task == current)
|
||||
return 0;
|
||||
if (((current->uid != task->euid) ||
|
||||
(current->uid != task->suid) ||
|
||||
(current->uid != task->uid) ||
|
||||
(current->gid != task->egid) ||
|
||||
(current->gid != task->sgid) ||
|
||||
(current->gid != task->gid)) && !capable(CAP_SYS_PTRACE))
|
||||
rcu_read_lock();
|
||||
tcred = __task_cred(task);
|
||||
if ((cred->uid != tcred->euid ||
|
||||
cred->uid != tcred->suid ||
|
||||
cred->uid != tcred->uid ||
|
||||
cred->gid != tcred->egid ||
|
||||
cred->gid != tcred->sgid ||
|
||||
cred->gid != tcred->gid) &&
|
||||
!capable(CAP_SYS_PTRACE)) {
|
||||
rcu_read_unlock();
|
||||
return -EPERM;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
smp_rmb();
|
||||
if (task->mm)
|
||||
dumpable = get_dumpable(task->mm);
|
||||
@@ -163,6 +183,14 @@ int ptrace_attach(struct task_struct *task)
|
||||
if (same_thread_group(task, current))
|
||||
goto out;
|
||||
|
||||
/* Protect exec's credential calculations against our interference;
|
||||
* SUID, SGID and LSM creds get determined differently under ptrace.
|
||||
*/
|
||||
retval = mutex_lock_interruptible(¤t->cred_exec_mutex);
|
||||
if (retval < 0)
|
||||
goto out;
|
||||
|
||||
retval = -EPERM;
|
||||
repeat:
|
||||
/*
|
||||
* Nasty, nasty.
|
||||
@@ -202,6 +230,7 @@ repeat:
|
||||
bad:
|
||||
write_unlock_irqrestore(&tasklist_lock, flags);
|
||||
task_unlock(task);
|
||||
mutex_unlock(¤t->cred_exec_mutex);
|
||||
out:
|
||||
return retval;
|
||||
}
|
||||
|
||||
+225
-182
@@ -118,6 +118,12 @@
|
||||
*/
|
||||
#define RUNTIME_INF ((u64)~0ULL)
|
||||
|
||||
DEFINE_TRACE(sched_wait_task);
|
||||
DEFINE_TRACE(sched_wakeup);
|
||||
DEFINE_TRACE(sched_wakeup_new);
|
||||
DEFINE_TRACE(sched_switch);
|
||||
DEFINE_TRACE(sched_migrate_task);
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
/*
|
||||
* Divide a load by a sched group cpu_power : (load / sg->__cpu_power)
|
||||
@@ -261,6 +267,10 @@ struct task_group {
|
||||
struct cgroup_subsys_state css;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USER_SCHED
|
||||
uid_t uid;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_FAIR_GROUP_SCHED
|
||||
/* schedulable entities of this group on each cpu */
|
||||
struct sched_entity **se;
|
||||
@@ -286,6 +296,12 @@ struct task_group {
|
||||
|
||||
#ifdef CONFIG_USER_SCHED
|
||||
|
||||
/* Helper function to pass uid information to create_sched_user() */
|
||||
void set_tg_uid(struct user_struct *user)
|
||||
{
|
||||
user->tg->uid = user->uid;
|
||||
}
|
||||
|
||||
/*
|
||||
* Root task group.
|
||||
* Every UID task group (including init_task_group aka UID-0) will
|
||||
@@ -345,7 +361,9 @@ static inline struct task_group *task_group(struct task_struct *p)
|
||||
struct task_group *tg;
|
||||
|
||||
#ifdef CONFIG_USER_SCHED
|
||||
tg = p->user->tg;
|
||||
rcu_read_lock();
|
||||
tg = __task_cred(p)->user->tg;
|
||||
rcu_read_unlock();
|
||||
#elif defined(CONFIG_CGROUP_SCHED)
|
||||
tg = container_of(task_subsys_state(p, cpu_cgroup_subsys_id),
|
||||
struct task_group, css);
|
||||
@@ -586,6 +604,8 @@ struct rq {
|
||||
#ifdef CONFIG_SCHEDSTATS
|
||||
/* latency stats */
|
||||
struct sched_info rq_sched_info;
|
||||
unsigned long long rq_cpu_time;
|
||||
/* could above be rq->cfs_rq.exec_clock + rq->rt_rq.rt_runtime ? */
|
||||
|
||||
/* sys_sched_yield() stats */
|
||||
unsigned int yld_exp_empty;
|
||||
@@ -703,45 +723,18 @@ static __read_mostly char *sched_feat_names[] = {
|
||||
|
||||
#undef SCHED_FEAT
|
||||
|
||||
static int sched_feat_open(struct inode *inode, struct file *filp)
|
||||
static int sched_feat_show(struct seq_file *m, void *v)
|
||||
{
|
||||
filp->private_data = inode->i_private;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
sched_feat_read(struct file *filp, char __user *ubuf,
|
||||
size_t cnt, loff_t *ppos)
|
||||
{
|
||||
char *buf;
|
||||
int r = 0;
|
||||
int len = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; sched_feat_names[i]; i++) {
|
||||
len += strlen(sched_feat_names[i]);
|
||||
len += 4;
|
||||
if (!(sysctl_sched_features & (1UL << i)))
|
||||
seq_puts(m, "NO_");
|
||||
seq_printf(m, "%s ", sched_feat_names[i]);
|
||||
}
|
||||
seq_puts(m, "\n");
|
||||
|
||||
buf = kmalloc(len + 2, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; sched_feat_names[i]; i++) {
|
||||
if (sysctl_sched_features & (1UL << i))
|
||||
r += sprintf(buf + r, "%s ", sched_feat_names[i]);
|
||||
else
|
||||
r += sprintf(buf + r, "NO_%s ", sched_feat_names[i]);
|
||||
}
|
||||
|
||||
r += sprintf(buf + r, "\n");
|
||||
WARN_ON(r >= len + 2);
|
||||
|
||||
r = simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
|
||||
|
||||
kfree(buf);
|
||||
|
||||
return r;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
@@ -786,10 +779,17 @@ sched_feat_write(struct file *filp, const char __user *ubuf,
|
||||
return cnt;
|
||||
}
|
||||
|
||||
static int sched_feat_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
return single_open(filp, sched_feat_show, NULL);
|
||||
}
|
||||
|
||||
static struct file_operations sched_feat_fops = {
|
||||
.open = sched_feat_open,
|
||||
.read = sched_feat_read,
|
||||
.write = sched_feat_write,
|
||||
.open = sched_feat_open,
|
||||
.write = sched_feat_write,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static __init int sched_init_debug(void)
|
||||
@@ -1474,27 +1474,13 @@ static void
|
||||
update_group_shares_cpu(struct task_group *tg, int cpu,
|
||||
unsigned long sd_shares, unsigned long sd_rq_weight)
|
||||
{
|
||||
int boost = 0;
|
||||
unsigned long shares;
|
||||
unsigned long rq_weight;
|
||||
|
||||
if (!tg->se[cpu])
|
||||
return;
|
||||
|
||||
rq_weight = tg->cfs_rq[cpu]->load.weight;
|
||||
|
||||
/*
|
||||
* If there are currently no tasks on the cpu pretend there is one of
|
||||
* average load so that when a new task gets to run here it will not
|
||||
* get delayed by group starvation.
|
||||
*/
|
||||
if (!rq_weight) {
|
||||
boost = 1;
|
||||
rq_weight = NICE_0_LOAD;
|
||||
}
|
||||
|
||||
if (unlikely(rq_weight > sd_rq_weight))
|
||||
rq_weight = sd_rq_weight;
|
||||
rq_weight = tg->cfs_rq[cpu]->rq_weight;
|
||||
|
||||
/*
|
||||
* \Sum shares * rq_weight
|
||||
@@ -1502,7 +1488,7 @@ update_group_shares_cpu(struct task_group *tg, int cpu,
|
||||
* \Sum rq_weight
|
||||
*
|
||||
*/
|
||||
shares = (sd_shares * rq_weight) / (sd_rq_weight + 1);
|
||||
shares = (sd_shares * rq_weight) / sd_rq_weight;
|
||||
shares = clamp_t(unsigned long, shares, MIN_SHARES, MAX_SHARES);
|
||||
|
||||
if (abs(shares - tg->se[cpu]->load.weight) >
|
||||
@@ -1511,11 +1497,7 @@ update_group_shares_cpu(struct task_group *tg, int cpu,
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&rq->lock, flags);
|
||||
/*
|
||||
* record the actual number of shares, not the boosted amount.
|
||||
*/
|
||||
tg->cfs_rq[cpu]->shares = boost ? 0 : shares;
|
||||
tg->cfs_rq[cpu]->rq_weight = rq_weight;
|
||||
tg->cfs_rq[cpu]->shares = shares;
|
||||
|
||||
__set_se_shares(tg->se[cpu], shares);
|
||||
spin_unlock_irqrestore(&rq->lock, flags);
|
||||
@@ -1529,13 +1511,23 @@ update_group_shares_cpu(struct task_group *tg, int cpu,
|
||||
*/
|
||||
static int tg_shares_up(struct task_group *tg, void *data)
|
||||
{
|
||||
unsigned long rq_weight = 0;
|
||||
unsigned long weight, rq_weight = 0;
|
||||
unsigned long shares = 0;
|
||||
struct sched_domain *sd = data;
|
||||
int i;
|
||||
|
||||
for_each_cpu_mask(i, sd->span) {
|
||||
rq_weight += tg->cfs_rq[i]->load.weight;
|
||||
/*
|
||||
* If there are currently no tasks on the cpu pretend there
|
||||
* is one of average load so that when a new task gets to
|
||||
* run here it will not get delayed by group starvation.
|
||||
*/
|
||||
weight = tg->cfs_rq[i]->load.weight;
|
||||
if (!weight)
|
||||
weight = NICE_0_LOAD;
|
||||
|
||||
tg->cfs_rq[i]->rq_weight = weight;
|
||||
rq_weight += weight;
|
||||
shares += tg->cfs_rq[i]->shares;
|
||||
}
|
||||
|
||||
@@ -1545,9 +1537,6 @@ static int tg_shares_up(struct task_group *tg, void *data)
|
||||
if (!sd->parent || !(sd->parent->flags & SD_LOAD_BALANCE))
|
||||
shares = tg->shares;
|
||||
|
||||
if (!rq_weight)
|
||||
rq_weight = cpus_weight(sd->span) * NICE_0_LOAD;
|
||||
|
||||
for_each_cpu_mask(i, sd->span)
|
||||
update_group_shares_cpu(tg, i, shares, rq_weight);
|
||||
|
||||
@@ -1612,6 +1601,39 @@ static inline void update_shares_locked(struct rq *rq, struct sched_domain *sd)
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* double_lock_balance - lock the busiest runqueue, this_rq is locked already.
|
||||
*/
|
||||
static int double_lock_balance(struct rq *this_rq, struct rq *busiest)
|
||||
__releases(this_rq->lock)
|
||||
__acquires(busiest->lock)
|
||||
__acquires(this_rq->lock)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (unlikely(!irqs_disabled())) {
|
||||
/* printk() doesn't work good under rq->lock */
|
||||
spin_unlock(&this_rq->lock);
|
||||
BUG_ON(1);
|
||||
}
|
||||
if (unlikely(!spin_trylock(&busiest->lock))) {
|
||||
if (busiest < this_rq) {
|
||||
spin_unlock(&this_rq->lock);
|
||||
spin_lock(&busiest->lock);
|
||||
spin_lock_nested(&this_rq->lock, SINGLE_DEPTH_NESTING);
|
||||
ret = 1;
|
||||
} else
|
||||
spin_lock_nested(&busiest->lock, SINGLE_DEPTH_NESTING);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void double_unlock_balance(struct rq *this_rq, struct rq *busiest)
|
||||
__releases(busiest->lock)
|
||||
{
|
||||
spin_unlock(&busiest->lock);
|
||||
lock_set_subclass(&this_rq->lock.dep_map, 0, _RET_IP_);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_FAIR_GROUP_SCHED
|
||||
@@ -1845,6 +1867,8 @@ void set_task_cpu(struct task_struct *p, unsigned int new_cpu)
|
||||
|
||||
clock_offset = old_rq->clock - new_rq->clock;
|
||||
|
||||
trace_sched_migrate_task(p, task_cpu(p), new_cpu);
|
||||
|
||||
#ifdef CONFIG_SCHEDSTATS
|
||||
if (p->se.wait_start)
|
||||
p->se.wait_start -= clock_offset;
|
||||
@@ -2254,6 +2278,7 @@ static int try_to_wake_up(struct task_struct *p, unsigned int state, int sync)
|
||||
|
||||
smp_wmb();
|
||||
rq = task_rq_lock(p, &flags);
|
||||
update_rq_clock(rq);
|
||||
old_state = p->state;
|
||||
if (!(old_state & state))
|
||||
goto out;
|
||||
@@ -2311,12 +2336,11 @@ out_activate:
|
||||
schedstat_inc(p, se.nr_wakeups_local);
|
||||
else
|
||||
schedstat_inc(p, se.nr_wakeups_remote);
|
||||
update_rq_clock(rq);
|
||||
activate_task(rq, p, 1);
|
||||
success = 1;
|
||||
|
||||
out_running:
|
||||
trace_sched_wakeup(rq, p);
|
||||
trace_sched_wakeup(rq, p, success);
|
||||
check_preempt_curr(rq, p, sync);
|
||||
|
||||
p->state = TASK_RUNNING;
|
||||
@@ -2449,7 +2473,7 @@ void wake_up_new_task(struct task_struct *p, unsigned long clone_flags)
|
||||
p->sched_class->task_new(rq, p);
|
||||
inc_nr_running(rq);
|
||||
}
|
||||
trace_sched_wakeup_new(rq, p);
|
||||
trace_sched_wakeup_new(rq, p, 1);
|
||||
check_preempt_curr(rq, p, 0);
|
||||
#ifdef CONFIG_SMP
|
||||
if (p->sched_class->task_wake_up)
|
||||
@@ -2811,40 +2835,6 @@ static void double_rq_unlock(struct rq *rq1, struct rq *rq2)
|
||||
__release(rq2->lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* double_lock_balance - lock the busiest runqueue, this_rq is locked already.
|
||||
*/
|
||||
static int double_lock_balance(struct rq *this_rq, struct rq *busiest)
|
||||
__releases(this_rq->lock)
|
||||
__acquires(busiest->lock)
|
||||
__acquires(this_rq->lock)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (unlikely(!irqs_disabled())) {
|
||||
/* printk() doesn't work good under rq->lock */
|
||||
spin_unlock(&this_rq->lock);
|
||||
BUG_ON(1);
|
||||
}
|
||||
if (unlikely(!spin_trylock(&busiest->lock))) {
|
||||
if (busiest < this_rq) {
|
||||
spin_unlock(&this_rq->lock);
|
||||
spin_lock(&busiest->lock);
|
||||
spin_lock_nested(&this_rq->lock, SINGLE_DEPTH_NESTING);
|
||||
ret = 1;
|
||||
} else
|
||||
spin_lock_nested(&busiest->lock, SINGLE_DEPTH_NESTING);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void double_unlock_balance(struct rq *this_rq, struct rq *busiest)
|
||||
__releases(busiest->lock)
|
||||
{
|
||||
spin_unlock(&busiest->lock);
|
||||
lock_set_subclass(&this_rq->lock.dep_map, 0, _RET_IP_);
|
||||
}
|
||||
|
||||
/*
|
||||
* If dest_cpu is allowed for this process, migrate the task to it.
|
||||
* This is accomplished by forcing the cpu_allowed mask to only
|
||||
@@ -2862,7 +2852,6 @@ static void sched_migrate_task(struct task_struct *p, int dest_cpu)
|
||||
|| unlikely(!cpu_active(dest_cpu)))
|
||||
goto out;
|
||||
|
||||
trace_sched_migrate_task(rq, p, dest_cpu);
|
||||
/* force the process onto the specified CPU */
|
||||
if (migrate_task(p, dest_cpu, &req)) {
|
||||
/* Need to wait for migration thread (might exit: take ref). */
|
||||
@@ -3707,7 +3696,7 @@ out_balanced:
|
||||
static void idle_balance(int this_cpu, struct rq *this_rq)
|
||||
{
|
||||
struct sched_domain *sd;
|
||||
int pulled_task = -1;
|
||||
int pulled_task = 0;
|
||||
unsigned long next_balance = jiffies + HZ;
|
||||
cpumask_t tmpmask;
|
||||
|
||||
@@ -5134,6 +5123,22 @@ __setscheduler(struct rq *rq, struct task_struct *p, int policy, int prio)
|
||||
set_load_weight(p);
|
||||
}
|
||||
|
||||
/*
|
||||
* check the target process has a UID that matches the current process's
|
||||
*/
|
||||
static bool check_same_owner(struct task_struct *p)
|
||||
{
|
||||
const struct cred *cred = current_cred(), *pcred;
|
||||
bool match;
|
||||
|
||||
rcu_read_lock();
|
||||
pcred = __task_cred(p);
|
||||
match = (cred->euid == pcred->euid ||
|
||||
cred->euid == pcred->uid);
|
||||
rcu_read_unlock();
|
||||
return match;
|
||||
}
|
||||
|
||||
static int __sched_setscheduler(struct task_struct *p, int policy,
|
||||
struct sched_param *param, bool user)
|
||||
{
|
||||
@@ -5193,8 +5198,7 @@ recheck:
|
||||
return -EPERM;
|
||||
|
||||
/* can't change other user's priorities */
|
||||
if ((current->euid != p->euid) &&
|
||||
(current->euid != p->uid))
|
||||
if (!check_same_owner(p))
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
@@ -5426,8 +5430,7 @@ long sched_setaffinity(pid_t pid, const cpumask_t *in_mask)
|
||||
read_unlock(&tasklist_lock);
|
||||
|
||||
retval = -EPERM;
|
||||
if ((current->euid != p->euid) && (current->euid != p->uid) &&
|
||||
!capable(CAP_SYS_NICE))
|
||||
if (!check_same_owner(p) && !capable(CAP_SYS_NICE))
|
||||
goto out_unlock;
|
||||
|
||||
retval = security_task_setscheduler(p, 0, NULL);
|
||||
@@ -5896,6 +5899,7 @@ void __cpuinit init_idle(struct task_struct *idle, int cpu)
|
||||
* The idle tasks have their own, simple scheduling class:
|
||||
*/
|
||||
idle->sched_class = &idle_sched_class;
|
||||
ftrace_graph_init_task(idle);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -6126,7 +6130,6 @@ static int __migrate_task_irq(struct task_struct *p, int src_cpu, int dest_cpu)
|
||||
|
||||
/*
|
||||
* Figure out where task on dead CPU should go, use force if necessary.
|
||||
* NOTE: interrupts should be disabled by the caller
|
||||
*/
|
||||
static void move_task_off_dead_cpu(int dead_cpu, struct task_struct *p)
|
||||
{
|
||||
@@ -6638,28 +6641,6 @@ early_initcall(migration_init);
|
||||
|
||||
#ifdef CONFIG_SCHED_DEBUG
|
||||
|
||||
static inline const char *sd_level_to_string(enum sched_domain_level lvl)
|
||||
{
|
||||
switch (lvl) {
|
||||
case SD_LV_NONE:
|
||||
return "NONE";
|
||||
case SD_LV_SIBLING:
|
||||
return "SIBLING";
|
||||
case SD_LV_MC:
|
||||
return "MC";
|
||||
case SD_LV_CPU:
|
||||
return "CPU";
|
||||
case SD_LV_NODE:
|
||||
return "NODE";
|
||||
case SD_LV_ALLNODES:
|
||||
return "ALLNODES";
|
||||
case SD_LV_MAX:
|
||||
return "MAX";
|
||||
|
||||
}
|
||||
return "MAX";
|
||||
}
|
||||
|
||||
static int sched_domain_debug_one(struct sched_domain *sd, int cpu, int level,
|
||||
cpumask_t *groupmask)
|
||||
{
|
||||
@@ -6679,8 +6660,7 @@ static int sched_domain_debug_one(struct sched_domain *sd, int cpu, int level,
|
||||
return -1;
|
||||
}
|
||||
|
||||
printk(KERN_CONT "span %s level %s\n",
|
||||
str, sd_level_to_string(sd->level));
|
||||
printk(KERN_CONT "span %s level %s\n", str, sd->name);
|
||||
|
||||
if (!cpu_isset(cpu, sd->span)) {
|
||||
printk(KERN_ERR "ERROR: domain->span does not contain "
|
||||
@@ -6816,6 +6796,8 @@ sd_parent_degenerate(struct sched_domain *sd, struct sched_domain *parent)
|
||||
SD_BALANCE_EXEC |
|
||||
SD_SHARE_CPUPOWER |
|
||||
SD_SHARE_PKG_RESOURCES);
|
||||
if (nr_node_ids == 1)
|
||||
pflags &= ~SD_SERIALIZE;
|
||||
}
|
||||
if (~cflags & pflags)
|
||||
return 0;
|
||||
@@ -7336,13 +7318,21 @@ struct allmasks {
|
||||
};
|
||||
|
||||
#if NR_CPUS > 128
|
||||
#define SCHED_CPUMASK_ALLOC 1
|
||||
#define SCHED_CPUMASK_FREE(v) kfree(v)
|
||||
#define SCHED_CPUMASK_DECLARE(v) struct allmasks *v
|
||||
#define SCHED_CPUMASK_DECLARE(v) struct allmasks *v
|
||||
static inline void sched_cpumask_alloc(struct allmasks **masks)
|
||||
{
|
||||
*masks = kmalloc(sizeof(**masks), GFP_KERNEL);
|
||||
}
|
||||
static inline void sched_cpumask_free(struct allmasks *masks)
|
||||
{
|
||||
kfree(masks);
|
||||
}
|
||||
#else
|
||||
#define SCHED_CPUMASK_ALLOC 0
|
||||
#define SCHED_CPUMASK_FREE(v)
|
||||
#define SCHED_CPUMASK_DECLARE(v) struct allmasks _v, *v = &_v
|
||||
#define SCHED_CPUMASK_DECLARE(v) struct allmasks _v, *v = &_v
|
||||
static inline void sched_cpumask_alloc(struct allmasks **masks)
|
||||
{ }
|
||||
static inline void sched_cpumask_free(struct allmasks *masks)
|
||||
{ }
|
||||
#endif
|
||||
|
||||
#define SCHED_CPUMASK_VAR(v, a) cpumask_t *v = (cpumask_t *) \
|
||||
@@ -7418,9 +7408,8 @@ static int __build_sched_domains(const cpumask_t *cpu_map,
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
#if SCHED_CPUMASK_ALLOC
|
||||
/* get space for all scratch cpumask variables */
|
||||
allmasks = kmalloc(sizeof(*allmasks), GFP_KERNEL);
|
||||
sched_cpumask_alloc(&allmasks);
|
||||
if (!allmasks) {
|
||||
printk(KERN_WARNING "Cannot alloc cpumask array\n");
|
||||
kfree(rd);
|
||||
@@ -7429,7 +7418,7 @@ static int __build_sched_domains(const cpumask_t *cpu_map,
|
||||
#endif
|
||||
return -ENOMEM;
|
||||
}
|
||||
#endif
|
||||
|
||||
tmpmask = (cpumask_t *)allmasks;
|
||||
|
||||
|
||||
@@ -7683,13 +7672,13 @@ static int __build_sched_domains(const cpumask_t *cpu_map,
|
||||
cpu_attach_domain(sd, rd, i);
|
||||
}
|
||||
|
||||
SCHED_CPUMASK_FREE((void *)allmasks);
|
||||
sched_cpumask_free(allmasks);
|
||||
return 0;
|
||||
|
||||
#ifdef CONFIG_NUMA
|
||||
error:
|
||||
free_sched_groups(cpu_map, tmpmask);
|
||||
SCHED_CPUMASK_FREE((void *)allmasks);
|
||||
sched_cpumask_free(allmasks);
|
||||
kfree(rd);
|
||||
return -ENOMEM;
|
||||
#endif
|
||||
@@ -7712,8 +7701,14 @@ static struct sched_domain_attr *dattr_cur;
|
||||
*/
|
||||
static cpumask_t fallback_doms;
|
||||
|
||||
void __attribute__((weak)) arch_update_cpu_topology(void)
|
||||
/*
|
||||
* arch_update_cpu_topology lets virtualized architectures update the
|
||||
* cpu core maps. It is supposed to return 1 if the topology changed
|
||||
* or 0 if it stayed the same.
|
||||
*/
|
||||
int __attribute__((weak)) arch_update_cpu_topology(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -7753,8 +7748,6 @@ static void detach_destroy_domains(const cpumask_t *cpu_map)
|
||||
cpumask_t tmpmask;
|
||||
int i;
|
||||
|
||||
unregister_sched_domain_sysctl();
|
||||
|
||||
for_each_cpu_mask_nr(i, *cpu_map)
|
||||
cpu_attach_domain(NULL, &def_root_domain, i);
|
||||
synchronize_sched();
|
||||
@@ -7807,17 +7800,21 @@ void partition_sched_domains(int ndoms_new, cpumask_t *doms_new,
|
||||
struct sched_domain_attr *dattr_new)
|
||||
{
|
||||
int i, j, n;
|
||||
int new_topology;
|
||||
|
||||
mutex_lock(&sched_domains_mutex);
|
||||
|
||||
/* always unregister in case we don't destroy any domains */
|
||||
unregister_sched_domain_sysctl();
|
||||
|
||||
/* Let architecture update cpu core mappings. */
|
||||
new_topology = arch_update_cpu_topology();
|
||||
|
||||
n = doms_new ? ndoms_new : 0;
|
||||
|
||||
/* Destroy deleted domains */
|
||||
for (i = 0; i < ndoms_cur; i++) {
|
||||
for (j = 0; j < n; j++) {
|
||||
for (j = 0; j < n && !new_topology; j++) {
|
||||
if (cpus_equal(doms_cur[i], doms_new[j])
|
||||
&& dattrs_equal(dattr_cur, i, dattr_new, j))
|
||||
goto match1;
|
||||
@@ -7832,12 +7829,12 @@ match1:
|
||||
ndoms_cur = 0;
|
||||
doms_new = &fallback_doms;
|
||||
cpus_andnot(doms_new[0], cpu_online_map, cpu_isolated_map);
|
||||
dattr_new = NULL;
|
||||
WARN_ON_ONCE(dattr_new);
|
||||
}
|
||||
|
||||
/* Build new domains */
|
||||
for (i = 0; i < ndoms_new; i++) {
|
||||
for (j = 0; j < ndoms_cur; j++) {
|
||||
for (j = 0; j < ndoms_cur && !new_topology; j++) {
|
||||
if (cpus_equal(doms_new[i], doms_cur[j])
|
||||
&& dattrs_equal(dattr_new, i, dattr_cur, j))
|
||||
goto match2;
|
||||
@@ -8492,7 +8489,7 @@ static
|
||||
int alloc_fair_sched_group(struct task_group *tg, struct task_group *parent)
|
||||
{
|
||||
struct cfs_rq *cfs_rq;
|
||||
struct sched_entity *se, *parent_se;
|
||||
struct sched_entity *se;
|
||||
struct rq *rq;
|
||||
int i;
|
||||
|
||||
@@ -8508,18 +8505,17 @@ int alloc_fair_sched_group(struct task_group *tg, struct task_group *parent)
|
||||
for_each_possible_cpu(i) {
|
||||
rq = cpu_rq(i);
|
||||
|
||||
cfs_rq = kmalloc_node(sizeof(struct cfs_rq),
|
||||
GFP_KERNEL|__GFP_ZERO, cpu_to_node(i));
|
||||
cfs_rq = kzalloc_node(sizeof(struct cfs_rq),
|
||||
GFP_KERNEL, cpu_to_node(i));
|
||||
if (!cfs_rq)
|
||||
goto err;
|
||||
|
||||
se = kmalloc_node(sizeof(struct sched_entity),
|
||||
GFP_KERNEL|__GFP_ZERO, cpu_to_node(i));
|
||||
se = kzalloc_node(sizeof(struct sched_entity),
|
||||
GFP_KERNEL, cpu_to_node(i));
|
||||
if (!se)
|
||||
goto err;
|
||||
|
||||
parent_se = parent ? parent->se[i] : NULL;
|
||||
init_tg_cfs_entry(tg, cfs_rq, se, i, 0, parent_se);
|
||||
init_tg_cfs_entry(tg, cfs_rq, se, i, 0, parent->se[i]);
|
||||
}
|
||||
|
||||
return 1;
|
||||
@@ -8580,7 +8576,7 @@ static
|
||||
int alloc_rt_sched_group(struct task_group *tg, struct task_group *parent)
|
||||
{
|
||||
struct rt_rq *rt_rq;
|
||||
struct sched_rt_entity *rt_se, *parent_se;
|
||||
struct sched_rt_entity *rt_se;
|
||||
struct rq *rq;
|
||||
int i;
|
||||
|
||||
@@ -8597,18 +8593,17 @@ int alloc_rt_sched_group(struct task_group *tg, struct task_group *parent)
|
||||
for_each_possible_cpu(i) {
|
||||
rq = cpu_rq(i);
|
||||
|
||||
rt_rq = kmalloc_node(sizeof(struct rt_rq),
|
||||
GFP_KERNEL|__GFP_ZERO, cpu_to_node(i));
|
||||
rt_rq = kzalloc_node(sizeof(struct rt_rq),
|
||||
GFP_KERNEL, cpu_to_node(i));
|
||||
if (!rt_rq)
|
||||
goto err;
|
||||
|
||||
rt_se = kmalloc_node(sizeof(struct sched_rt_entity),
|
||||
GFP_KERNEL|__GFP_ZERO, cpu_to_node(i));
|
||||
rt_se = kzalloc_node(sizeof(struct sched_rt_entity),
|
||||
GFP_KERNEL, cpu_to_node(i));
|
||||
if (!rt_se)
|
||||
goto err;
|
||||
|
||||
parent_se = parent ? parent->rt_se[i] : NULL;
|
||||
init_tg_rt_entry(tg, rt_rq, rt_se, i, 0, parent_se);
|
||||
init_tg_rt_entry(tg, rt_rq, rt_se, i, 0, parent->rt_se[i]);
|
||||
}
|
||||
|
||||
return 1;
|
||||
@@ -9251,11 +9246,12 @@ struct cgroup_subsys cpu_cgroup_subsys = {
|
||||
* (balbir@in.ibm.com).
|
||||
*/
|
||||
|
||||
/* track cpu usage of a group of tasks */
|
||||
/* track cpu usage of a group of tasks and its child groups */
|
||||
struct cpuacct {
|
||||
struct cgroup_subsys_state css;
|
||||
/* cpuusage holds pointer to a u64-type object on every cpu */
|
||||
u64 *cpuusage;
|
||||
struct cpuacct *parent;
|
||||
};
|
||||
|
||||
struct cgroup_subsys cpuacct_subsys;
|
||||
@@ -9289,6 +9285,9 @@ static struct cgroup_subsys_state *cpuacct_create(
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
if (cgrp->parent)
|
||||
ca->parent = cgroup_ca(cgrp->parent);
|
||||
|
||||
return &ca->css;
|
||||
}
|
||||
|
||||
@@ -9302,6 +9301,41 @@ cpuacct_destroy(struct cgroup_subsys *ss, struct cgroup *cgrp)
|
||||
kfree(ca);
|
||||
}
|
||||
|
||||
static u64 cpuacct_cpuusage_read(struct cpuacct *ca, int cpu)
|
||||
{
|
||||
u64 *cpuusage = percpu_ptr(ca->cpuusage, cpu);
|
||||
u64 data;
|
||||
|
||||
#ifndef CONFIG_64BIT
|
||||
/*
|
||||
* Take rq->lock to make 64-bit read safe on 32-bit platforms.
|
||||
*/
|
||||
spin_lock_irq(&cpu_rq(cpu)->lock);
|
||||
data = *cpuusage;
|
||||
spin_unlock_irq(&cpu_rq(cpu)->lock);
|
||||
#else
|
||||
data = *cpuusage;
|
||||
#endif
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static void cpuacct_cpuusage_write(struct cpuacct *ca, int cpu, u64 val)
|
||||
{
|
||||
u64 *cpuusage = percpu_ptr(ca->cpuusage, cpu);
|
||||
|
||||
#ifndef CONFIG_64BIT
|
||||
/*
|
||||
* Take rq->lock to make 64-bit write safe on 32-bit platforms.
|
||||
*/
|
||||
spin_lock_irq(&cpu_rq(cpu)->lock);
|
||||
*cpuusage = val;
|
||||
spin_unlock_irq(&cpu_rq(cpu)->lock);
|
||||
#else
|
||||
*cpuusage = val;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* return total cpu usage (in nanoseconds) of a group */
|
||||
static u64 cpuusage_read(struct cgroup *cgrp, struct cftype *cft)
|
||||
{
|
||||
@@ -9309,17 +9343,8 @@ static u64 cpuusage_read(struct cgroup *cgrp, struct cftype *cft)
|
||||
u64 totalcpuusage = 0;
|
||||
int i;
|
||||
|
||||
for_each_possible_cpu(i) {
|
||||
u64 *cpuusage = percpu_ptr(ca->cpuusage, i);
|
||||
|
||||
/*
|
||||
* Take rq->lock to make 64-bit addition safe on 32-bit
|
||||
* platforms.
|
||||
*/
|
||||
spin_lock_irq(&cpu_rq(i)->lock);
|
||||
totalcpuusage += *cpuusage;
|
||||
spin_unlock_irq(&cpu_rq(i)->lock);
|
||||
}
|
||||
for_each_present_cpu(i)
|
||||
totalcpuusage += cpuacct_cpuusage_read(ca, i);
|
||||
|
||||
return totalcpuusage;
|
||||
}
|
||||
@@ -9336,23 +9361,39 @@ static int cpuusage_write(struct cgroup *cgrp, struct cftype *cftype,
|
||||
goto out;
|
||||
}
|
||||
|
||||
for_each_possible_cpu(i) {
|
||||
u64 *cpuusage = percpu_ptr(ca->cpuusage, i);
|
||||
for_each_present_cpu(i)
|
||||
cpuacct_cpuusage_write(ca, i, 0);
|
||||
|
||||
spin_lock_irq(&cpu_rq(i)->lock);
|
||||
*cpuusage = 0;
|
||||
spin_unlock_irq(&cpu_rq(i)->lock);
|
||||
}
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cpuacct_percpu_seq_read(struct cgroup *cgroup, struct cftype *cft,
|
||||
struct seq_file *m)
|
||||
{
|
||||
struct cpuacct *ca = cgroup_ca(cgroup);
|
||||
u64 percpu;
|
||||
int i;
|
||||
|
||||
for_each_present_cpu(i) {
|
||||
percpu = cpuacct_cpuusage_read(ca, i);
|
||||
seq_printf(m, "%llu ", (unsigned long long) percpu);
|
||||
}
|
||||
seq_printf(m, "\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct cftype files[] = {
|
||||
{
|
||||
.name = "usage",
|
||||
.read_u64 = cpuusage_read,
|
||||
.write_u64 = cpuusage_write,
|
||||
},
|
||||
{
|
||||
.name = "usage_percpu",
|
||||
.read_seq_string = cpuacct_percpu_seq_read,
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
static int cpuacct_populate(struct cgroup_subsys *ss, struct cgroup *cgrp)
|
||||
@@ -9368,14 +9409,16 @@ static int cpuacct_populate(struct cgroup_subsys *ss, struct cgroup *cgrp)
|
||||
static void cpuacct_charge(struct task_struct *tsk, u64 cputime)
|
||||
{
|
||||
struct cpuacct *ca;
|
||||
int cpu;
|
||||
|
||||
if (!cpuacct_subsys.active)
|
||||
return;
|
||||
|
||||
cpu = task_cpu(tsk);
|
||||
ca = task_ca(tsk);
|
||||
if (ca) {
|
||||
u64 *cpuusage = percpu_ptr(ca->cpuusage, task_cpu(tsk));
|
||||
|
||||
for (; ca; ca = ca->parent) {
|
||||
u64 *cpuusage = percpu_ptr(ca->cpuusage, cpu);
|
||||
*cpuusage += cputime;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,13 +118,13 @@ static u64 __update_sched_clock(struct sched_clock_data *scd, u64 now)
|
||||
|
||||
/*
|
||||
* scd->clock = clamp(scd->tick_gtod + delta,
|
||||
* max(scd->tick_gtod, scd->clock),
|
||||
* max(scd->clock, scd->tick_gtod + TICK_NSEC));
|
||||
* max(scd->tick_gtod, scd->clock),
|
||||
* scd->tick_gtod + TICK_NSEC);
|
||||
*/
|
||||
|
||||
clock = scd->tick_gtod + delta;
|
||||
min_clock = wrap_max(scd->tick_gtod, scd->clock);
|
||||
max_clock = wrap_max(scd->clock, scd->tick_gtod + TICK_NSEC);
|
||||
max_clock = scd->tick_gtod + TICK_NSEC;
|
||||
|
||||
clock = wrap_max(clock, min_clock);
|
||||
clock = wrap_min(clock, max_clock);
|
||||
|
||||
+43
-14
@@ -53,6 +53,40 @@ static unsigned long nsec_low(unsigned long long nsec)
|
||||
|
||||
#define SPLIT_NS(x) nsec_high(x), nsec_low(x)
|
||||
|
||||
#ifdef CONFIG_FAIR_GROUP_SCHED
|
||||
static void print_cfs_group_stats(struct seq_file *m, int cpu,
|
||||
struct task_group *tg)
|
||||
{
|
||||
struct sched_entity *se = tg->se[cpu];
|
||||
if (!se)
|
||||
return;
|
||||
|
||||
#define P(F) \
|
||||
SEQ_printf(m, " .%-30s: %lld\n", #F, (long long)F)
|
||||
#define PN(F) \
|
||||
SEQ_printf(m, " .%-30s: %lld.%06ld\n", #F, SPLIT_NS((long long)F))
|
||||
|
||||
PN(se->exec_start);
|
||||
PN(se->vruntime);
|
||||
PN(se->sum_exec_runtime);
|
||||
#ifdef CONFIG_SCHEDSTATS
|
||||
PN(se->wait_start);
|
||||
PN(se->sleep_start);
|
||||
PN(se->block_start);
|
||||
PN(se->sleep_max);
|
||||
PN(se->block_max);
|
||||
PN(se->exec_max);
|
||||
PN(se->slice_max);
|
||||
PN(se->wait_max);
|
||||
PN(se->wait_sum);
|
||||
P(se->wait_count);
|
||||
#endif
|
||||
P(se->load.weight);
|
||||
#undef PN
|
||||
#undef P
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
print_task(struct seq_file *m, struct rq *rq, struct task_struct *p)
|
||||
{
|
||||
@@ -121,20 +155,19 @@ void print_cfs_rq(struct seq_file *m, int cpu, struct cfs_rq *cfs_rq)
|
||||
|
||||
#if defined(CONFIG_CGROUP_SCHED) && defined(CONFIG_FAIR_GROUP_SCHED)
|
||||
char path[128] = "";
|
||||
struct cgroup *cgroup = NULL;
|
||||
struct task_group *tg = cfs_rq->tg;
|
||||
|
||||
if (tg)
|
||||
cgroup = tg->css.cgroup;
|
||||
|
||||
if (cgroup)
|
||||
cgroup_path(cgroup, path, sizeof(path));
|
||||
cgroup_path(tg->css.cgroup, path, sizeof(path));
|
||||
|
||||
SEQ_printf(m, "\ncfs_rq[%d]:%s\n", cpu, path);
|
||||
#elif defined(CONFIG_USER_SCHED) && defined(CONFIG_FAIR_GROUP_SCHED)
|
||||
{
|
||||
uid_t uid = cfs_rq->tg->uid;
|
||||
SEQ_printf(m, "\ncfs_rq[%d] for UID: %u\n", cpu, uid);
|
||||
}
|
||||
#else
|
||||
SEQ_printf(m, "\ncfs_rq[%d]:\n", cpu);
|
||||
#endif
|
||||
|
||||
SEQ_printf(m, " .%-30s: %Ld.%06ld\n", "exec_clock",
|
||||
SPLIT_NS(cfs_rq->exec_clock));
|
||||
|
||||
@@ -168,6 +201,7 @@ void print_cfs_rq(struct seq_file *m, int cpu, struct cfs_rq *cfs_rq)
|
||||
#ifdef CONFIG_SMP
|
||||
SEQ_printf(m, " .%-30s: %lu\n", "shares", cfs_rq->shares);
|
||||
#endif
|
||||
print_cfs_group_stats(m, cpu, cfs_rq->tg);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -175,14 +209,9 @@ void print_rt_rq(struct seq_file *m, int cpu, struct rt_rq *rt_rq)
|
||||
{
|
||||
#if defined(CONFIG_CGROUP_SCHED) && defined(CONFIG_RT_GROUP_SCHED)
|
||||
char path[128] = "";
|
||||
struct cgroup *cgroup = NULL;
|
||||
struct task_group *tg = rt_rq->tg;
|
||||
|
||||
if (tg)
|
||||
cgroup = tg->css.cgroup;
|
||||
|
||||
if (cgroup)
|
||||
cgroup_path(cgroup, path, sizeof(path));
|
||||
cgroup_path(tg->css.cgroup, path, sizeof(path));
|
||||
|
||||
SEQ_printf(m, "\nrt_rq[%d]:%s\n", cpu, path);
|
||||
#else
|
||||
@@ -272,7 +301,7 @@ static int sched_debug_show(struct seq_file *m, void *v)
|
||||
u64 now = ktime_to_ns(ktime_get());
|
||||
int cpu;
|
||||
|
||||
SEQ_printf(m, "Sched Debug Version: v0.07, %s %.*s\n",
|
||||
SEQ_printf(m, "Sched Debug Version: v0.08, %s %.*s\n",
|
||||
init_utsname()->release,
|
||||
(int)strcspn(init_utsname()->version, " "),
|
||||
init_utsname()->version);
|
||||
|
||||
+5
-4
@@ -492,6 +492,8 @@ static void update_curr(struct cfs_rq *cfs_rq)
|
||||
* overflow on 32 bits):
|
||||
*/
|
||||
delta_exec = (unsigned long)(now - curr->exec_start);
|
||||
if (!delta_exec)
|
||||
return;
|
||||
|
||||
__update_curr(cfs_rq, curr, delta_exec);
|
||||
curr->exec_start = now;
|
||||
@@ -1345,12 +1347,11 @@ static void check_preempt_wakeup(struct rq *rq, struct task_struct *p, int sync)
|
||||
{
|
||||
struct task_struct *curr = rq->curr;
|
||||
struct sched_entity *se = &curr->se, *pse = &p->se;
|
||||
struct cfs_rq *cfs_rq = task_cfs_rq(curr);
|
||||
|
||||
update_curr(cfs_rq);
|
||||
|
||||
if (unlikely(rt_prio(p->prio))) {
|
||||
struct cfs_rq *cfs_rq = task_cfs_rq(curr);
|
||||
|
||||
update_rq_clock(rq);
|
||||
update_curr(cfs_rq);
|
||||
resched_task(curr);
|
||||
return;
|
||||
}
|
||||
|
||||
+3
-6
@@ -77,7 +77,7 @@ static inline u64 sched_rt_period(struct rt_rq *rt_rq)
|
||||
}
|
||||
|
||||
#define for_each_leaf_rt_rq(rt_rq, rq) \
|
||||
list_for_each_entry(rt_rq, &rq->leaf_rt_rq_list, leaf_rt_rq_list)
|
||||
list_for_each_entry_rcu(rt_rq, &rq->leaf_rt_rq_list, leaf_rt_rq_list)
|
||||
|
||||
static inline struct rq *rq_of_rt_rq(struct rt_rq *rt_rq)
|
||||
{
|
||||
@@ -537,13 +537,13 @@ static void update_curr_rt(struct rq *rq)
|
||||
for_each_sched_rt_entity(rt_se) {
|
||||
rt_rq = rt_rq_of_se(rt_se);
|
||||
|
||||
spin_lock(&rt_rq->rt_runtime_lock);
|
||||
if (sched_rt_runtime(rt_rq) != RUNTIME_INF) {
|
||||
spin_lock(&rt_rq->rt_runtime_lock);
|
||||
rt_rq->rt_time += delta_exec;
|
||||
if (sched_rt_runtime_exceeded(rt_rq))
|
||||
resched_task(curr);
|
||||
spin_unlock(&rt_rq->rt_runtime_lock);
|
||||
}
|
||||
spin_unlock(&rt_rq->rt_runtime_lock);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -909,9 +909,6 @@ static void put_prev_task_rt(struct rq *rq, struct task_struct *p)
|
||||
/* Only try algorithms three times */
|
||||
#define RT_MAX_TRIES 3
|
||||
|
||||
static int double_lock_balance(struct rq *this_rq, struct rq *busiest);
|
||||
static void double_unlock_balance(struct rq *this_rq, struct rq *busiest);
|
||||
|
||||
static void deactivate_task(struct rq *rq, struct task_struct *p, int sleep);
|
||||
|
||||
static int pick_rt_task(struct rq *rq, struct task_struct *p, int cpu)
|
||||
|
||||
@@ -31,7 +31,7 @@ static int show_schedstat(struct seq_file *seq, void *v)
|
||||
rq->yld_act_empty, rq->yld_exp_empty, rq->yld_count,
|
||||
rq->sched_switch, rq->sched_count, rq->sched_goidle,
|
||||
rq->ttwu_count, rq->ttwu_local,
|
||||
rq->rq_sched_info.cpu_time,
|
||||
rq->rq_cpu_time,
|
||||
rq->rq_sched_info.run_delay, rq->rq_sched_info.pcount);
|
||||
|
||||
seq_printf(seq, "\n");
|
||||
@@ -123,7 +123,7 @@ static inline void
|
||||
rq_sched_info_depart(struct rq *rq, unsigned long long delta)
|
||||
{
|
||||
if (rq)
|
||||
rq->rq_sched_info.cpu_time += delta;
|
||||
rq->rq_cpu_time += delta;
|
||||
}
|
||||
|
||||
static inline void
|
||||
@@ -236,7 +236,6 @@ static inline void sched_info_depart(struct task_struct *t)
|
||||
unsigned long long delta = task_rq(t)->clock -
|
||||
t->sched_info.last_arrival;
|
||||
|
||||
t->sched_info.cpu_time += delta;
|
||||
rq_sched_info_depart(task_rq(t), delta);
|
||||
|
||||
if (t->state == TASK_RUNNING)
|
||||
|
||||
+41
-21
@@ -41,6 +41,8 @@
|
||||
|
||||
static struct kmem_cache *sigqueue_cachep;
|
||||
|
||||
DEFINE_TRACE(sched_signal_send);
|
||||
|
||||
static void __user *sig_handler(struct task_struct *t, int sig)
|
||||
{
|
||||
return t->sighand->action[sig - 1].sa.sa_handler;
|
||||
@@ -177,6 +179,11 @@ int next_signal(struct sigpending *pending, sigset_t *mask)
|
||||
return sig;
|
||||
}
|
||||
|
||||
/*
|
||||
* allocate a new signal queue record
|
||||
* - this may be called without locks if and only if t == current, otherwise an
|
||||
* appopriate lock must be held to stop the target task from exiting
|
||||
*/
|
||||
static struct sigqueue *__sigqueue_alloc(struct task_struct *t, gfp_t flags,
|
||||
int override_rlimit)
|
||||
{
|
||||
@@ -184,11 +191,12 @@ static struct sigqueue *__sigqueue_alloc(struct task_struct *t, gfp_t flags,
|
||||
struct user_struct *user;
|
||||
|
||||
/*
|
||||
* In order to avoid problems with "switch_user()", we want to make
|
||||
* sure that the compiler doesn't re-load "t->user"
|
||||
* We won't get problems with the target's UID changing under us
|
||||
* because changing it requires RCU be used, and if t != current, the
|
||||
* caller must be holding the RCU readlock (by way of a spinlock) and
|
||||
* we use RCU protection here
|
||||
*/
|
||||
user = t->user;
|
||||
barrier();
|
||||
user = get_uid(__task_cred(t)->user);
|
||||
atomic_inc(&user->sigpending);
|
||||
if (override_rlimit ||
|
||||
atomic_read(&user->sigpending) <=
|
||||
@@ -196,12 +204,14 @@ static struct sigqueue *__sigqueue_alloc(struct task_struct *t, gfp_t flags,
|
||||
q = kmem_cache_alloc(sigqueue_cachep, flags);
|
||||
if (unlikely(q == NULL)) {
|
||||
atomic_dec(&user->sigpending);
|
||||
free_uid(user);
|
||||
} else {
|
||||
INIT_LIST_HEAD(&q->list);
|
||||
q->flags = 0;
|
||||
q->user = get_uid(user);
|
||||
q->user = user;
|
||||
}
|
||||
return(q);
|
||||
|
||||
return q;
|
||||
}
|
||||
|
||||
static void __sigqueue_free(struct sigqueue *q)
|
||||
@@ -562,10 +572,12 @@ static int rm_from_queue(unsigned long mask, struct sigpending *s)
|
||||
|
||||
/*
|
||||
* Bad permissions for sending the signal
|
||||
* - the caller must hold at least the RCU read lock
|
||||
*/
|
||||
static int check_kill_permission(int sig, struct siginfo *info,
|
||||
struct task_struct *t)
|
||||
{
|
||||
const struct cred *cred = current_cred(), *tcred;
|
||||
struct pid *sid;
|
||||
int error;
|
||||
|
||||
@@ -579,8 +591,11 @@ static int check_kill_permission(int sig, struct siginfo *info,
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if ((current->euid ^ t->suid) && (current->euid ^ t->uid) &&
|
||||
(current->uid ^ t->suid) && (current->uid ^ t->uid) &&
|
||||
tcred = __task_cred(t);
|
||||
if ((cred->euid ^ tcred->suid) &&
|
||||
(cred->euid ^ tcred->uid) &&
|
||||
(cred->uid ^ tcred->suid) &&
|
||||
(cred->uid ^ tcred->uid) &&
|
||||
!capable(CAP_KILL)) {
|
||||
switch (sig) {
|
||||
case SIGCONT:
|
||||
@@ -844,7 +859,7 @@ static int send_signal(int sig, struct siginfo *info, struct task_struct *t,
|
||||
q->info.si_errno = 0;
|
||||
q->info.si_code = SI_USER;
|
||||
q->info.si_pid = task_pid_vnr(current);
|
||||
q->info.si_uid = current->uid;
|
||||
q->info.si_uid = current_uid();
|
||||
break;
|
||||
case (unsigned long) SEND_SIG_PRIV:
|
||||
q->info.si_signo = sig;
|
||||
@@ -1008,6 +1023,10 @@ struct sighand_struct *lock_task_sighand(struct task_struct *tsk, unsigned long
|
||||
return sighand;
|
||||
}
|
||||
|
||||
/*
|
||||
* send signal info to all the members of a group
|
||||
* - the caller must hold the RCU read lock at least
|
||||
*/
|
||||
int group_send_sig_info(int sig, struct siginfo *info, struct task_struct *p)
|
||||
{
|
||||
unsigned long flags;
|
||||
@@ -1029,8 +1048,8 @@ int group_send_sig_info(int sig, struct siginfo *info, struct task_struct *p)
|
||||
/*
|
||||
* __kill_pgrp_info() sends a signal to a process group: this is what the tty
|
||||
* control characters do (^C, ^Z etc)
|
||||
* - the caller must hold at least a readlock on tasklist_lock
|
||||
*/
|
||||
|
||||
int __kill_pgrp_info(int sig, struct siginfo *info, struct pid *pgrp)
|
||||
{
|
||||
struct task_struct *p = NULL;
|
||||
@@ -1086,6 +1105,7 @@ int kill_pid_info_as_uid(int sig, struct siginfo *info, struct pid *pid,
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
struct task_struct *p;
|
||||
const struct cred *pcred;
|
||||
|
||||
if (!valid_signal(sig))
|
||||
return ret;
|
||||
@@ -1096,9 +1116,11 @@ int kill_pid_info_as_uid(int sig, struct siginfo *info, struct pid *pid,
|
||||
ret = -ESRCH;
|
||||
goto out_unlock;
|
||||
}
|
||||
if ((info == SEND_SIG_NOINFO || (!is_si_special(info) && SI_FROMUSER(info)))
|
||||
&& (euid != p->suid) && (euid != p->uid)
|
||||
&& (uid != p->suid) && (uid != p->uid)) {
|
||||
pcred = __task_cred(p);
|
||||
if ((info == SEND_SIG_NOINFO ||
|
||||
(!is_si_special(info) && SI_FROMUSER(info))) &&
|
||||
euid != pcred->suid && euid != pcred->uid &&
|
||||
uid != pcred->suid && uid != pcred->uid) {
|
||||
ret = -EPERM;
|
||||
goto out_unlock;
|
||||
}
|
||||
@@ -1369,10 +1391,9 @@ int do_notify_parent(struct task_struct *tsk, int sig)
|
||||
*/
|
||||
rcu_read_lock();
|
||||
info.si_pid = task_pid_nr_ns(tsk, tsk->parent->nsproxy->pid_ns);
|
||||
info.si_uid = __task_cred(tsk)->uid;
|
||||
rcu_read_unlock();
|
||||
|
||||
info.si_uid = tsk->uid;
|
||||
|
||||
thread_group_cputime(tsk, &cputime);
|
||||
info.si_utime = cputime_to_jiffies(cputime.utime);
|
||||
info.si_stime = cputime_to_jiffies(cputime.stime);
|
||||
@@ -1440,10 +1461,9 @@ static void do_notify_parent_cldstop(struct task_struct *tsk, int why)
|
||||
*/
|
||||
rcu_read_lock();
|
||||
info.si_pid = task_pid_nr_ns(tsk, tsk->parent->nsproxy->pid_ns);
|
||||
info.si_uid = __task_cred(tsk)->uid;
|
||||
rcu_read_unlock();
|
||||
|
||||
info.si_uid = tsk->uid;
|
||||
|
||||
info.si_utime = cputime_to_clock_t(tsk->utime);
|
||||
info.si_stime = cputime_to_clock_t(tsk->stime);
|
||||
|
||||
@@ -1598,7 +1618,7 @@ void ptrace_notify(int exit_code)
|
||||
info.si_signo = SIGTRAP;
|
||||
info.si_code = exit_code;
|
||||
info.si_pid = task_pid_vnr(current);
|
||||
info.si_uid = current->uid;
|
||||
info.si_uid = current_uid();
|
||||
|
||||
/* Let the debugger run. */
|
||||
spin_lock_irq(¤t->sighand->siglock);
|
||||
@@ -1710,7 +1730,7 @@ static int ptrace_signal(int signr, siginfo_t *info,
|
||||
info->si_errno = 0;
|
||||
info->si_code = SI_USER;
|
||||
info->si_pid = task_pid_vnr(current->parent);
|
||||
info->si_uid = current->parent->uid;
|
||||
info->si_uid = task_uid(current->parent);
|
||||
}
|
||||
|
||||
/* If the (new) signal is now blocked, requeue it. */
|
||||
@@ -2211,7 +2231,7 @@ sys_kill(pid_t pid, int sig)
|
||||
info.si_errno = 0;
|
||||
info.si_code = SI_USER;
|
||||
info.si_pid = task_tgid_vnr(current);
|
||||
info.si_uid = current->uid;
|
||||
info.si_uid = current_uid();
|
||||
|
||||
return kill_something_info(sig, &info, pid);
|
||||
}
|
||||
@@ -2228,7 +2248,7 @@ static int do_tkill(pid_t tgid, pid_t pid, int sig)
|
||||
info.si_errno = 0;
|
||||
info.si_code = SI_TKILL;
|
||||
info.si_pid = task_tgid_vnr(current);
|
||||
info.si_uid = current->uid;
|
||||
info.si_uid = current_uid();
|
||||
|
||||
rcu_read_lock();
|
||||
p = find_task_by_vpid(pid);
|
||||
|
||||
+336
-258
@@ -112,12 +112,17 @@ EXPORT_SYMBOL(cad_pid);
|
||||
|
||||
void (*pm_power_off_prepare)(void);
|
||||
|
||||
/*
|
||||
* set the priority of a task
|
||||
* - the caller must hold the RCU read lock
|
||||
*/
|
||||
static int set_one_prio(struct task_struct *p, int niceval, int error)
|
||||
{
|
||||
const struct cred *cred = current_cred(), *pcred = __task_cred(p);
|
||||
int no_nice;
|
||||
|
||||
if (p->uid != current->euid &&
|
||||
p->euid != current->euid && !capable(CAP_SYS_NICE)) {
|
||||
if (pcred->uid != cred->euid &&
|
||||
pcred->euid != cred->euid && !capable(CAP_SYS_NICE)) {
|
||||
error = -EPERM;
|
||||
goto out;
|
||||
}
|
||||
@@ -141,6 +146,7 @@ asmlinkage long sys_setpriority(int which, int who, int niceval)
|
||||
{
|
||||
struct task_struct *g, *p;
|
||||
struct user_struct *user;
|
||||
const struct cred *cred = current_cred();
|
||||
int error = -EINVAL;
|
||||
struct pid *pgrp;
|
||||
|
||||
@@ -174,18 +180,18 @@ asmlinkage long sys_setpriority(int which, int who, int niceval)
|
||||
} while_each_pid_thread(pgrp, PIDTYPE_PGID, p);
|
||||
break;
|
||||
case PRIO_USER:
|
||||
user = current->user;
|
||||
user = (struct user_struct *) cred->user;
|
||||
if (!who)
|
||||
who = current->uid;
|
||||
else
|
||||
if ((who != current->uid) && !(user = find_user(who)))
|
||||
goto out_unlock; /* No processes for this user */
|
||||
who = cred->uid;
|
||||
else if ((who != cred->uid) &&
|
||||
!(user = find_user(who)))
|
||||
goto out_unlock; /* No processes for this user */
|
||||
|
||||
do_each_thread(g, p)
|
||||
if (p->uid == who)
|
||||
if (__task_cred(p)->uid == who)
|
||||
error = set_one_prio(p, niceval, error);
|
||||
while_each_thread(g, p);
|
||||
if (who != current->uid)
|
||||
if (who != cred->uid)
|
||||
free_uid(user); /* For find_user() */
|
||||
break;
|
||||
}
|
||||
@@ -205,6 +211,7 @@ asmlinkage long sys_getpriority(int which, int who)
|
||||
{
|
||||
struct task_struct *g, *p;
|
||||
struct user_struct *user;
|
||||
const struct cred *cred = current_cred();
|
||||
long niceval, retval = -ESRCH;
|
||||
struct pid *pgrp;
|
||||
|
||||
@@ -236,21 +243,21 @@ asmlinkage long sys_getpriority(int which, int who)
|
||||
} while_each_pid_thread(pgrp, PIDTYPE_PGID, p);
|
||||
break;
|
||||
case PRIO_USER:
|
||||
user = current->user;
|
||||
user = (struct user_struct *) cred->user;
|
||||
if (!who)
|
||||
who = current->uid;
|
||||
else
|
||||
if ((who != current->uid) && !(user = find_user(who)))
|
||||
goto out_unlock; /* No processes for this user */
|
||||
who = cred->uid;
|
||||
else if ((who != cred->uid) &&
|
||||
!(user = find_user(who)))
|
||||
goto out_unlock; /* No processes for this user */
|
||||
|
||||
do_each_thread(g, p)
|
||||
if (p->uid == who) {
|
||||
if (__task_cred(p)->uid == who) {
|
||||
niceval = 20 - task_nice(p);
|
||||
if (niceval > retval)
|
||||
retval = niceval;
|
||||
}
|
||||
while_each_thread(g, p);
|
||||
if (who != current->uid)
|
||||
if (who != cred->uid)
|
||||
free_uid(user); /* for find_user() */
|
||||
break;
|
||||
}
|
||||
@@ -472,46 +479,48 @@ void ctrl_alt_del(void)
|
||||
*/
|
||||
asmlinkage long sys_setregid(gid_t rgid, gid_t egid)
|
||||
{
|
||||
int old_rgid = current->gid;
|
||||
int old_egid = current->egid;
|
||||
int new_rgid = old_rgid;
|
||||
int new_egid = old_egid;
|
||||
const struct cred *old;
|
||||
struct cred *new;
|
||||
int retval;
|
||||
|
||||
new = prepare_creds();
|
||||
if (!new)
|
||||
return -ENOMEM;
|
||||
old = current_cred();
|
||||
|
||||
retval = security_task_setgid(rgid, egid, (gid_t)-1, LSM_SETID_RE);
|
||||
if (retval)
|
||||
return retval;
|
||||
goto error;
|
||||
|
||||
retval = -EPERM;
|
||||
if (rgid != (gid_t) -1) {
|
||||
if ((old_rgid == rgid) ||
|
||||
(current->egid==rgid) ||
|
||||
if (old->gid == rgid ||
|
||||
old->egid == rgid ||
|
||||
capable(CAP_SETGID))
|
||||
new_rgid = rgid;
|
||||
new->gid = rgid;
|
||||
else
|
||||
return -EPERM;
|
||||
goto error;
|
||||
}
|
||||
if (egid != (gid_t) -1) {
|
||||
if ((old_rgid == egid) ||
|
||||
(current->egid == egid) ||
|
||||
(current->sgid == egid) ||
|
||||
if (old->gid == egid ||
|
||||
old->egid == egid ||
|
||||
old->sgid == egid ||
|
||||
capable(CAP_SETGID))
|
||||
new_egid = egid;
|
||||
new->egid = egid;
|
||||
else
|
||||
return -EPERM;
|
||||
}
|
||||
if (new_egid != old_egid) {
|
||||
set_dumpable(current->mm, suid_dumpable);
|
||||
smp_wmb();
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (rgid != (gid_t) -1 ||
|
||||
(egid != (gid_t) -1 && egid != old_rgid))
|
||||
current->sgid = new_egid;
|
||||
current->fsgid = new_egid;
|
||||
current->egid = new_egid;
|
||||
current->gid = new_rgid;
|
||||
key_fsgid_changed(current);
|
||||
proc_id_connector(current, PROC_EVENT_GID);
|
||||
return 0;
|
||||
(egid != (gid_t) -1 && egid != old->gid))
|
||||
new->sgid = new->egid;
|
||||
new->fsgid = new->egid;
|
||||
|
||||
return commit_creds(new);
|
||||
|
||||
error:
|
||||
abort_creds(new);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -521,56 +530,54 @@ asmlinkage long sys_setregid(gid_t rgid, gid_t egid)
|
||||
*/
|
||||
asmlinkage long sys_setgid(gid_t gid)
|
||||
{
|
||||
int old_egid = current->egid;
|
||||
const struct cred *old;
|
||||
struct cred *new;
|
||||
int retval;
|
||||
|
||||
new = prepare_creds();
|
||||
if (!new)
|
||||
return -ENOMEM;
|
||||
old = current_cred();
|
||||
|
||||
retval = security_task_setgid(gid, (gid_t)-1, (gid_t)-1, LSM_SETID_ID);
|
||||
if (retval)
|
||||
return retval;
|
||||
goto error;
|
||||
|
||||
if (capable(CAP_SETGID)) {
|
||||
if (old_egid != gid) {
|
||||
set_dumpable(current->mm, suid_dumpable);
|
||||
smp_wmb();
|
||||
}
|
||||
current->gid = current->egid = current->sgid = current->fsgid = gid;
|
||||
} else if ((gid == current->gid) || (gid == current->sgid)) {
|
||||
if (old_egid != gid) {
|
||||
set_dumpable(current->mm, suid_dumpable);
|
||||
smp_wmb();
|
||||
}
|
||||
current->egid = current->fsgid = gid;
|
||||
}
|
||||
retval = -EPERM;
|
||||
if (capable(CAP_SETGID))
|
||||
new->gid = new->egid = new->sgid = new->fsgid = gid;
|
||||
else if (gid == old->gid || gid == old->sgid)
|
||||
new->egid = new->fsgid = gid;
|
||||
else
|
||||
return -EPERM;
|
||||
goto error;
|
||||
|
||||
key_fsgid_changed(current);
|
||||
proc_id_connector(current, PROC_EVENT_GID);
|
||||
return 0;
|
||||
return commit_creds(new);
|
||||
|
||||
error:
|
||||
abort_creds(new);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int set_user(uid_t new_ruid, int dumpclear)
|
||||
/*
|
||||
* change the user struct in a credentials set to match the new UID
|
||||
*/
|
||||
static int set_user(struct cred *new)
|
||||
{
|
||||
struct user_struct *new_user;
|
||||
|
||||
new_user = alloc_uid(current->nsproxy->user_ns, new_ruid);
|
||||
new_user = alloc_uid(current_user_ns(), new->uid);
|
||||
if (!new_user)
|
||||
return -EAGAIN;
|
||||
|
||||
if (atomic_read(&new_user->processes) >=
|
||||
current->signal->rlim[RLIMIT_NPROC].rlim_cur &&
|
||||
new_user != current->nsproxy->user_ns->root_user) {
|
||||
new_user != INIT_USER) {
|
||||
free_uid(new_user);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
switch_uid(new_user);
|
||||
|
||||
if (dumpclear) {
|
||||
set_dumpable(current->mm, suid_dumpable);
|
||||
smp_wmb();
|
||||
}
|
||||
current->uid = new_ruid;
|
||||
free_uid(new->user);
|
||||
new->user = new_user;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -591,54 +598,56 @@ static int set_user(uid_t new_ruid, int dumpclear)
|
||||
*/
|
||||
asmlinkage long sys_setreuid(uid_t ruid, uid_t euid)
|
||||
{
|
||||
int old_ruid, old_euid, old_suid, new_ruid, new_euid;
|
||||
const struct cred *old;
|
||||
struct cred *new;
|
||||
int retval;
|
||||
|
||||
new = prepare_creds();
|
||||
if (!new)
|
||||
return -ENOMEM;
|
||||
old = current_cred();
|
||||
|
||||
retval = security_task_setuid(ruid, euid, (uid_t)-1, LSM_SETID_RE);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
new_ruid = old_ruid = current->uid;
|
||||
new_euid = old_euid = current->euid;
|
||||
old_suid = current->suid;
|
||||
goto error;
|
||||
|
||||
retval = -EPERM;
|
||||
if (ruid != (uid_t) -1) {
|
||||
new_ruid = ruid;
|
||||
if ((old_ruid != ruid) &&
|
||||
(current->euid != ruid) &&
|
||||
new->uid = ruid;
|
||||
if (old->uid != ruid &&
|
||||
old->euid != ruid &&
|
||||
!capable(CAP_SETUID))
|
||||
return -EPERM;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (euid != (uid_t) -1) {
|
||||
new_euid = euid;
|
||||
if ((old_ruid != euid) &&
|
||||
(current->euid != euid) &&
|
||||
(current->suid != euid) &&
|
||||
new->euid = euid;
|
||||
if (old->uid != euid &&
|
||||
old->euid != euid &&
|
||||
old->suid != euid &&
|
||||
!capable(CAP_SETUID))
|
||||
return -EPERM;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (new_ruid != old_ruid && set_user(new_ruid, new_euid != old_euid) < 0)
|
||||
return -EAGAIN;
|
||||
retval = -EAGAIN;
|
||||
if (new->uid != old->uid && set_user(new) < 0)
|
||||
goto error;
|
||||
|
||||
if (new_euid != old_euid) {
|
||||
set_dumpable(current->mm, suid_dumpable);
|
||||
smp_wmb();
|
||||
}
|
||||
current->fsuid = current->euid = new_euid;
|
||||
if (ruid != (uid_t) -1 ||
|
||||
(euid != (uid_t) -1 && euid != old_ruid))
|
||||
current->suid = current->euid;
|
||||
current->fsuid = current->euid;
|
||||
(euid != (uid_t) -1 && euid != old->uid))
|
||||
new->suid = new->euid;
|
||||
new->fsuid = new->euid;
|
||||
|
||||
key_fsuid_changed(current);
|
||||
proc_id_connector(current, PROC_EVENT_UID);
|
||||
retval = security_task_fix_setuid(new, old, LSM_SETID_RE);
|
||||
if (retval < 0)
|
||||
goto error;
|
||||
|
||||
return security_task_post_setuid(old_ruid, old_euid, old_suid, LSM_SETID_RE);
|
||||
return commit_creds(new);
|
||||
|
||||
error:
|
||||
abort_creds(new);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* setuid() is implemented like SysV with SAVED_IDS
|
||||
@@ -653,36 +662,41 @@ asmlinkage long sys_setreuid(uid_t ruid, uid_t euid)
|
||||
*/
|
||||
asmlinkage long sys_setuid(uid_t uid)
|
||||
{
|
||||
int old_euid = current->euid;
|
||||
int old_ruid, old_suid, new_suid;
|
||||
const struct cred *old;
|
||||
struct cred *new;
|
||||
int retval;
|
||||
|
||||
new = prepare_creds();
|
||||
if (!new)
|
||||
return -ENOMEM;
|
||||
old = current_cred();
|
||||
|
||||
retval = security_task_setuid(uid, (uid_t)-1, (uid_t)-1, LSM_SETID_ID);
|
||||
if (retval)
|
||||
return retval;
|
||||
goto error;
|
||||
|
||||
old_ruid = current->uid;
|
||||
old_suid = current->suid;
|
||||
new_suid = old_suid;
|
||||
|
||||
retval = -EPERM;
|
||||
if (capable(CAP_SETUID)) {
|
||||
if (uid != old_ruid && set_user(uid, old_euid != uid) < 0)
|
||||
return -EAGAIN;
|
||||
new_suid = uid;
|
||||
} else if ((uid != current->uid) && (uid != new_suid))
|
||||
return -EPERM;
|
||||
|
||||
if (old_euid != uid) {
|
||||
set_dumpable(current->mm, suid_dumpable);
|
||||
smp_wmb();
|
||||
new->suid = new->uid = uid;
|
||||
if (uid != old->uid && set_user(new) < 0) {
|
||||
retval = -EAGAIN;
|
||||
goto error;
|
||||
}
|
||||
} else if (uid != old->uid && uid != new->suid) {
|
||||
goto error;
|
||||
}
|
||||
current->fsuid = current->euid = uid;
|
||||
current->suid = new_suid;
|
||||
|
||||
key_fsuid_changed(current);
|
||||
proc_id_connector(current, PROC_EVENT_UID);
|
||||
new->fsuid = new->euid = uid;
|
||||
|
||||
return security_task_post_setuid(old_ruid, old_euid, old_suid, LSM_SETID_ID);
|
||||
retval = security_task_fix_setuid(new, old, LSM_SETID_ID);
|
||||
if (retval < 0)
|
||||
goto error;
|
||||
|
||||
return commit_creds(new);
|
||||
|
||||
error:
|
||||
abort_creds(new);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
@@ -692,54 +706,63 @@ asmlinkage long sys_setuid(uid_t uid)
|
||||
*/
|
||||
asmlinkage long sys_setresuid(uid_t ruid, uid_t euid, uid_t suid)
|
||||
{
|
||||
int old_ruid = current->uid;
|
||||
int old_euid = current->euid;
|
||||
int old_suid = current->suid;
|
||||
const struct cred *old;
|
||||
struct cred *new;
|
||||
int retval;
|
||||
|
||||
new = prepare_creds();
|
||||
if (!new)
|
||||
return -ENOMEM;
|
||||
|
||||
retval = security_task_setuid(ruid, euid, suid, LSM_SETID_RES);
|
||||
if (retval)
|
||||
return retval;
|
||||
goto error;
|
||||
old = current_cred();
|
||||
|
||||
retval = -EPERM;
|
||||
if (!capable(CAP_SETUID)) {
|
||||
if ((ruid != (uid_t) -1) && (ruid != current->uid) &&
|
||||
(ruid != current->euid) && (ruid != current->suid))
|
||||
return -EPERM;
|
||||
if ((euid != (uid_t) -1) && (euid != current->uid) &&
|
||||
(euid != current->euid) && (euid != current->suid))
|
||||
return -EPERM;
|
||||
if ((suid != (uid_t) -1) && (suid != current->uid) &&
|
||||
(suid != current->euid) && (suid != current->suid))
|
||||
return -EPERM;
|
||||
if (ruid != (uid_t) -1 && ruid != old->uid &&
|
||||
ruid != old->euid && ruid != old->suid)
|
||||
goto error;
|
||||
if (euid != (uid_t) -1 && euid != old->uid &&
|
||||
euid != old->euid && euid != old->suid)
|
||||
goto error;
|
||||
if (suid != (uid_t) -1 && suid != old->uid &&
|
||||
suid != old->euid && suid != old->suid)
|
||||
goto error;
|
||||
}
|
||||
|
||||
retval = -EAGAIN;
|
||||
if (ruid != (uid_t) -1) {
|
||||
if (ruid != current->uid && set_user(ruid, euid != current->euid) < 0)
|
||||
return -EAGAIN;
|
||||
new->uid = ruid;
|
||||
if (ruid != old->uid && set_user(new) < 0)
|
||||
goto error;
|
||||
}
|
||||
if (euid != (uid_t) -1) {
|
||||
if (euid != current->euid) {
|
||||
set_dumpable(current->mm, suid_dumpable);
|
||||
smp_wmb();
|
||||
}
|
||||
current->euid = euid;
|
||||
}
|
||||
current->fsuid = current->euid;
|
||||
if (euid != (uid_t) -1)
|
||||
new->euid = euid;
|
||||
if (suid != (uid_t) -1)
|
||||
current->suid = suid;
|
||||
new->suid = suid;
|
||||
new->fsuid = new->euid;
|
||||
|
||||
key_fsuid_changed(current);
|
||||
proc_id_connector(current, PROC_EVENT_UID);
|
||||
retval = security_task_fix_setuid(new, old, LSM_SETID_RES);
|
||||
if (retval < 0)
|
||||
goto error;
|
||||
|
||||
return security_task_post_setuid(old_ruid, old_euid, old_suid, LSM_SETID_RES);
|
||||
return commit_creds(new);
|
||||
|
||||
error:
|
||||
abort_creds(new);
|
||||
return retval;
|
||||
}
|
||||
|
||||
asmlinkage long sys_getresuid(uid_t __user *ruid, uid_t __user *euid, uid_t __user *suid)
|
||||
{
|
||||
const struct cred *cred = current_cred();
|
||||
int retval;
|
||||
|
||||
if (!(retval = put_user(current->uid, ruid)) &&
|
||||
!(retval = put_user(current->euid, euid)))
|
||||
retval = put_user(current->suid, suid);
|
||||
if (!(retval = put_user(cred->uid, ruid)) &&
|
||||
!(retval = put_user(cred->euid, euid)))
|
||||
retval = put_user(cred->suid, suid);
|
||||
|
||||
return retval;
|
||||
}
|
||||
@@ -749,48 +772,55 @@ asmlinkage long sys_getresuid(uid_t __user *ruid, uid_t __user *euid, uid_t __us
|
||||
*/
|
||||
asmlinkage long sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid)
|
||||
{
|
||||
const struct cred *old;
|
||||
struct cred *new;
|
||||
int retval;
|
||||
|
||||
new = prepare_creds();
|
||||
if (!new)
|
||||
return -ENOMEM;
|
||||
old = current_cred();
|
||||
|
||||
retval = security_task_setgid(rgid, egid, sgid, LSM_SETID_RES);
|
||||
if (retval)
|
||||
return retval;
|
||||
goto error;
|
||||
|
||||
retval = -EPERM;
|
||||
if (!capable(CAP_SETGID)) {
|
||||
if ((rgid != (gid_t) -1) && (rgid != current->gid) &&
|
||||
(rgid != current->egid) && (rgid != current->sgid))
|
||||
return -EPERM;
|
||||
if ((egid != (gid_t) -1) && (egid != current->gid) &&
|
||||
(egid != current->egid) && (egid != current->sgid))
|
||||
return -EPERM;
|
||||
if ((sgid != (gid_t) -1) && (sgid != current->gid) &&
|
||||
(sgid != current->egid) && (sgid != current->sgid))
|
||||
return -EPERM;
|
||||
if (rgid != (gid_t) -1 && rgid != old->gid &&
|
||||
rgid != old->egid && rgid != old->sgid)
|
||||
goto error;
|
||||
if (egid != (gid_t) -1 && egid != old->gid &&
|
||||
egid != old->egid && egid != old->sgid)
|
||||
goto error;
|
||||
if (sgid != (gid_t) -1 && sgid != old->gid &&
|
||||
sgid != old->egid && sgid != old->sgid)
|
||||
goto error;
|
||||
}
|
||||
if (egid != (gid_t) -1) {
|
||||
if (egid != current->egid) {
|
||||
set_dumpable(current->mm, suid_dumpable);
|
||||
smp_wmb();
|
||||
}
|
||||
current->egid = egid;
|
||||
}
|
||||
current->fsgid = current->egid;
|
||||
if (rgid != (gid_t) -1)
|
||||
current->gid = rgid;
|
||||
if (sgid != (gid_t) -1)
|
||||
current->sgid = sgid;
|
||||
|
||||
key_fsgid_changed(current);
|
||||
proc_id_connector(current, PROC_EVENT_GID);
|
||||
return 0;
|
||||
if (rgid != (gid_t) -1)
|
||||
new->gid = rgid;
|
||||
if (egid != (gid_t) -1)
|
||||
new->egid = egid;
|
||||
if (sgid != (gid_t) -1)
|
||||
new->sgid = sgid;
|
||||
new->fsgid = new->egid;
|
||||
|
||||
return commit_creds(new);
|
||||
|
||||
error:
|
||||
abort_creds(new);
|
||||
return retval;
|
||||
}
|
||||
|
||||
asmlinkage long sys_getresgid(gid_t __user *rgid, gid_t __user *egid, gid_t __user *sgid)
|
||||
{
|
||||
const struct cred *cred = current_cred();
|
||||
int retval;
|
||||
|
||||
if (!(retval = put_user(current->gid, rgid)) &&
|
||||
!(retval = put_user(current->egid, egid)))
|
||||
retval = put_user(current->sgid, sgid);
|
||||
if (!(retval = put_user(cred->gid, rgid)) &&
|
||||
!(retval = put_user(cred->egid, egid)))
|
||||
retval = put_user(cred->sgid, sgid);
|
||||
|
||||
return retval;
|
||||
}
|
||||
@@ -804,27 +834,35 @@ asmlinkage long sys_getresgid(gid_t __user *rgid, gid_t __user *egid, gid_t __us
|
||||
*/
|
||||
asmlinkage long sys_setfsuid(uid_t uid)
|
||||
{
|
||||
int old_fsuid;
|
||||
const struct cred *old;
|
||||
struct cred *new;
|
||||
uid_t old_fsuid;
|
||||
|
||||
old_fsuid = current->fsuid;
|
||||
if (security_task_setuid(uid, (uid_t)-1, (uid_t)-1, LSM_SETID_FS))
|
||||
return old_fsuid;
|
||||
new = prepare_creds();
|
||||
if (!new)
|
||||
return current_fsuid();
|
||||
old = current_cred();
|
||||
old_fsuid = old->fsuid;
|
||||
|
||||
if (uid == current->uid || uid == current->euid ||
|
||||
uid == current->suid || uid == current->fsuid ||
|
||||
if (security_task_setuid(uid, (uid_t)-1, (uid_t)-1, LSM_SETID_FS) < 0)
|
||||
goto error;
|
||||
|
||||
if (uid == old->uid || uid == old->euid ||
|
||||
uid == old->suid || uid == old->fsuid ||
|
||||
capable(CAP_SETUID)) {
|
||||
if (uid != old_fsuid) {
|
||||
set_dumpable(current->mm, suid_dumpable);
|
||||
smp_wmb();
|
||||
new->fsuid = uid;
|
||||
if (security_task_fix_setuid(new, old, LSM_SETID_FS) == 0)
|
||||
goto change_okay;
|
||||
}
|
||||
current->fsuid = uid;
|
||||
}
|
||||
|
||||
key_fsuid_changed(current);
|
||||
proc_id_connector(current, PROC_EVENT_UID);
|
||||
|
||||
security_task_post_setuid(old_fsuid, (uid_t)-1, (uid_t)-1, LSM_SETID_FS);
|
||||
error:
|
||||
abort_creds(new);
|
||||
return old_fsuid;
|
||||
|
||||
change_okay:
|
||||
commit_creds(new);
|
||||
return old_fsuid;
|
||||
}
|
||||
|
||||
@@ -833,23 +871,34 @@ asmlinkage long sys_setfsuid(uid_t uid)
|
||||
*/
|
||||
asmlinkage long sys_setfsgid(gid_t gid)
|
||||
{
|
||||
int old_fsgid;
|
||||
const struct cred *old;
|
||||
struct cred *new;
|
||||
gid_t old_fsgid;
|
||||
|
||||
new = prepare_creds();
|
||||
if (!new)
|
||||
return current_fsgid();
|
||||
old = current_cred();
|
||||
old_fsgid = old->fsgid;
|
||||
|
||||
old_fsgid = current->fsgid;
|
||||
if (security_task_setgid(gid, (gid_t)-1, (gid_t)-1, LSM_SETID_FS))
|
||||
return old_fsgid;
|
||||
goto error;
|
||||
|
||||
if (gid == current->gid || gid == current->egid ||
|
||||
gid == current->sgid || gid == current->fsgid ||
|
||||
if (gid == old->gid || gid == old->egid ||
|
||||
gid == old->sgid || gid == old->fsgid ||
|
||||
capable(CAP_SETGID)) {
|
||||
if (gid != old_fsgid) {
|
||||
set_dumpable(current->mm, suid_dumpable);
|
||||
smp_wmb();
|
||||
new->fsgid = gid;
|
||||
goto change_okay;
|
||||
}
|
||||
current->fsgid = gid;
|
||||
key_fsgid_changed(current);
|
||||
proc_id_connector(current, PROC_EVENT_GID);
|
||||
}
|
||||
|
||||
error:
|
||||
abort_creds(new);
|
||||
return old_fsgid;
|
||||
|
||||
change_okay:
|
||||
commit_creds(new);
|
||||
return old_fsgid;
|
||||
}
|
||||
|
||||
@@ -1118,7 +1167,7 @@ EXPORT_SYMBOL(groups_free);
|
||||
|
||||
/* export the group_info to a user-space array */
|
||||
static int groups_to_user(gid_t __user *grouplist,
|
||||
struct group_info *group_info)
|
||||
const struct group_info *group_info)
|
||||
{
|
||||
int i;
|
||||
unsigned int count = group_info->ngroups;
|
||||
@@ -1186,7 +1235,7 @@ static void groups_sort(struct group_info *group_info)
|
||||
}
|
||||
|
||||
/* a simple bsearch */
|
||||
int groups_search(struct group_info *group_info, gid_t grp)
|
||||
int groups_search(const struct group_info *group_info, gid_t grp)
|
||||
{
|
||||
unsigned int left, right;
|
||||
|
||||
@@ -1208,51 +1257,74 @@ int groups_search(struct group_info *group_info, gid_t grp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* validate and set current->group_info */
|
||||
int set_current_groups(struct group_info *group_info)
|
||||
/**
|
||||
* set_groups - Change a group subscription in a set of credentials
|
||||
* @new: The newly prepared set of credentials to alter
|
||||
* @group_info: The group list to install
|
||||
*
|
||||
* Validate a group subscription and, if valid, insert it into a set
|
||||
* of credentials.
|
||||
*/
|
||||
int set_groups(struct cred *new, struct group_info *group_info)
|
||||
{
|
||||
int retval;
|
||||
struct group_info *old_info;
|
||||
|
||||
retval = security_task_setgroups(group_info);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
put_group_info(new->group_info);
|
||||
groups_sort(group_info);
|
||||
get_group_info(group_info);
|
||||
|
||||
task_lock(current);
|
||||
old_info = current->group_info;
|
||||
current->group_info = group_info;
|
||||
task_unlock(current);
|
||||
|
||||
put_group_info(old_info);
|
||||
|
||||
new->group_info = group_info;
|
||||
return 0;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(set_groups);
|
||||
|
||||
/**
|
||||
* set_current_groups - Change current's group subscription
|
||||
* @group_info: The group list to impose
|
||||
*
|
||||
* Validate a group subscription and, if valid, impose it upon current's task
|
||||
* security record.
|
||||
*/
|
||||
int set_current_groups(struct group_info *group_info)
|
||||
{
|
||||
struct cred *new;
|
||||
int ret;
|
||||
|
||||
new = prepare_creds();
|
||||
if (!new)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = set_groups(new, group_info);
|
||||
if (ret < 0) {
|
||||
abort_creds(new);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return commit_creds(new);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(set_current_groups);
|
||||
|
||||
asmlinkage long sys_getgroups(int gidsetsize, gid_t __user *grouplist)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
/*
|
||||
* SMP: Nobody else can change our grouplist. Thus we are
|
||||
* safe.
|
||||
*/
|
||||
const struct cred *cred = current_cred();
|
||||
int i;
|
||||
|
||||
if (gidsetsize < 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* no need to grab task_lock here; it cannot change */
|
||||
i = current->group_info->ngroups;
|
||||
i = cred->group_info->ngroups;
|
||||
if (gidsetsize) {
|
||||
if (i > gidsetsize) {
|
||||
i = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (groups_to_user(grouplist, current->group_info)) {
|
||||
if (groups_to_user(grouplist, cred->group_info)) {
|
||||
i = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
@@ -1296,9 +1368,11 @@ asmlinkage long sys_setgroups(int gidsetsize, gid_t __user *grouplist)
|
||||
*/
|
||||
int in_group_p(gid_t grp)
|
||||
{
|
||||
const struct cred *cred = current_cred();
|
||||
int retval = 1;
|
||||
if (grp != current->fsgid)
|
||||
retval = groups_search(current->group_info, grp);
|
||||
|
||||
if (grp != cred->fsgid)
|
||||
retval = groups_search(cred->group_info, grp);
|
||||
return retval;
|
||||
}
|
||||
|
||||
@@ -1306,9 +1380,11 @@ EXPORT_SYMBOL(in_group_p);
|
||||
|
||||
int in_egroup_p(gid_t grp)
|
||||
{
|
||||
const struct cred *cred = current_cred();
|
||||
int retval = 1;
|
||||
if (grp != current->egid)
|
||||
retval = groups_search(current->group_info, grp);
|
||||
|
||||
if (grp != cred->egid)
|
||||
retval = groups_search(cred->group_info, grp);
|
||||
return retval;
|
||||
}
|
||||
|
||||
@@ -1624,50 +1700,56 @@ asmlinkage long sys_umask(int mask)
|
||||
asmlinkage long sys_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
unsigned long arg4, unsigned long arg5)
|
||||
{
|
||||
long error = 0;
|
||||
struct task_struct *me = current;
|
||||
unsigned char comm[sizeof(me->comm)];
|
||||
long error;
|
||||
|
||||
if (security_task_prctl(option, arg2, arg3, arg4, arg5, &error))
|
||||
error = security_task_prctl(option, arg2, arg3, arg4, arg5);
|
||||
if (error != -ENOSYS)
|
||||
return error;
|
||||
|
||||
error = 0;
|
||||
switch (option) {
|
||||
case PR_SET_PDEATHSIG:
|
||||
if (!valid_signal(arg2)) {
|
||||
error = -EINVAL;
|
||||
break;
|
||||
}
|
||||
current->pdeath_signal = arg2;
|
||||
me->pdeath_signal = arg2;
|
||||
error = 0;
|
||||
break;
|
||||
case PR_GET_PDEATHSIG:
|
||||
error = put_user(current->pdeath_signal, (int __user *)arg2);
|
||||
error = put_user(me->pdeath_signal, (int __user *)arg2);
|
||||
break;
|
||||
case PR_GET_DUMPABLE:
|
||||
error = get_dumpable(current->mm);
|
||||
error = get_dumpable(me->mm);
|
||||
break;
|
||||
case PR_SET_DUMPABLE:
|
||||
if (arg2 < 0 || arg2 > 1) {
|
||||
error = -EINVAL;
|
||||
break;
|
||||
}
|
||||
set_dumpable(current->mm, arg2);
|
||||
set_dumpable(me->mm, arg2);
|
||||
error = 0;
|
||||
break;
|
||||
|
||||
case PR_SET_UNALIGN:
|
||||
error = SET_UNALIGN_CTL(current, arg2);
|
||||
error = SET_UNALIGN_CTL(me, arg2);
|
||||
break;
|
||||
case PR_GET_UNALIGN:
|
||||
error = GET_UNALIGN_CTL(current, arg2);
|
||||
error = GET_UNALIGN_CTL(me, arg2);
|
||||
break;
|
||||
case PR_SET_FPEMU:
|
||||
error = SET_FPEMU_CTL(current, arg2);
|
||||
error = SET_FPEMU_CTL(me, arg2);
|
||||
break;
|
||||
case PR_GET_FPEMU:
|
||||
error = GET_FPEMU_CTL(current, arg2);
|
||||
error = GET_FPEMU_CTL(me, arg2);
|
||||
break;
|
||||
case PR_SET_FPEXC:
|
||||
error = SET_FPEXC_CTL(current, arg2);
|
||||
error = SET_FPEXC_CTL(me, arg2);
|
||||
break;
|
||||
case PR_GET_FPEXC:
|
||||
error = GET_FPEXC_CTL(current, arg2);
|
||||
error = GET_FPEXC_CTL(me, arg2);
|
||||
break;
|
||||
case PR_GET_TIMING:
|
||||
error = PR_TIMING_STATISTICAL;
|
||||
@@ -1675,33 +1757,28 @@ asmlinkage long sys_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
case PR_SET_TIMING:
|
||||
if (arg2 != PR_TIMING_STATISTICAL)
|
||||
error = -EINVAL;
|
||||
else
|
||||
error = 0;
|
||||
break;
|
||||
|
||||
case PR_SET_NAME: {
|
||||
struct task_struct *me = current;
|
||||
unsigned char ncomm[sizeof(me->comm)];
|
||||
|
||||
ncomm[sizeof(me->comm)-1] = 0;
|
||||
if (strncpy_from_user(ncomm, (char __user *)arg2,
|
||||
sizeof(me->comm)-1) < 0)
|
||||
case PR_SET_NAME:
|
||||
comm[sizeof(me->comm)-1] = 0;
|
||||
if (strncpy_from_user(comm, (char __user *)arg2,
|
||||
sizeof(me->comm) - 1) < 0)
|
||||
return -EFAULT;
|
||||
set_task_comm(me, ncomm);
|
||||
set_task_comm(me, comm);
|
||||
return 0;
|
||||
}
|
||||
case PR_GET_NAME: {
|
||||
struct task_struct *me = current;
|
||||
unsigned char tcomm[sizeof(me->comm)];
|
||||
|
||||
get_task_comm(tcomm, me);
|
||||
if (copy_to_user((char __user *)arg2, tcomm, sizeof(tcomm)))
|
||||
case PR_GET_NAME:
|
||||
get_task_comm(comm, me);
|
||||
if (copy_to_user((char __user *)arg2, comm,
|
||||
sizeof(comm)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
case PR_GET_ENDIAN:
|
||||
error = GET_ENDIAN(current, arg2);
|
||||
error = GET_ENDIAN(me, arg2);
|
||||
break;
|
||||
case PR_SET_ENDIAN:
|
||||
error = SET_ENDIAN(current, arg2);
|
||||
error = SET_ENDIAN(me, arg2);
|
||||
break;
|
||||
|
||||
case PR_GET_SECCOMP:
|
||||
@@ -1725,6 +1802,7 @@ asmlinkage long sys_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
current->default_timer_slack_ns;
|
||||
else
|
||||
current->timer_slack_ns = arg2;
|
||||
error = 0;
|
||||
break;
|
||||
default:
|
||||
error = -EINVAL;
|
||||
|
||||
+21
-1
@@ -487,6 +487,26 @@ static struct ctl_table kern_table[] = {
|
||||
.proc_handler = &ftrace_enable_sysctl,
|
||||
},
|
||||
#endif
|
||||
#ifdef CONFIG_STACK_TRACER
|
||||
{
|
||||
.ctl_name = CTL_UNNUMBERED,
|
||||
.procname = "stack_tracer_enabled",
|
||||
.data = &stack_tracer_enabled,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &stack_trace_sysctl,
|
||||
},
|
||||
#endif
|
||||
#ifdef CONFIG_TRACING
|
||||
{
|
||||
.ctl_name = CTL_UNNUMBERED,
|
||||
.procname = "ftrace_dump_on_oops",
|
||||
.data = &ftrace_dump_on_oops,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec,
|
||||
},
|
||||
#endif
|
||||
#ifdef CONFIG_MODULES
|
||||
{
|
||||
.ctl_name = KERN_MODPROBE,
|
||||
@@ -1651,7 +1671,7 @@ out:
|
||||
|
||||
static int test_perm(int mode, int op)
|
||||
{
|
||||
if (!current->euid)
|
||||
if (!current_euid())
|
||||
mode >>= 6;
|
||||
else if (in_egroup_p(0))
|
||||
mode >>= 3;
|
||||
|
||||
+4
-4
@@ -1192,25 +1192,25 @@ asmlinkage long sys_getppid(void)
|
||||
asmlinkage long sys_getuid(void)
|
||||
{
|
||||
/* Only we change this so SMP safe */
|
||||
return current->uid;
|
||||
return current_uid();
|
||||
}
|
||||
|
||||
asmlinkage long sys_geteuid(void)
|
||||
{
|
||||
/* Only we change this so SMP safe */
|
||||
return current->euid;
|
||||
return current_euid();
|
||||
}
|
||||
|
||||
asmlinkage long sys_getgid(void)
|
||||
{
|
||||
/* Only we change this so SMP safe */
|
||||
return current->gid;
|
||||
return current_gid();
|
||||
}
|
||||
|
||||
asmlinkage long sys_getegid(void)
|
||||
{
|
||||
/* Only we change this so SMP safe */
|
||||
return current->egid;
|
||||
return current_egid();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+111
-4
@@ -3,18 +3,34 @@
|
||||
# select HAVE_FUNCTION_TRACER:
|
||||
#
|
||||
|
||||
config USER_STACKTRACE_SUPPORT
|
||||
bool
|
||||
|
||||
config NOP_TRACER
|
||||
bool
|
||||
|
||||
config HAVE_FUNCTION_TRACER
|
||||
bool
|
||||
|
||||
config HAVE_FUNCTION_GRAPH_TRACER
|
||||
bool
|
||||
|
||||
config HAVE_FUNCTION_TRACE_MCOUNT_TEST
|
||||
bool
|
||||
help
|
||||
This gets selected when the arch tests the function_trace_stop
|
||||
variable at the mcount call site. Otherwise, this variable
|
||||
is tested by the called function.
|
||||
|
||||
config HAVE_DYNAMIC_FTRACE
|
||||
bool
|
||||
|
||||
config HAVE_FTRACE_MCOUNT_RECORD
|
||||
bool
|
||||
|
||||
config HAVE_HW_BRANCH_TRACER
|
||||
bool
|
||||
|
||||
config TRACER_MAX_TRACE
|
||||
bool
|
||||
|
||||
@@ -47,6 +63,20 @@ config FUNCTION_TRACER
|
||||
(the bootup default), then the overhead of the instructions is very
|
||||
small and not measurable even in micro-benchmarks.
|
||||
|
||||
config FUNCTION_GRAPH_TRACER
|
||||
bool "Kernel Function Graph Tracer"
|
||||
depends on HAVE_FUNCTION_GRAPH_TRACER
|
||||
depends on FUNCTION_TRACER
|
||||
default y
|
||||
help
|
||||
Enable the kernel to trace a function at both its return
|
||||
and its entry.
|
||||
It's first purpose is to trace the duration of functions and
|
||||
draw a call graph for each thread with some informations like
|
||||
the return value.
|
||||
This is done by setting the current return address on the current
|
||||
task structure into a stack of calls.
|
||||
|
||||
config IRQSOFF_TRACER
|
||||
bool "Interrupts-off Latency Tracer"
|
||||
default n
|
||||
@@ -138,6 +168,70 @@ config BOOT_TRACER
|
||||
selected, because the self-tests are an initcall as well and that
|
||||
would invalidate the boot trace. )
|
||||
|
||||
config TRACE_BRANCH_PROFILING
|
||||
bool "Trace likely/unlikely profiler"
|
||||
depends on DEBUG_KERNEL
|
||||
select TRACING
|
||||
help
|
||||
This tracer profiles all the the likely and unlikely macros
|
||||
in the kernel. It will display the results in:
|
||||
|
||||
/debugfs/tracing/profile_annotated_branch
|
||||
|
||||
Note: this will add a significant overhead, only turn this
|
||||
on if you need to profile the system's use of these macros.
|
||||
|
||||
Say N if unsure.
|
||||
|
||||
config PROFILE_ALL_BRANCHES
|
||||
bool "Profile all if conditionals"
|
||||
depends on TRACE_BRANCH_PROFILING
|
||||
help
|
||||
This tracer profiles all branch conditions. Every if ()
|
||||
taken in the kernel is recorded whether it hit or miss.
|
||||
The results will be displayed in:
|
||||
|
||||
/debugfs/tracing/profile_branch
|
||||
|
||||
This configuration, when enabled, will impose a great overhead
|
||||
on the system. This should only be enabled when the system
|
||||
is to be analyzed
|
||||
|
||||
Say N if unsure.
|
||||
|
||||
config TRACING_BRANCHES
|
||||
bool
|
||||
help
|
||||
Selected by tracers that will trace the likely and unlikely
|
||||
conditions. This prevents the tracers themselves from being
|
||||
profiled. Profiling the tracing infrastructure can only happen
|
||||
when the likelys and unlikelys are not being traced.
|
||||
|
||||
config BRANCH_TRACER
|
||||
bool "Trace likely/unlikely instances"
|
||||
depends on TRACE_BRANCH_PROFILING
|
||||
select TRACING_BRANCHES
|
||||
help
|
||||
This traces the events of likely and unlikely condition
|
||||
calls in the kernel. The difference between this and the
|
||||
"Trace likely/unlikely profiler" is that this is not a
|
||||
histogram of the callers, but actually places the calling
|
||||
events into a running trace buffer to see when and where the
|
||||
events happened, as well as their results.
|
||||
|
||||
Say N if unsure.
|
||||
|
||||
config POWER_TRACER
|
||||
bool "Trace power consumption behavior"
|
||||
depends on DEBUG_KERNEL
|
||||
depends on X86
|
||||
select TRACING
|
||||
help
|
||||
This tracer helps developers to analyze and optimize the kernels
|
||||
power management decisions, specifically the C-state and P-state
|
||||
behavior.
|
||||
|
||||
|
||||
config STACK_TRACER
|
||||
bool "Trace max stack"
|
||||
depends on HAVE_FUNCTION_TRACER
|
||||
@@ -150,13 +244,26 @@ config STACK_TRACER
|
||||
|
||||
This tracer works by hooking into every function call that the
|
||||
kernel executes, and keeping a maximum stack depth value and
|
||||
stack-trace saved. Because this logic has to execute in every
|
||||
kernel function, all the time, this option can slow down the
|
||||
kernel measurably and is generally intended for kernel
|
||||
developers only.
|
||||
stack-trace saved. If this is configured with DYNAMIC_FTRACE
|
||||
then it will not have any overhead while the stack tracer
|
||||
is disabled.
|
||||
|
||||
To enable the stack tracer on bootup, pass in 'stacktrace'
|
||||
on the kernel command line.
|
||||
|
||||
The stack tracer can also be enabled or disabled via the
|
||||
sysctl kernel.stack_tracer_enabled
|
||||
|
||||
Say N if unsure.
|
||||
|
||||
config HW_BRANCH_TRACER
|
||||
depends on HAVE_HW_BRANCH_TRACER
|
||||
bool "Trace hw branches"
|
||||
select TRACING
|
||||
help
|
||||
This tracer records all branches on the system in a circular
|
||||
buffer giving access to the last N branches for each cpu.
|
||||
|
||||
config DYNAMIC_FTRACE
|
||||
bool "enable/disable ftrace tracepoints dynamically"
|
||||
depends on FUNCTION_TRACER
|
||||
|
||||
@@ -10,6 +10,11 @@ CFLAGS_trace_selftest_dynamic.o = -pg
|
||||
obj-y += trace_selftest_dynamic.o
|
||||
endif
|
||||
|
||||
# If unlikely tracing is enabled, do not trace these files
|
||||
ifdef CONFIG_TRACING_BRANCHES
|
||||
KBUILD_CFLAGS += -DDISABLE_BRANCH_PROFILING
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_FUNCTION_TRACER) += libftrace.o
|
||||
obj-$(CONFIG_RING_BUFFER) += ring_buffer.o
|
||||
|
||||
@@ -24,5 +29,9 @@ obj-$(CONFIG_NOP_TRACER) += trace_nop.o
|
||||
obj-$(CONFIG_STACK_TRACER) += trace_stack.o
|
||||
obj-$(CONFIG_MMIOTRACE) += trace_mmiotrace.o
|
||||
obj-$(CONFIG_BOOT_TRACER) += trace_boot.o
|
||||
obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += trace_functions_graph.o
|
||||
obj-$(CONFIG_TRACE_BRANCH_PROFILING) += trace_branch.o
|
||||
obj-$(CONFIG_HW_BRANCH_TRACER) += trace_hw_branches.o
|
||||
obj-$(CONFIG_POWER_TRACER) += trace_power.o
|
||||
|
||||
libftrace-y := ftrace.o
|
||||
|
||||
+799
-136
File diff suppressed because it is too large
Load Diff
+503
-206
File diff suppressed because it is too large
Load Diff
+819
-173
File diff suppressed because it is too large
Load Diff
+255
-10
@@ -8,6 +8,7 @@
|
||||
#include <linux/ring_buffer.h>
|
||||
#include <linux/mmiotrace.h>
|
||||
#include <linux/ftrace.h>
|
||||
#include <trace/boot.h>
|
||||
|
||||
enum trace_type {
|
||||
__TRACE_FIRST_TYPE = 0,
|
||||
@@ -21,7 +22,14 @@ enum trace_type {
|
||||
TRACE_SPECIAL,
|
||||
TRACE_MMIO_RW,
|
||||
TRACE_MMIO_MAP,
|
||||
TRACE_BOOT,
|
||||
TRACE_BRANCH,
|
||||
TRACE_BOOT_CALL,
|
||||
TRACE_BOOT_RET,
|
||||
TRACE_GRAPH_RET,
|
||||
TRACE_GRAPH_ENT,
|
||||
TRACE_USER_STACK,
|
||||
TRACE_HW_BRANCHES,
|
||||
TRACE_POWER,
|
||||
|
||||
__TRACE_LAST_TYPE
|
||||
};
|
||||
@@ -38,6 +46,7 @@ struct trace_entry {
|
||||
unsigned char flags;
|
||||
unsigned char preempt_count;
|
||||
int pid;
|
||||
int tgid;
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -48,6 +57,18 @@ struct ftrace_entry {
|
||||
unsigned long ip;
|
||||
unsigned long parent_ip;
|
||||
};
|
||||
|
||||
/* Function call entry */
|
||||
struct ftrace_graph_ent_entry {
|
||||
struct trace_entry ent;
|
||||
struct ftrace_graph_ent graph_ent;
|
||||
};
|
||||
|
||||
/* Function return entry */
|
||||
struct ftrace_graph_ret_entry {
|
||||
struct trace_entry ent;
|
||||
struct ftrace_graph_ret ret;
|
||||
};
|
||||
extern struct tracer boot_tracer;
|
||||
|
||||
/*
|
||||
@@ -85,12 +106,18 @@ struct stack_entry {
|
||||
unsigned long caller[FTRACE_STACK_ENTRIES];
|
||||
};
|
||||
|
||||
struct userstack_entry {
|
||||
struct trace_entry ent;
|
||||
unsigned long caller[FTRACE_STACK_ENTRIES];
|
||||
};
|
||||
|
||||
/*
|
||||
* ftrace_printk entry:
|
||||
*/
|
||||
struct print_entry {
|
||||
struct trace_entry ent;
|
||||
unsigned long ip;
|
||||
int depth;
|
||||
char buf[];
|
||||
};
|
||||
|
||||
@@ -112,9 +139,35 @@ struct trace_mmiotrace_map {
|
||||
struct mmiotrace_map map;
|
||||
};
|
||||
|
||||
struct trace_boot {
|
||||
struct trace_boot_call {
|
||||
struct trace_entry ent;
|
||||
struct boot_trace initcall;
|
||||
struct boot_trace_call boot_call;
|
||||
};
|
||||
|
||||
struct trace_boot_ret {
|
||||
struct trace_entry ent;
|
||||
struct boot_trace_ret boot_ret;
|
||||
};
|
||||
|
||||
#define TRACE_FUNC_SIZE 30
|
||||
#define TRACE_FILE_SIZE 20
|
||||
struct trace_branch {
|
||||
struct trace_entry ent;
|
||||
unsigned line;
|
||||
char func[TRACE_FUNC_SIZE+1];
|
||||
char file[TRACE_FILE_SIZE+1];
|
||||
char correct;
|
||||
};
|
||||
|
||||
struct hw_branch_entry {
|
||||
struct trace_entry ent;
|
||||
u64 from;
|
||||
u64 to;
|
||||
};
|
||||
|
||||
struct trace_power {
|
||||
struct trace_entry ent;
|
||||
struct power_trace state_data;
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -172,7 +225,6 @@ struct trace_iterator;
|
||||
struct trace_array {
|
||||
struct ring_buffer *buffer;
|
||||
unsigned long entries;
|
||||
long ctrl;
|
||||
int cpu;
|
||||
cycle_t time_start;
|
||||
struct task_struct *waiter;
|
||||
@@ -212,13 +264,22 @@ extern void __ftrace_bad_type(void);
|
||||
IF_ASSIGN(var, ent, struct ctx_switch_entry, 0); \
|
||||
IF_ASSIGN(var, ent, struct trace_field_cont, TRACE_CONT); \
|
||||
IF_ASSIGN(var, ent, struct stack_entry, TRACE_STACK); \
|
||||
IF_ASSIGN(var, ent, struct userstack_entry, TRACE_USER_STACK);\
|
||||
IF_ASSIGN(var, ent, struct print_entry, TRACE_PRINT); \
|
||||
IF_ASSIGN(var, ent, struct special_entry, 0); \
|
||||
IF_ASSIGN(var, ent, struct trace_mmiotrace_rw, \
|
||||
TRACE_MMIO_RW); \
|
||||
IF_ASSIGN(var, ent, struct trace_mmiotrace_map, \
|
||||
TRACE_MMIO_MAP); \
|
||||
IF_ASSIGN(var, ent, struct trace_boot, TRACE_BOOT); \
|
||||
IF_ASSIGN(var, ent, struct trace_boot_call, TRACE_BOOT_CALL);\
|
||||
IF_ASSIGN(var, ent, struct trace_boot_ret, TRACE_BOOT_RET);\
|
||||
IF_ASSIGN(var, ent, struct trace_branch, TRACE_BRANCH); \
|
||||
IF_ASSIGN(var, ent, struct ftrace_graph_ent_entry, \
|
||||
TRACE_GRAPH_ENT); \
|
||||
IF_ASSIGN(var, ent, struct ftrace_graph_ret_entry, \
|
||||
TRACE_GRAPH_RET); \
|
||||
IF_ASSIGN(var, ent, struct hw_branch_entry, TRACE_HW_BRANCHES);\
|
||||
IF_ASSIGN(var, ent, struct trace_power, TRACE_POWER); \
|
||||
__ftrace_bad_type(); \
|
||||
} while (0)
|
||||
|
||||
@@ -229,29 +290,56 @@ enum print_line_t {
|
||||
TRACE_TYPE_UNHANDLED = 2 /* Relay to other output functions */
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* An option specific to a tracer. This is a boolean value.
|
||||
* The bit is the bit index that sets its value on the
|
||||
* flags value in struct tracer_flags.
|
||||
*/
|
||||
struct tracer_opt {
|
||||
const char *name; /* Will appear on the trace_options file */
|
||||
u32 bit; /* Mask assigned in val field in tracer_flags */
|
||||
};
|
||||
|
||||
/*
|
||||
* The set of specific options for a tracer. Your tracer
|
||||
* have to set the initial value of the flags val.
|
||||
*/
|
||||
struct tracer_flags {
|
||||
u32 val;
|
||||
struct tracer_opt *opts;
|
||||
};
|
||||
|
||||
/* Makes more easy to define a tracer opt */
|
||||
#define TRACER_OPT(s, b) .name = #s, .bit = b
|
||||
|
||||
/*
|
||||
* A specific tracer, represented by methods that operate on a trace array:
|
||||
*/
|
||||
struct tracer {
|
||||
const char *name;
|
||||
void (*init)(struct trace_array *tr);
|
||||
/* Your tracer should raise a warning if init fails */
|
||||
int (*init)(struct trace_array *tr);
|
||||
void (*reset)(struct trace_array *tr);
|
||||
void (*start)(struct trace_array *tr);
|
||||
void (*stop)(struct trace_array *tr);
|
||||
void (*open)(struct trace_iterator *iter);
|
||||
void (*pipe_open)(struct trace_iterator *iter);
|
||||
void (*close)(struct trace_iterator *iter);
|
||||
void (*start)(struct trace_iterator *iter);
|
||||
void (*stop)(struct trace_iterator *iter);
|
||||
ssize_t (*read)(struct trace_iterator *iter,
|
||||
struct file *filp, char __user *ubuf,
|
||||
size_t cnt, loff_t *ppos);
|
||||
void (*ctrl_update)(struct trace_array *tr);
|
||||
#ifdef CONFIG_FTRACE_STARTUP_TEST
|
||||
int (*selftest)(struct tracer *trace,
|
||||
struct trace_array *tr);
|
||||
#endif
|
||||
void (*print_header)(struct seq_file *m);
|
||||
enum print_line_t (*print_line)(struct trace_iterator *iter);
|
||||
/* If you handled the flag setting, return 0 */
|
||||
int (*set_flag)(u32 old_flags, u32 bit, int set);
|
||||
struct tracer *next;
|
||||
int print_max;
|
||||
struct tracer_flags *flags;
|
||||
};
|
||||
|
||||
struct trace_seq {
|
||||
@@ -279,10 +367,14 @@ struct trace_iterator {
|
||||
unsigned long iter_flags;
|
||||
loff_t pos;
|
||||
long idx;
|
||||
|
||||
cpumask_t started;
|
||||
};
|
||||
|
||||
int tracing_is_enabled(void);
|
||||
void trace_wake_up(void);
|
||||
void tracing_reset(struct trace_array *tr, int cpu);
|
||||
void tracing_reset_online_cpus(struct trace_array *tr);
|
||||
int tracing_open_generic(struct inode *inode, struct file *filp);
|
||||
struct dentry *tracing_init_dentry(void);
|
||||
void init_tracer_sysprof_debugfs(struct dentry *d_tracer);
|
||||
@@ -321,8 +413,15 @@ void trace_function(struct trace_array *tr,
|
||||
unsigned long parent_ip,
|
||||
unsigned long flags, int pc);
|
||||
|
||||
void trace_graph_return(struct ftrace_graph_ret *trace);
|
||||
int trace_graph_entry(struct ftrace_graph_ent *trace);
|
||||
void trace_hw_branch(struct trace_array *tr, u64 from, u64 to);
|
||||
|
||||
void tracing_start_cmdline_record(void);
|
||||
void tracing_stop_cmdline_record(void);
|
||||
void tracing_sched_switch_assign_trace(struct trace_array *tr);
|
||||
void tracing_stop_sched_switch_record(void);
|
||||
void tracing_start_sched_switch_record(void);
|
||||
int register_tracer(struct tracer *type);
|
||||
void unregister_tracer(struct tracer *type);
|
||||
|
||||
@@ -358,6 +457,7 @@ struct tracer_switch_ops {
|
||||
struct tracer_switch_ops *next;
|
||||
};
|
||||
|
||||
char *trace_find_cmdline(int pid);
|
||||
#endif /* CONFIG_CONTEXT_SWITCH_TRACER */
|
||||
|
||||
#ifdef CONFIG_DYNAMIC_FTRACE
|
||||
@@ -383,19 +483,79 @@ extern int trace_selftest_startup_sched_switch(struct tracer *trace,
|
||||
struct trace_array *tr);
|
||||
extern int trace_selftest_startup_sysprof(struct tracer *trace,
|
||||
struct trace_array *tr);
|
||||
extern int trace_selftest_startup_branch(struct tracer *trace,
|
||||
struct trace_array *tr);
|
||||
#endif /* CONFIG_FTRACE_STARTUP_TEST */
|
||||
|
||||
extern void *head_page(struct trace_array_cpu *data);
|
||||
extern int trace_seq_printf(struct trace_seq *s, const char *fmt, ...);
|
||||
extern void trace_seq_print_cont(struct trace_seq *s,
|
||||
struct trace_iterator *iter);
|
||||
|
||||
extern int
|
||||
seq_print_ip_sym(struct trace_seq *s, unsigned long ip,
|
||||
unsigned long sym_flags);
|
||||
extern ssize_t trace_seq_to_user(struct trace_seq *s, char __user *ubuf,
|
||||
size_t cnt);
|
||||
extern long ns2usecs(cycle_t nsec);
|
||||
extern int trace_vprintk(unsigned long ip, const char *fmt, va_list args);
|
||||
extern int
|
||||
trace_vprintk(unsigned long ip, int depth, const char *fmt, va_list args);
|
||||
|
||||
extern unsigned long trace_flags;
|
||||
|
||||
/* 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);
|
||||
|
||||
#ifdef CONFIG_DYNAMIC_FTRACE
|
||||
/* TODO: make this variable */
|
||||
#define FTRACE_GRAPH_MAX_FUNCS 32
|
||||
extern int ftrace_graph_count;
|
||||
extern unsigned long ftrace_graph_funcs[FTRACE_GRAPH_MAX_FUNCS];
|
||||
|
||||
static inline int ftrace_graph_addr(unsigned long addr)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!ftrace_graph_count || test_tsk_trace_graph(current))
|
||||
return 1;
|
||||
|
||||
for (i = 0; i < ftrace_graph_count; i++) {
|
||||
if (addr == ftrace_graph_funcs[i])
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static inline int ftrace_trace_addr(unsigned long addr)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
static inline int ftrace_graph_addr(unsigned long addr)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
#endif /* CONFIG_DYNAMIC_FTRACE */
|
||||
|
||||
#else /* CONFIG_FUNCTION_GRAPH_TRACER */
|
||||
static inline enum print_line_t
|
||||
print_graph_function(struct trace_iterator *iter)
|
||||
{
|
||||
return TRACE_TYPE_UNHANDLED;
|
||||
}
|
||||
#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
|
||||
|
||||
extern struct pid *ftrace_pid_trace;
|
||||
|
||||
static inline int ftrace_trace_task(struct task_struct *task)
|
||||
{
|
||||
if (!ftrace_pid_trace)
|
||||
return 1;
|
||||
|
||||
return test_tsk_trace_trace(task);
|
||||
}
|
||||
|
||||
/*
|
||||
* trace_iterator_flags is an enumeration that defines bit
|
||||
* positions into trace_flags that controls the output.
|
||||
@@ -415,8 +575,93 @@ enum trace_iterator_flags {
|
||||
TRACE_ITER_STACKTRACE = 0x100,
|
||||
TRACE_ITER_SCHED_TREE = 0x200,
|
||||
TRACE_ITER_PRINTK = 0x400,
|
||||
TRACE_ITER_PREEMPTONLY = 0x800,
|
||||
TRACE_ITER_BRANCH = 0x1000,
|
||||
TRACE_ITER_ANNOTATE = 0x2000,
|
||||
TRACE_ITER_USERSTACKTRACE = 0x4000,
|
||||
TRACE_ITER_SYM_USEROBJ = 0x8000,
|
||||
TRACE_ITER_PRINTK_MSGONLY = 0x10000
|
||||
};
|
||||
|
||||
/*
|
||||
* TRACE_ITER_SYM_MASK masks the options in trace_flags that
|
||||
* control the output of kernel symbols.
|
||||
*/
|
||||
#define TRACE_ITER_SYM_MASK \
|
||||
(TRACE_ITER_PRINT_PARENT|TRACE_ITER_SYM_OFFSET|TRACE_ITER_SYM_ADDR)
|
||||
|
||||
extern struct tracer nop_trace;
|
||||
|
||||
/**
|
||||
* ftrace_preempt_disable - disable preemption scheduler safe
|
||||
*
|
||||
* When tracing can happen inside the scheduler, there exists
|
||||
* cases that the tracing might happen before the need_resched
|
||||
* flag is checked. If this happens and the tracer calls
|
||||
* preempt_enable (after a disable), a schedule might take place
|
||||
* causing an infinite recursion.
|
||||
*
|
||||
* To prevent this, we read the need_recshed flag before
|
||||
* disabling preemption. When we want to enable preemption we
|
||||
* check the flag, if it is set, then we call preempt_enable_no_resched.
|
||||
* Otherwise, we call preempt_enable.
|
||||
*
|
||||
* The rational for doing the above is that if need resched is set
|
||||
* and we have yet to reschedule, we are either in an atomic location
|
||||
* (where we do not need to check for scheduling) or we are inside
|
||||
* the scheduler and do not want to resched.
|
||||
*/
|
||||
static inline int ftrace_preempt_disable(void)
|
||||
{
|
||||
int resched;
|
||||
|
||||
resched = need_resched();
|
||||
preempt_disable_notrace();
|
||||
|
||||
return resched;
|
||||
}
|
||||
|
||||
/**
|
||||
* ftrace_preempt_enable - enable preemption scheduler safe
|
||||
* @resched: the return value from ftrace_preempt_disable
|
||||
*
|
||||
* This is a scheduler safe way to enable preemption and not miss
|
||||
* any preemption checks. The disabled saved the state of preemption.
|
||||
* If resched is set, then we were either inside an atomic or
|
||||
* are inside the scheduler (we would have already scheduled
|
||||
* otherwise). In this case, we do not want to call normal
|
||||
* preempt_enable, but preempt_enable_no_resched instead.
|
||||
*/
|
||||
static inline void ftrace_preempt_enable(int resched)
|
||||
{
|
||||
if (resched)
|
||||
preempt_enable_no_resched_notrace();
|
||||
else
|
||||
preempt_enable_notrace();
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BRANCH_TRACER
|
||||
extern int enable_branch_tracing(struct trace_array *tr);
|
||||
extern void disable_branch_tracing(void);
|
||||
static inline int trace_branch_enable(struct trace_array *tr)
|
||||
{
|
||||
if (trace_flags & TRACE_ITER_BRANCH)
|
||||
return enable_branch_tracing(tr);
|
||||
return 0;
|
||||
}
|
||||
static inline void trace_branch_disable(void)
|
||||
{
|
||||
/* due to races, always disable */
|
||||
disable_branch_tracing();
|
||||
}
|
||||
#else
|
||||
static inline int trace_branch_enable(struct trace_array *tr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline void trace_branch_disable(void)
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_BRANCH_TRACER */
|
||||
|
||||
#endif /* _LINUX_KERNEL_TRACE_H */
|
||||
|
||||
+109
-49
@@ -13,101 +13,132 @@
|
||||
#include "trace.h"
|
||||
|
||||
static struct trace_array *boot_trace;
|
||||
static int trace_boot_enabled;
|
||||
static bool pre_initcalls_finished;
|
||||
|
||||
|
||||
/* Should be started after do_pre_smp_initcalls() in init/main.c */
|
||||
/* Tells the boot tracer that the pre_smp_initcalls are finished.
|
||||
* So we are ready .
|
||||
* It doesn't enable sched events tracing however.
|
||||
* You have to call enable_boot_trace to do so.
|
||||
*/
|
||||
void start_boot_trace(void)
|
||||
{
|
||||
trace_boot_enabled = 1;
|
||||
pre_initcalls_finished = true;
|
||||
}
|
||||
|
||||
void stop_boot_trace(void)
|
||||
void enable_boot_trace(void)
|
||||
{
|
||||
trace_boot_enabled = 0;
|
||||
if (pre_initcalls_finished)
|
||||
tracing_start_sched_switch_record();
|
||||
}
|
||||
|
||||
void reset_boot_trace(struct trace_array *tr)
|
||||
void disable_boot_trace(void)
|
||||
{
|
||||
stop_boot_trace();
|
||||
if (pre_initcalls_finished)
|
||||
tracing_stop_sched_switch_record();
|
||||
}
|
||||
|
||||
static void boot_trace_init(struct trace_array *tr)
|
||||
static int boot_trace_init(struct trace_array *tr)
|
||||
{
|
||||
int cpu;
|
||||
boot_trace = tr;
|
||||
|
||||
trace_boot_enabled = 0;
|
||||
|
||||
for_each_cpu_mask(cpu, cpu_possible_map)
|
||||
tracing_reset(tr, cpu);
|
||||
|
||||
tracing_sched_switch_assign_trace(tr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void boot_trace_ctrl_update(struct trace_array *tr)
|
||||
static enum print_line_t
|
||||
initcall_call_print_line(struct trace_iterator *iter)
|
||||
{
|
||||
if (tr->ctrl)
|
||||
start_boot_trace();
|
||||
struct trace_entry *entry = iter->ent;
|
||||
struct trace_seq *s = &iter->seq;
|
||||
struct trace_boot_call *field;
|
||||
struct boot_trace_call *call;
|
||||
u64 ts;
|
||||
unsigned long nsec_rem;
|
||||
int ret;
|
||||
|
||||
trace_assign_type(field, entry);
|
||||
call = &field->boot_call;
|
||||
ts = iter->ts;
|
||||
nsec_rem = do_div(ts, 1000000000);
|
||||
|
||||
ret = trace_seq_printf(s, "[%5ld.%09ld] calling %s @ %i\n",
|
||||
(unsigned long)ts, nsec_rem, call->func, call->caller);
|
||||
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
else
|
||||
stop_boot_trace();
|
||||
return TRACE_TYPE_HANDLED;
|
||||
}
|
||||
|
||||
static enum print_line_t
|
||||
initcall_ret_print_line(struct trace_iterator *iter)
|
||||
{
|
||||
struct trace_entry *entry = iter->ent;
|
||||
struct trace_seq *s = &iter->seq;
|
||||
struct trace_boot_ret *field;
|
||||
struct boot_trace_ret *init_ret;
|
||||
u64 ts;
|
||||
unsigned long nsec_rem;
|
||||
int ret;
|
||||
|
||||
trace_assign_type(field, entry);
|
||||
init_ret = &field->boot_ret;
|
||||
ts = iter->ts;
|
||||
nsec_rem = do_div(ts, 1000000000);
|
||||
|
||||
ret = trace_seq_printf(s, "[%5ld.%09ld] initcall %s "
|
||||
"returned %d after %llu msecs\n",
|
||||
(unsigned long) ts,
|
||||
nsec_rem,
|
||||
init_ret->func, init_ret->result, init_ret->duration);
|
||||
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
else
|
||||
return TRACE_TYPE_HANDLED;
|
||||
}
|
||||
|
||||
static enum print_line_t initcall_print_line(struct trace_iterator *iter)
|
||||
{
|
||||
int ret;
|
||||
struct trace_entry *entry = iter->ent;
|
||||
struct trace_boot *field = (struct trace_boot *)entry;
|
||||
struct boot_trace *it = &field->initcall;
|
||||
struct trace_seq *s = &iter->seq;
|
||||
struct timespec calltime = ktime_to_timespec(it->calltime);
|
||||
struct timespec rettime = ktime_to_timespec(it->rettime);
|
||||
|
||||
if (entry->type == TRACE_BOOT) {
|
||||
ret = trace_seq_printf(s, "[%5ld.%09ld] calling %s @ %i\n",
|
||||
calltime.tv_sec,
|
||||
calltime.tv_nsec,
|
||||
it->func, it->caller);
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
ret = trace_seq_printf(s, "[%5ld.%09ld] initcall %s "
|
||||
"returned %d after %lld msecs\n",
|
||||
rettime.tv_sec,
|
||||
rettime.tv_nsec,
|
||||
it->func, it->result, it->duration);
|
||||
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
return TRACE_TYPE_HANDLED;
|
||||
switch (entry->type) {
|
||||
case TRACE_BOOT_CALL:
|
||||
return initcall_call_print_line(iter);
|
||||
case TRACE_BOOT_RET:
|
||||
return initcall_ret_print_line(iter);
|
||||
default:
|
||||
return TRACE_TYPE_UNHANDLED;
|
||||
}
|
||||
return TRACE_TYPE_UNHANDLED;
|
||||
}
|
||||
|
||||
struct tracer boot_tracer __read_mostly =
|
||||
{
|
||||
.name = "initcall",
|
||||
.init = boot_trace_init,
|
||||
.reset = reset_boot_trace,
|
||||
.ctrl_update = boot_trace_ctrl_update,
|
||||
.reset = tracing_reset_online_cpus,
|
||||
.print_line = initcall_print_line,
|
||||
};
|
||||
|
||||
void trace_boot(struct boot_trace *it, initcall_t fn)
|
||||
void trace_boot_call(struct boot_trace_call *bt, initcall_t fn)
|
||||
{
|
||||
struct ring_buffer_event *event;
|
||||
struct trace_boot *entry;
|
||||
struct trace_array_cpu *data;
|
||||
struct trace_boot_call *entry;
|
||||
unsigned long irq_flags;
|
||||
struct trace_array *tr = boot_trace;
|
||||
|
||||
if (!trace_boot_enabled)
|
||||
if (!pre_initcalls_finished)
|
||||
return;
|
||||
|
||||
/* Get its name now since this function could
|
||||
* disappear because it is in the .init section.
|
||||
*/
|
||||
sprint_symbol(it->func, (unsigned long)fn);
|
||||
sprint_symbol(bt->func, (unsigned long)fn);
|
||||
preempt_disable();
|
||||
data = tr->data[smp_processor_id()];
|
||||
|
||||
event = ring_buffer_lock_reserve(tr->buffer, sizeof(*entry),
|
||||
&irq_flags);
|
||||
@@ -115,8 +146,37 @@ void trace_boot(struct boot_trace *it, initcall_t fn)
|
||||
goto out;
|
||||
entry = ring_buffer_event_data(event);
|
||||
tracing_generic_entry_update(&entry->ent, 0, 0);
|
||||
entry->ent.type = TRACE_BOOT;
|
||||
entry->initcall = *it;
|
||||
entry->ent.type = TRACE_BOOT_CALL;
|
||||
entry->boot_call = *bt;
|
||||
ring_buffer_unlock_commit(tr->buffer, event, irq_flags);
|
||||
|
||||
trace_wake_up();
|
||||
|
||||
out:
|
||||
preempt_enable();
|
||||
}
|
||||
|
||||
void trace_boot_ret(struct boot_trace_ret *bt, initcall_t fn)
|
||||
{
|
||||
struct ring_buffer_event *event;
|
||||
struct trace_boot_ret *entry;
|
||||
unsigned long irq_flags;
|
||||
struct trace_array *tr = boot_trace;
|
||||
|
||||
if (!pre_initcalls_finished)
|
||||
return;
|
||||
|
||||
sprint_symbol(bt->func, (unsigned long)fn);
|
||||
preempt_disable();
|
||||
|
||||
event = ring_buffer_lock_reserve(tr->buffer, sizeof(*entry),
|
||||
&irq_flags);
|
||||
if (!event)
|
||||
goto out;
|
||||
entry = ring_buffer_event_data(event);
|
||||
tracing_generic_entry_update(&entry->ent, 0, 0);
|
||||
entry->ent.type = TRACE_BOOT_RET;
|
||||
entry->boot_ret = *bt;
|
||||
ring_buffer_unlock_commit(tr->buffer, event, irq_flags);
|
||||
|
||||
trace_wake_up();
|
||||
|
||||
@@ -0,0 +1,342 @@
|
||||
/*
|
||||
* unlikely profiler
|
||||
*
|
||||
* Copyright (C) 2008 Steven Rostedt <srostedt@redhat.com>
|
||||
*/
|
||||
#include <linux/kallsyms.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/irqflags.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/ftrace.h>
|
||||
#include <linux/hash.h>
|
||||
#include <linux/fs.h>
|
||||
#include <asm/local.h>
|
||||
#include "trace.h"
|
||||
|
||||
#ifdef CONFIG_BRANCH_TRACER
|
||||
|
||||
static int branch_tracing_enabled __read_mostly;
|
||||
static DEFINE_MUTEX(branch_tracing_mutex);
|
||||
static struct trace_array *branch_tracer;
|
||||
|
||||
static void
|
||||
probe_likely_condition(struct ftrace_branch_data *f, int val, int expect)
|
||||
{
|
||||
struct trace_array *tr = branch_tracer;
|
||||
struct ring_buffer_event *event;
|
||||
struct trace_branch *entry;
|
||||
unsigned long flags, irq_flags;
|
||||
int cpu, pc;
|
||||
const char *p;
|
||||
|
||||
/*
|
||||
* I would love to save just the ftrace_likely_data pointer, but
|
||||
* this code can also be used by modules. Ugly things can happen
|
||||
* if the module is unloaded, and then we go and read the
|
||||
* pointer. This is slower, but much safer.
|
||||
*/
|
||||
|
||||
if (unlikely(!tr))
|
||||
return;
|
||||
|
||||
local_irq_save(flags);
|
||||
cpu = raw_smp_processor_id();
|
||||
if (atomic_inc_return(&tr->data[cpu]->disabled) != 1)
|
||||
goto out;
|
||||
|
||||
event = ring_buffer_lock_reserve(tr->buffer, sizeof(*entry),
|
||||
&irq_flags);
|
||||
if (!event)
|
||||
goto out;
|
||||
|
||||
pc = preempt_count();
|
||||
entry = ring_buffer_event_data(event);
|
||||
tracing_generic_entry_update(&entry->ent, flags, pc);
|
||||
entry->ent.type = TRACE_BRANCH;
|
||||
|
||||
/* Strip off the path, only save the file */
|
||||
p = f->file + strlen(f->file);
|
||||
while (p >= f->file && *p != '/')
|
||||
p--;
|
||||
p++;
|
||||
|
||||
strncpy(entry->func, f->func, TRACE_FUNC_SIZE);
|
||||
strncpy(entry->file, p, TRACE_FILE_SIZE);
|
||||
entry->func[TRACE_FUNC_SIZE] = 0;
|
||||
entry->file[TRACE_FILE_SIZE] = 0;
|
||||
entry->line = f->line;
|
||||
entry->correct = val == expect;
|
||||
|
||||
ring_buffer_unlock_commit(tr->buffer, event, irq_flags);
|
||||
|
||||
out:
|
||||
atomic_dec(&tr->data[cpu]->disabled);
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
static inline
|
||||
void trace_likely_condition(struct ftrace_branch_data *f, int val, int expect)
|
||||
{
|
||||
if (!branch_tracing_enabled)
|
||||
return;
|
||||
|
||||
probe_likely_condition(f, val, expect);
|
||||
}
|
||||
|
||||
int enable_branch_tracing(struct trace_array *tr)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&branch_tracing_mutex);
|
||||
branch_tracer = tr;
|
||||
/*
|
||||
* Must be seen before enabling. The reader is a condition
|
||||
* where we do not need a matching rmb()
|
||||
*/
|
||||
smp_wmb();
|
||||
branch_tracing_enabled++;
|
||||
mutex_unlock(&branch_tracing_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void disable_branch_tracing(void)
|
||||
{
|
||||
mutex_lock(&branch_tracing_mutex);
|
||||
|
||||
if (!branch_tracing_enabled)
|
||||
goto out_unlock;
|
||||
|
||||
branch_tracing_enabled--;
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&branch_tracing_mutex);
|
||||
}
|
||||
|
||||
static void start_branch_trace(struct trace_array *tr)
|
||||
{
|
||||
enable_branch_tracing(tr);
|
||||
}
|
||||
|
||||
static void stop_branch_trace(struct trace_array *tr)
|
||||
{
|
||||
disable_branch_tracing();
|
||||
}
|
||||
|
||||
static int branch_trace_init(struct trace_array *tr)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
for_each_online_cpu(cpu)
|
||||
tracing_reset(tr, cpu);
|
||||
|
||||
start_branch_trace(tr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void branch_trace_reset(struct trace_array *tr)
|
||||
{
|
||||
stop_branch_trace(tr);
|
||||
}
|
||||
|
||||
struct tracer branch_trace __read_mostly =
|
||||
{
|
||||
.name = "branch",
|
||||
.init = branch_trace_init,
|
||||
.reset = branch_trace_reset,
|
||||
#ifdef CONFIG_FTRACE_SELFTEST
|
||||
.selftest = trace_selftest_startup_branch,
|
||||
#endif
|
||||
};
|
||||
|
||||
__init static int init_branch_trace(void)
|
||||
{
|
||||
return register_tracer(&branch_trace);
|
||||
}
|
||||
|
||||
device_initcall(init_branch_trace);
|
||||
#else
|
||||
static inline
|
||||
void trace_likely_condition(struct ftrace_branch_data *f, int val, int expect)
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_BRANCH_TRACER */
|
||||
|
||||
void ftrace_likely_update(struct ftrace_branch_data *f, int val, int expect)
|
||||
{
|
||||
/*
|
||||
* I would love to have a trace point here instead, but the
|
||||
* trace point code is so inundated with unlikely and likely
|
||||
* conditions that the recursive nightmare that exists is too
|
||||
* much to try to get working. At least for now.
|
||||
*/
|
||||
trace_likely_condition(f, val, expect);
|
||||
|
||||
/* FIXME: Make this atomic! */
|
||||
if (val == expect)
|
||||
f->correct++;
|
||||
else
|
||||
f->incorrect++;
|
||||
}
|
||||
EXPORT_SYMBOL(ftrace_likely_update);
|
||||
|
||||
struct ftrace_pointer {
|
||||
void *start;
|
||||
void *stop;
|
||||
int hit;
|
||||
};
|
||||
|
||||
static void *
|
||||
t_next(struct seq_file *m, void *v, loff_t *pos)
|
||||
{
|
||||
const struct ftrace_pointer *f = m->private;
|
||||
struct ftrace_branch_data *p = v;
|
||||
|
||||
(*pos)++;
|
||||
|
||||
if (v == (void *)1)
|
||||
return f->start;
|
||||
|
||||
++p;
|
||||
|
||||
if ((void *)p >= (void *)f->stop)
|
||||
return NULL;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
static void *t_start(struct seq_file *m, loff_t *pos)
|
||||
{
|
||||
void *t = (void *)1;
|
||||
loff_t l = 0;
|
||||
|
||||
for (; t && l < *pos; t = t_next(m, t, &l))
|
||||
;
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
static void t_stop(struct seq_file *m, void *p)
|
||||
{
|
||||
}
|
||||
|
||||
static int t_show(struct seq_file *m, void *v)
|
||||
{
|
||||
const struct ftrace_pointer *fp = m->private;
|
||||
struct ftrace_branch_data *p = v;
|
||||
const char *f;
|
||||
long percent;
|
||||
|
||||
if (v == (void *)1) {
|
||||
if (fp->hit)
|
||||
seq_printf(m, " miss hit %% ");
|
||||
else
|
||||
seq_printf(m, " correct incorrect %% ");
|
||||
seq_printf(m, " Function "
|
||||
" File Line\n"
|
||||
" ------- --------- - "
|
||||
" -------- "
|
||||
" ---- ----\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Only print the file, not the path */
|
||||
f = p->file + strlen(p->file);
|
||||
while (f >= p->file && *f != '/')
|
||||
f--;
|
||||
f++;
|
||||
|
||||
/*
|
||||
* The miss is overlayed on correct, and hit on incorrect.
|
||||
*/
|
||||
if (p->correct) {
|
||||
percent = p->incorrect * 100;
|
||||
percent /= p->correct + p->incorrect;
|
||||
} else
|
||||
percent = p->incorrect ? 100 : -1;
|
||||
|
||||
seq_printf(m, "%8lu %8lu ", p->correct, p->incorrect);
|
||||
if (percent < 0)
|
||||
seq_printf(m, " X ");
|
||||
else
|
||||
seq_printf(m, "%3ld ", percent);
|
||||
seq_printf(m, "%-30.30s %-20.20s %d\n", p->func, f, p->line);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct seq_operations tracing_likely_seq_ops = {
|
||||
.start = t_start,
|
||||
.next = t_next,
|
||||
.stop = t_stop,
|
||||
.show = t_show,
|
||||
};
|
||||
|
||||
static int tracing_branch_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = seq_open(file, &tracing_likely_seq_ops);
|
||||
if (!ret) {
|
||||
struct seq_file *m = file->private_data;
|
||||
m->private = (void *)inode->i_private;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct file_operations tracing_branch_fops = {
|
||||
.open = tracing_branch_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PROFILE_ALL_BRANCHES
|
||||
extern unsigned long __start_branch_profile[];
|
||||
extern unsigned long __stop_branch_profile[];
|
||||
|
||||
static const struct ftrace_pointer ftrace_branch_pos = {
|
||||
.start = __start_branch_profile,
|
||||
.stop = __stop_branch_profile,
|
||||
.hit = 1,
|
||||
};
|
||||
|
||||
#endif /* CONFIG_PROFILE_ALL_BRANCHES */
|
||||
|
||||
extern unsigned long __start_annotated_branch_profile[];
|
||||
extern unsigned long __stop_annotated_branch_profile[];
|
||||
|
||||
static const struct ftrace_pointer ftrace_annotated_branch_pos = {
|
||||
.start = __start_annotated_branch_profile,
|
||||
.stop = __stop_annotated_branch_profile,
|
||||
};
|
||||
|
||||
static __init int ftrace_branch_init(void)
|
||||
{
|
||||
struct dentry *d_tracer;
|
||||
struct dentry *entry;
|
||||
|
||||
d_tracer = tracing_init_dentry();
|
||||
|
||||
entry = debugfs_create_file("profile_annotated_branch", 0444, d_tracer,
|
||||
(void *)&ftrace_annotated_branch_pos,
|
||||
&tracing_branch_fops);
|
||||
if (!entry)
|
||||
pr_warning("Could not create debugfs "
|
||||
"'profile_annotatet_branch' entry\n");
|
||||
|
||||
#ifdef CONFIG_PROFILE_ALL_BRANCHES
|
||||
entry = debugfs_create_file("profile_branch", 0444, d_tracer,
|
||||
(void *)&ftrace_branch_pos,
|
||||
&tracing_branch_fops);
|
||||
if (!entry)
|
||||
pr_warning("Could not create debugfs"
|
||||
" 'profile_branch' entry\n");
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
device_initcall(ftrace_branch_init);
|
||||
@@ -16,20 +16,10 @@
|
||||
|
||||
#include "trace.h"
|
||||
|
||||
static void function_reset(struct trace_array *tr)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
tr->time_start = ftrace_now(tr->cpu);
|
||||
|
||||
for_each_online_cpu(cpu)
|
||||
tracing_reset(tr, cpu);
|
||||
}
|
||||
|
||||
static void start_function_trace(struct trace_array *tr)
|
||||
{
|
||||
tr->cpu = get_cpu();
|
||||
function_reset(tr);
|
||||
tracing_reset_online_cpus(tr);
|
||||
put_cpu();
|
||||
|
||||
tracing_start_cmdline_record();
|
||||
@@ -42,24 +32,20 @@ static void stop_function_trace(struct trace_array *tr)
|
||||
tracing_stop_cmdline_record();
|
||||
}
|
||||
|
||||
static void function_trace_init(struct trace_array *tr)
|
||||
static int function_trace_init(struct trace_array *tr)
|
||||
{
|
||||
if (tr->ctrl)
|
||||
start_function_trace(tr);
|
||||
start_function_trace(tr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void function_trace_reset(struct trace_array *tr)
|
||||
{
|
||||
if (tr->ctrl)
|
||||
stop_function_trace(tr);
|
||||
stop_function_trace(tr);
|
||||
}
|
||||
|
||||
static void function_trace_ctrl_update(struct trace_array *tr)
|
||||
static void function_trace_start(struct trace_array *tr)
|
||||
{
|
||||
if (tr->ctrl)
|
||||
start_function_trace(tr);
|
||||
else
|
||||
stop_function_trace(tr);
|
||||
tracing_reset_online_cpus(tr);
|
||||
}
|
||||
|
||||
static struct tracer function_trace __read_mostly =
|
||||
@@ -67,7 +53,7 @@ static struct tracer function_trace __read_mostly =
|
||||
.name = "function",
|
||||
.init = function_trace_init,
|
||||
.reset = function_trace_reset,
|
||||
.ctrl_update = function_trace_ctrl_update,
|
||||
.start = function_trace_start,
|
||||
#ifdef CONFIG_FTRACE_SELFTEST
|
||||
.selftest = trace_selftest_startup_function,
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,669 @@
|
||||
/*
|
||||
*
|
||||
* Function graph tracer.
|
||||
* Copyright (c) 2008 Frederic Weisbecker <fweisbec@gmail.com>
|
||||
* Mostly borrowed from function tracer which
|
||||
* is Copyright (c) Steven Rostedt <srostedt@redhat.com>
|
||||
*
|
||||
*/
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/ftrace.h>
|
||||
#include <linux/fs.h>
|
||||
|
||||
#include "trace.h"
|
||||
|
||||
#define TRACE_GRAPH_INDENT 2
|
||||
|
||||
/* Flag options */
|
||||
#define TRACE_GRAPH_PRINT_OVERRUN 0x1
|
||||
#define TRACE_GRAPH_PRINT_CPU 0x2
|
||||
#define TRACE_GRAPH_PRINT_OVERHEAD 0x4
|
||||
#define TRACE_GRAPH_PRINT_PROC 0x8
|
||||
|
||||
static struct tracer_opt trace_opts[] = {
|
||||
/* Display overruns ? */
|
||||
{ TRACER_OPT(funcgraph-overrun, TRACE_GRAPH_PRINT_OVERRUN) },
|
||||
/* Display CPU ? */
|
||||
{ TRACER_OPT(funcgraph-cpu, TRACE_GRAPH_PRINT_CPU) },
|
||||
/* Display Overhead ? */
|
||||
{ TRACER_OPT(funcgraph-overhead, TRACE_GRAPH_PRINT_OVERHEAD) },
|
||||
/* Display proc name/pid */
|
||||
{ TRACER_OPT(funcgraph-proc, TRACE_GRAPH_PRINT_PROC) },
|
||||
{ } /* Empty entry */
|
||||
};
|
||||
|
||||
static struct tracer_flags tracer_flags = {
|
||||
/* Don't display overruns and proc by default */
|
||||
.val = TRACE_GRAPH_PRINT_CPU | TRACE_GRAPH_PRINT_OVERHEAD,
|
||||
.opts = trace_opts
|
||||
};
|
||||
|
||||
/* pid on the last trace processed */
|
||||
static pid_t last_pid[NR_CPUS] = { [0 ... NR_CPUS-1] = -1 };
|
||||
|
||||
static int graph_trace_init(struct trace_array *tr)
|
||||
{
|
||||
int cpu, ret;
|
||||
|
||||
for_each_online_cpu(cpu)
|
||||
tracing_reset(tr, cpu);
|
||||
|
||||
ret = register_ftrace_graph(&trace_graph_return,
|
||||
&trace_graph_entry);
|
||||
if (ret)
|
||||
return ret;
|
||||
tracing_start_cmdline_record();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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 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(cpus_weight_nr(cpu_online_map));
|
||||
|
||||
|
||||
/*
|
||||
* 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);
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
return TRACE_TYPE_HANDLED;
|
||||
}
|
||||
|
||||
#define TRACE_GRAPH_PROCINFO_LENGTH 14
|
||||
|
||||
static enum print_line_t
|
||||
print_graph_proc(struct trace_seq *s, pid_t pid)
|
||||
{
|
||||
int i;
|
||||
int ret;
|
||||
int len;
|
||||
char comm[8];
|
||||
int spaces = 0;
|
||||
/* sign + log10(MAX_INT) + '\0' */
|
||||
char pid_str[11];
|
||||
|
||||
strncpy(comm, trace_find_cmdline(pid), 7);
|
||||
comm[7] = '\0';
|
||||
sprintf(pid_str, "%d", pid);
|
||||
|
||||
/* 1 stands for the "-" character */
|
||||
len = strlen(comm) + strlen(pid_str) + 1;
|
||||
|
||||
if (len < TRACE_GRAPH_PROCINFO_LENGTH)
|
||||
spaces = TRACE_GRAPH_PROCINFO_LENGTH - len;
|
||||
|
||||
/* First spaces to align center */
|
||||
for (i = 0; i < spaces / 2; i++) {
|
||||
ret = trace_seq_printf(s, " ");
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
}
|
||||
|
||||
ret = trace_seq_printf(s, "%s-%s", comm, pid_str);
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
/* Last spaces to align center */
|
||||
for (i = 0; i < spaces - (spaces / 2); i++) {
|
||||
ret = trace_seq_printf(s, " ");
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
}
|
||||
return TRACE_TYPE_HANDLED;
|
||||
}
|
||||
|
||||
|
||||
/* If the pid changed since the last trace, output this event */
|
||||
static enum print_line_t
|
||||
verif_pid(struct trace_seq *s, pid_t pid, int cpu)
|
||||
{
|
||||
pid_t prev_pid;
|
||||
int ret;
|
||||
|
||||
if (last_pid[cpu] != -1 && last_pid[cpu] == pid)
|
||||
return TRACE_TYPE_HANDLED;
|
||||
|
||||
prev_pid = last_pid[cpu];
|
||||
last_pid[cpu] = pid;
|
||||
|
||||
/*
|
||||
* Context-switch trace line:
|
||||
|
||||
------------------------------------------
|
||||
| 1) migration/0--1 => sshd-1755
|
||||
------------------------------------------
|
||||
|
||||
*/
|
||||
ret = trace_seq_printf(s,
|
||||
" ------------------------------------------\n");
|
||||
if (!ret)
|
||||
TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
ret = print_graph_cpu(s, cpu);
|
||||
if (ret == TRACE_TYPE_PARTIAL_LINE)
|
||||
TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
ret = print_graph_proc(s, prev_pid);
|
||||
if (ret == TRACE_TYPE_PARTIAL_LINE)
|
||||
TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
ret = trace_seq_printf(s, " => ");
|
||||
if (!ret)
|
||||
TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
ret = print_graph_proc(s, pid);
|
||||
if (ret == TRACE_TYPE_PARTIAL_LINE)
|
||||
TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
ret = trace_seq_printf(s,
|
||||
"\n ------------------------------------------\n\n");
|
||||
if (!ret)
|
||||
TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool
|
||||
trace_branch_is_leaf(struct trace_iterator *iter,
|
||||
struct ftrace_graph_ent_entry *curr)
|
||||
{
|
||||
struct ring_buffer_iter *ring_iter;
|
||||
struct ring_buffer_event *event;
|
||||
struct ftrace_graph_ret_entry *next;
|
||||
|
||||
ring_iter = iter->buffer_iter[iter->cpu];
|
||||
|
||||
if (!ring_iter)
|
||||
return false;
|
||||
|
||||
event = ring_buffer_iter_peek(ring_iter, NULL);
|
||||
|
||||
if (!event)
|
||||
return false;
|
||||
|
||||
next = ring_buffer_event_data(event);
|
||||
|
||||
if (next->ent.type != TRACE_GRAPH_RET)
|
||||
return false;
|
||||
|
||||
if (curr->ent.pid != next->ent.pid ||
|
||||
curr->graph_ent.func != next->ret.func)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static enum print_line_t
|
||||
print_graph_irq(struct trace_seq *s, unsigned long addr,
|
||||
enum trace_type type, int cpu, pid_t pid)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (addr < (unsigned long)__irqentry_text_start ||
|
||||
addr >= (unsigned long)__irqentry_text_end)
|
||||
return TRACE_TYPE_UNHANDLED;
|
||||
|
||||
if (type == TRACE_GRAPH_ENT) {
|
||||
ret = trace_seq_printf(s, "==========> | ");
|
||||
} else {
|
||||
/* Cpu */
|
||||
if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU) {
|
||||
ret = print_graph_cpu(s, cpu);
|
||||
if (ret == TRACE_TYPE_PARTIAL_LINE)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
}
|
||||
/* Proc */
|
||||
if (tracer_flags.val & TRACE_GRAPH_PRINT_PROC) {
|
||||
ret = print_graph_proc(s, pid);
|
||||
if (ret == TRACE_TYPE_PARTIAL_LINE)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
ret = trace_seq_printf(s, " | ");
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
}
|
||||
|
||||
/* No overhead */
|
||||
if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERHEAD) {
|
||||
ret = trace_seq_printf(s, " ");
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
}
|
||||
|
||||
ret = trace_seq_printf(s, "<========== |\n");
|
||||
}
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
return TRACE_TYPE_HANDLED;
|
||||
}
|
||||
|
||||
static enum print_line_t
|
||||
print_graph_duration(unsigned long long duration, struct trace_seq *s)
|
||||
{
|
||||
unsigned long nsecs_rem = do_div(duration, 1000);
|
||||
/* log10(ULONG_MAX) + '\0' */
|
||||
char msecs_str[21];
|
||||
char nsecs_str[5];
|
||||
int ret, len;
|
||||
int i;
|
||||
|
||||
sprintf(msecs_str, "%lu", (unsigned long) duration);
|
||||
|
||||
/* Print msecs */
|
||||
ret = trace_seq_printf(s, msecs_str);
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
len = strlen(msecs_str);
|
||||
|
||||
/* Print nsecs (we don't want to exceed 7 numbers) */
|
||||
if (len < 7) {
|
||||
snprintf(nsecs_str, 8 - len, "%03lu", nsecs_rem);
|
||||
ret = trace_seq_printf(s, ".%s", nsecs_str);
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
len += strlen(nsecs_str);
|
||||
}
|
||||
|
||||
ret = trace_seq_printf(s, " us ");
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
/* Print remaining spaces to fit the row's width */
|
||||
for (i = len; i < 7; i++) {
|
||||
ret = trace_seq_printf(s, " ");
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
}
|
||||
|
||||
ret = trace_seq_printf(s, "| ");
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
return TRACE_TYPE_HANDLED;
|
||||
|
||||
}
|
||||
|
||||
/* Signal a overhead of time execution to the output */
|
||||
static int
|
||||
print_graph_overhead(unsigned long long duration, struct trace_seq *s)
|
||||
{
|
||||
/* Duration exceeded 100 msecs */
|
||||
if (duration > 100000ULL)
|
||||
return trace_seq_printf(s, "! ");
|
||||
|
||||
/* Duration exceeded 10 msecs */
|
||||
if (duration > 10000ULL)
|
||||
return trace_seq_printf(s, "+ ");
|
||||
|
||||
return trace_seq_printf(s, " ");
|
||||
}
|
||||
|
||||
/* Case of a leaf function on its call entry */
|
||||
static enum print_line_t
|
||||
print_graph_entry_leaf(struct trace_iterator *iter,
|
||||
struct ftrace_graph_ent_entry *entry, struct trace_seq *s)
|
||||
{
|
||||
struct ftrace_graph_ret_entry *ret_entry;
|
||||
struct ftrace_graph_ret *graph_ret;
|
||||
struct ring_buffer_event *event;
|
||||
struct ftrace_graph_ent *call;
|
||||
unsigned long long duration;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
event = ring_buffer_read(iter->buffer_iter[iter->cpu], NULL);
|
||||
ret_entry = ring_buffer_event_data(event);
|
||||
graph_ret = &ret_entry->ret;
|
||||
call = &entry->graph_ent;
|
||||
duration = graph_ret->rettime - graph_ret->calltime;
|
||||
|
||||
/* Overhead */
|
||||
if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERHEAD) {
|
||||
ret = print_graph_overhead(duration, s);
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
}
|
||||
|
||||
/* Duration */
|
||||
ret = print_graph_duration(duration, s);
|
||||
if (ret == TRACE_TYPE_PARTIAL_LINE)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
/* Function */
|
||||
for (i = 0; i < call->depth * TRACE_GRAPH_INDENT; i++) {
|
||||
ret = trace_seq_printf(s, " ");
|
||||
if (!ret)
|
||||
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");
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
return TRACE_TYPE_HANDLED;
|
||||
}
|
||||
|
||||
static enum print_line_t
|
||||
print_graph_entry_nested(struct ftrace_graph_ent_entry *entry,
|
||||
struct trace_seq *s, pid_t pid, int cpu)
|
||||
{
|
||||
int i;
|
||||
int ret;
|
||||
struct ftrace_graph_ent *call = &entry->graph_ent;
|
||||
|
||||
/* No overhead */
|
||||
if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERHEAD) {
|
||||
ret = trace_seq_printf(s, " ");
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
}
|
||||
|
||||
/* Interrupt */
|
||||
ret = print_graph_irq(s, call->func, TRACE_GRAPH_ENT, cpu, pid);
|
||||
if (ret == TRACE_TYPE_UNHANDLED) {
|
||||
/* No time */
|
||||
ret = trace_seq_printf(s, " | ");
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
} else {
|
||||
if (ret == TRACE_TYPE_PARTIAL_LINE)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
}
|
||||
|
||||
|
||||
/* Function */
|
||||
for (i = 0; i < call->depth * TRACE_GRAPH_INDENT; i++) {
|
||||
ret = trace_seq_printf(s, " ");
|
||||
if (!ret)
|
||||
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");
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
return TRACE_TYPE_HANDLED;
|
||||
}
|
||||
|
||||
static enum print_line_t
|
||||
print_graph_entry(struct ftrace_graph_ent_entry *field, struct trace_seq *s,
|
||||
struct trace_iterator *iter, int cpu)
|
||||
{
|
||||
int ret;
|
||||
struct trace_entry *ent = iter->ent;
|
||||
|
||||
/* Pid */
|
||||
if (verif_pid(s, ent->pid, cpu) == TRACE_TYPE_PARTIAL_LINE)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
/* Cpu */
|
||||
if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU) {
|
||||
ret = print_graph_cpu(s, cpu);
|
||||
if (ret == TRACE_TYPE_PARTIAL_LINE)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
}
|
||||
|
||||
/* Proc */
|
||||
if (tracer_flags.val & TRACE_GRAPH_PRINT_PROC) {
|
||||
ret = print_graph_proc(s, ent->pid);
|
||||
if (ret == TRACE_TYPE_PARTIAL_LINE)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
ret = trace_seq_printf(s, " | ");
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
}
|
||||
|
||||
if (trace_branch_is_leaf(iter, field))
|
||||
return print_graph_entry_leaf(iter, field, s);
|
||||
else
|
||||
return print_graph_entry_nested(field, s, iter->ent->pid, cpu);
|
||||
|
||||
}
|
||||
|
||||
static enum print_line_t
|
||||
print_graph_return(struct ftrace_graph_ret *trace, struct trace_seq *s,
|
||||
struct trace_entry *ent, int cpu)
|
||||
{
|
||||
int i;
|
||||
int ret;
|
||||
unsigned long long duration = trace->rettime - trace->calltime;
|
||||
|
||||
/* Pid */
|
||||
if (verif_pid(s, ent->pid, cpu) == TRACE_TYPE_PARTIAL_LINE)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
/* Cpu */
|
||||
if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU) {
|
||||
ret = print_graph_cpu(s, cpu);
|
||||
if (ret == TRACE_TYPE_PARTIAL_LINE)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
}
|
||||
|
||||
/* Proc */
|
||||
if (tracer_flags.val & TRACE_GRAPH_PRINT_PROC) {
|
||||
ret = print_graph_proc(s, ent->pid);
|
||||
if (ret == TRACE_TYPE_PARTIAL_LINE)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
ret = trace_seq_printf(s, " | ");
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
}
|
||||
|
||||
/* Overhead */
|
||||
if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERHEAD) {
|
||||
ret = print_graph_overhead(duration, s);
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
}
|
||||
|
||||
/* Duration */
|
||||
ret = print_graph_duration(duration, s);
|
||||
if (ret == TRACE_TYPE_PARTIAL_LINE)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
/* Closing brace */
|
||||
for (i = 0; i < trace->depth * TRACE_GRAPH_INDENT; i++) {
|
||||
ret = trace_seq_printf(s, " ");
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
}
|
||||
|
||||
ret = trace_seq_printf(s, "}\n");
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
/* Overrun */
|
||||
if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERRUN) {
|
||||
ret = trace_seq_printf(s, " (Overruns: %lu)\n",
|
||||
trace->overrun);
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
}
|
||||
|
||||
ret = print_graph_irq(s, trace->func, TRACE_GRAPH_RET, cpu, ent->pid);
|
||||
if (ret == TRACE_TYPE_PARTIAL_LINE)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
return TRACE_TYPE_HANDLED;
|
||||
}
|
||||
|
||||
static enum print_line_t
|
||||
print_graph_comment(struct print_entry *trace, struct trace_seq *s,
|
||||
struct trace_entry *ent, struct trace_iterator *iter)
|
||||
{
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
/* Pid */
|
||||
if (verif_pid(s, ent->pid, iter->cpu) == TRACE_TYPE_PARTIAL_LINE)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
/* Cpu */
|
||||
if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU) {
|
||||
ret = print_graph_cpu(s, iter->cpu);
|
||||
if (ret == TRACE_TYPE_PARTIAL_LINE)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
}
|
||||
|
||||
/* Proc */
|
||||
if (tracer_flags.val & TRACE_GRAPH_PRINT_PROC) {
|
||||
ret = print_graph_proc(s, ent->pid);
|
||||
if (ret == TRACE_TYPE_PARTIAL_LINE)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
ret = trace_seq_printf(s, " | ");
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
}
|
||||
|
||||
/* No overhead */
|
||||
if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERHEAD) {
|
||||
ret = trace_seq_printf(s, " ");
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
}
|
||||
|
||||
/* No time */
|
||||
ret = trace_seq_printf(s, " | ");
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
/* Indentation */
|
||||
if (trace->depth > 0)
|
||||
for (i = 0; i < (trace->depth + 1) * TRACE_GRAPH_INDENT; i++) {
|
||||
ret = trace_seq_printf(s, " ");
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
}
|
||||
|
||||
/* The comment */
|
||||
ret = trace_seq_printf(s, "/* %s", trace->buf);
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
if (ent->flags & TRACE_FLAG_CONT)
|
||||
trace_seq_print_cont(s, iter);
|
||||
|
||||
ret = trace_seq_printf(s, " */\n");
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
return TRACE_TYPE_HANDLED;
|
||||
}
|
||||
|
||||
|
||||
enum print_line_t
|
||||
print_graph_function(struct trace_iterator *iter)
|
||||
{
|
||||
struct trace_seq *s = &iter->seq;
|
||||
struct trace_entry *entry = iter->ent;
|
||||
|
||||
switch (entry->type) {
|
||||
case TRACE_GRAPH_ENT: {
|
||||
struct ftrace_graph_ent_entry *field;
|
||||
trace_assign_type(field, entry);
|
||||
return print_graph_entry(field, s, iter,
|
||||
iter->cpu);
|
||||
}
|
||||
case TRACE_GRAPH_RET: {
|
||||
struct ftrace_graph_ret_entry *field;
|
||||
trace_assign_type(field, entry);
|
||||
return print_graph_return(&field->ret, s, entry, iter->cpu);
|
||||
}
|
||||
case TRACE_PRINT: {
|
||||
struct print_entry *field;
|
||||
trace_assign_type(field, entry);
|
||||
return print_graph_comment(field, s, entry, iter);
|
||||
}
|
||||
default:
|
||||
return TRACE_TYPE_UNHANDLED;
|
||||
}
|
||||
}
|
||||
|
||||
static void print_graph_headers(struct seq_file *s)
|
||||
{
|
||||
/* 1st line */
|
||||
seq_printf(s, "# ");
|
||||
if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU)
|
||||
seq_printf(s, "CPU ");
|
||||
if (tracer_flags.val & TRACE_GRAPH_PRINT_PROC)
|
||||
seq_printf(s, "TASK/PID ");
|
||||
if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERHEAD)
|
||||
seq_printf(s, "OVERHEAD/");
|
||||
seq_printf(s, "DURATION FUNCTION CALLS\n");
|
||||
|
||||
/* 2nd line */
|
||||
seq_printf(s, "# ");
|
||||
if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU)
|
||||
seq_printf(s, "| ");
|
||||
if (tracer_flags.val & TRACE_GRAPH_PRINT_PROC)
|
||||
seq_printf(s, "| | ");
|
||||
if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERHEAD) {
|
||||
seq_printf(s, "| ");
|
||||
seq_printf(s, "| | | | |\n");
|
||||
} else
|
||||
seq_printf(s, " | | | | |\n");
|
||||
}
|
||||
static struct tracer graph_trace __read_mostly = {
|
||||
.name = "function_graph",
|
||||
.init = graph_trace_init,
|
||||
.reset = graph_trace_reset,
|
||||
.print_line = print_graph_function,
|
||||
.print_header = print_graph_headers,
|
||||
.flags = &tracer_flags,
|
||||
};
|
||||
|
||||
static __init int init_graph_trace(void)
|
||||
{
|
||||
return register_tracer(&graph_trace);
|
||||
}
|
||||
|
||||
device_initcall(init_graph_trace);
|
||||
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* h/w branch tracer for x86 based on bts
|
||||
*
|
||||
* Copyright (C) 2008 Markus Metzger <markus.t.metzger@gmail.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/ftrace.h>
|
||||
#include <linux/kallsyms.h>
|
||||
|
||||
#include <asm/ds.h>
|
||||
|
||||
#include "trace.h"
|
||||
|
||||
|
||||
#define SIZEOF_BTS (1 << 13)
|
||||
|
||||
static DEFINE_PER_CPU(struct bts_tracer *, tracer);
|
||||
static DEFINE_PER_CPU(unsigned char[SIZEOF_BTS], buffer);
|
||||
|
||||
#define this_tracer per_cpu(tracer, smp_processor_id())
|
||||
#define this_buffer per_cpu(buffer, smp_processor_id())
|
||||
|
||||
|
||||
static void bts_trace_start_cpu(void *arg)
|
||||
{
|
||||
if (this_tracer)
|
||||
ds_release_bts(this_tracer);
|
||||
|
||||
this_tracer =
|
||||
ds_request_bts(/* task = */ NULL, this_buffer, SIZEOF_BTS,
|
||||
/* ovfl = */ NULL, /* th = */ (size_t)-1,
|
||||
BTS_KERNEL);
|
||||
if (IS_ERR(this_tracer)) {
|
||||
this_tracer = NULL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void bts_trace_start(struct trace_array *tr)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
tracing_reset_online_cpus(tr);
|
||||
|
||||
for_each_cpu_mask(cpu, cpu_possible_map)
|
||||
smp_call_function_single(cpu, bts_trace_start_cpu, NULL, 1);
|
||||
}
|
||||
|
||||
static void bts_trace_stop_cpu(void *arg)
|
||||
{
|
||||
if (this_tracer) {
|
||||
ds_release_bts(this_tracer);
|
||||
this_tracer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void bts_trace_stop(struct trace_array *tr)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
for_each_cpu_mask(cpu, cpu_possible_map)
|
||||
smp_call_function_single(cpu, bts_trace_stop_cpu, NULL, 1);
|
||||
}
|
||||
|
||||
static int bts_trace_init(struct trace_array *tr)
|
||||
{
|
||||
tracing_reset_online_cpus(tr);
|
||||
bts_trace_start(tr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bts_trace_print_header(struct seq_file *m)
|
||||
{
|
||||
seq_puts(m,
|
||||
"# CPU# FROM TO FUNCTION\n");
|
||||
seq_puts(m,
|
||||
"# | | | |\n");
|
||||
}
|
||||
|
||||
static enum print_line_t bts_trace_print_line(struct trace_iterator *iter)
|
||||
{
|
||||
struct trace_entry *entry = iter->ent;
|
||||
struct trace_seq *seq = &iter->seq;
|
||||
struct hw_branch_entry *it;
|
||||
|
||||
trace_assign_type(it, entry);
|
||||
|
||||
if (entry->type == TRACE_HW_BRANCHES) {
|
||||
if (trace_seq_printf(seq, "%4d ", entry->cpu) &&
|
||||
trace_seq_printf(seq, "0x%016llx -> 0x%016llx ",
|
||||
it->from, it->to) &&
|
||||
(!it->from ||
|
||||
seq_print_ip_sym(seq, it->from, /* sym_flags = */ 0)) &&
|
||||
trace_seq_printf(seq, "\n"))
|
||||
return TRACE_TYPE_HANDLED;
|
||||
return TRACE_TYPE_PARTIAL_LINE;;
|
||||
}
|
||||
return TRACE_TYPE_UNHANDLED;
|
||||
}
|
||||
|
||||
void trace_hw_branch(struct trace_array *tr, u64 from, u64 to)
|
||||
{
|
||||
struct ring_buffer_event *event;
|
||||
struct hw_branch_entry *entry;
|
||||
unsigned long irq;
|
||||
|
||||
event = ring_buffer_lock_reserve(tr->buffer, sizeof(*entry), &irq);
|
||||
if (!event)
|
||||
return;
|
||||
entry = ring_buffer_event_data(event);
|
||||
tracing_generic_entry_update(&entry->ent, 0, from);
|
||||
entry->ent.type = TRACE_HW_BRANCHES;
|
||||
entry->ent.cpu = smp_processor_id();
|
||||
entry->from = from;
|
||||
entry->to = to;
|
||||
ring_buffer_unlock_commit(tr->buffer, event, irq);
|
||||
}
|
||||
|
||||
static void trace_bts_at(struct trace_array *tr,
|
||||
const struct bts_trace *trace, void *at)
|
||||
{
|
||||
struct bts_struct bts;
|
||||
int err = 0;
|
||||
|
||||
WARN_ON_ONCE(!trace->read);
|
||||
if (!trace->read)
|
||||
return;
|
||||
|
||||
err = trace->read(this_tracer, at, &bts);
|
||||
if (err < 0)
|
||||
return;
|
||||
|
||||
switch (bts.qualifier) {
|
||||
case BTS_BRANCH:
|
||||
trace_hw_branch(tr, bts.variant.lbr.from, bts.variant.lbr.to);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void trace_bts_cpu(void *arg)
|
||||
{
|
||||
struct trace_array *tr = (struct trace_array *) arg;
|
||||
const struct bts_trace *trace;
|
||||
unsigned char *at;
|
||||
|
||||
if (!this_tracer)
|
||||
return;
|
||||
|
||||
ds_suspend_bts(this_tracer);
|
||||
trace = ds_read_bts(this_tracer);
|
||||
if (!trace)
|
||||
goto out;
|
||||
|
||||
for (at = trace->ds.top; (void *)at < trace->ds.end;
|
||||
at += trace->ds.size)
|
||||
trace_bts_at(tr, trace, at);
|
||||
|
||||
for (at = trace->ds.begin; (void *)at < trace->ds.top;
|
||||
at += trace->ds.size)
|
||||
trace_bts_at(tr, trace, at);
|
||||
|
||||
out:
|
||||
ds_resume_bts(this_tracer);
|
||||
}
|
||||
|
||||
static void trace_bts_prepare(struct trace_iterator *iter)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
for_each_cpu_mask(cpu, cpu_possible_map)
|
||||
smp_call_function_single(cpu, trace_bts_cpu, iter->tr, 1);
|
||||
}
|
||||
|
||||
struct tracer bts_tracer __read_mostly =
|
||||
{
|
||||
.name = "hw-branch-tracer",
|
||||
.init = bts_trace_init,
|
||||
.reset = bts_trace_stop,
|
||||
.print_header = bts_trace_print_header,
|
||||
.print_line = bts_trace_print_line,
|
||||
.start = bts_trace_start,
|
||||
.stop = bts_trace_stop,
|
||||
.open = trace_bts_prepare
|
||||
};
|
||||
|
||||
__init static int init_bts_trace(void)
|
||||
{
|
||||
return register_tracer(&bts_tracer);
|
||||
}
|
||||
device_initcall(init_bts_trace);
|
||||
@@ -353,15 +353,28 @@ void trace_preempt_off(unsigned long a0, unsigned long a1)
|
||||
}
|
||||
#endif /* CONFIG_PREEMPT_TRACER */
|
||||
|
||||
/*
|
||||
* save_tracer_enabled is used to save the state of the tracer_enabled
|
||||
* variable when we disable it when we open a trace output file.
|
||||
*/
|
||||
static int save_tracer_enabled;
|
||||
|
||||
static void start_irqsoff_tracer(struct trace_array *tr)
|
||||
{
|
||||
register_ftrace_function(&trace_ops);
|
||||
tracer_enabled = 1;
|
||||
if (tracing_is_enabled()) {
|
||||
tracer_enabled = 1;
|
||||
save_tracer_enabled = 1;
|
||||
} else {
|
||||
tracer_enabled = 0;
|
||||
save_tracer_enabled = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void stop_irqsoff_tracer(struct trace_array *tr)
|
||||
{
|
||||
tracer_enabled = 0;
|
||||
save_tracer_enabled = 0;
|
||||
unregister_ftrace_function(&trace_ops);
|
||||
}
|
||||
|
||||
@@ -370,53 +383,55 @@ static void __irqsoff_tracer_init(struct trace_array *tr)
|
||||
irqsoff_trace = tr;
|
||||
/* make sure that the tracer is visible */
|
||||
smp_wmb();
|
||||
|
||||
if (tr->ctrl)
|
||||
start_irqsoff_tracer(tr);
|
||||
start_irqsoff_tracer(tr);
|
||||
}
|
||||
|
||||
static void irqsoff_tracer_reset(struct trace_array *tr)
|
||||
{
|
||||
if (tr->ctrl)
|
||||
stop_irqsoff_tracer(tr);
|
||||
stop_irqsoff_tracer(tr);
|
||||
}
|
||||
|
||||
static void irqsoff_tracer_ctrl_update(struct trace_array *tr)
|
||||
static void irqsoff_tracer_start(struct trace_array *tr)
|
||||
{
|
||||
if (tr->ctrl)
|
||||
start_irqsoff_tracer(tr);
|
||||
else
|
||||
stop_irqsoff_tracer(tr);
|
||||
tracer_enabled = 1;
|
||||
save_tracer_enabled = 1;
|
||||
}
|
||||
|
||||
static void irqsoff_tracer_stop(struct trace_array *tr)
|
||||
{
|
||||
tracer_enabled = 0;
|
||||
save_tracer_enabled = 0;
|
||||
}
|
||||
|
||||
static void irqsoff_tracer_open(struct trace_iterator *iter)
|
||||
{
|
||||
/* stop the trace while dumping */
|
||||
if (iter->tr->ctrl)
|
||||
stop_irqsoff_tracer(iter->tr);
|
||||
tracer_enabled = 0;
|
||||
}
|
||||
|
||||
static void irqsoff_tracer_close(struct trace_iterator *iter)
|
||||
{
|
||||
if (iter->tr->ctrl)
|
||||
start_irqsoff_tracer(iter->tr);
|
||||
/* restart tracing */
|
||||
tracer_enabled = save_tracer_enabled;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IRQSOFF_TRACER
|
||||
static void irqsoff_tracer_init(struct trace_array *tr)
|
||||
static int irqsoff_tracer_init(struct trace_array *tr)
|
||||
{
|
||||
trace_type = TRACER_IRQS_OFF;
|
||||
|
||||
__irqsoff_tracer_init(tr);
|
||||
return 0;
|
||||
}
|
||||
static struct tracer irqsoff_tracer __read_mostly =
|
||||
{
|
||||
.name = "irqsoff",
|
||||
.init = irqsoff_tracer_init,
|
||||
.reset = irqsoff_tracer_reset,
|
||||
.start = irqsoff_tracer_start,
|
||||
.stop = irqsoff_tracer_stop,
|
||||
.open = irqsoff_tracer_open,
|
||||
.close = irqsoff_tracer_close,
|
||||
.ctrl_update = irqsoff_tracer_ctrl_update,
|
||||
.print_max = 1,
|
||||
#ifdef CONFIG_FTRACE_SELFTEST
|
||||
.selftest = trace_selftest_startup_irqsoff,
|
||||
@@ -428,11 +443,12 @@ static struct tracer irqsoff_tracer __read_mostly =
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PREEMPT_TRACER
|
||||
static void preemptoff_tracer_init(struct trace_array *tr)
|
||||
static int preemptoff_tracer_init(struct trace_array *tr)
|
||||
{
|
||||
trace_type = TRACER_PREEMPT_OFF;
|
||||
|
||||
__irqsoff_tracer_init(tr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct tracer preemptoff_tracer __read_mostly =
|
||||
@@ -440,9 +456,10 @@ static struct tracer preemptoff_tracer __read_mostly =
|
||||
.name = "preemptoff",
|
||||
.init = preemptoff_tracer_init,
|
||||
.reset = irqsoff_tracer_reset,
|
||||
.start = irqsoff_tracer_start,
|
||||
.stop = irqsoff_tracer_stop,
|
||||
.open = irqsoff_tracer_open,
|
||||
.close = irqsoff_tracer_close,
|
||||
.ctrl_update = irqsoff_tracer_ctrl_update,
|
||||
.print_max = 1,
|
||||
#ifdef CONFIG_FTRACE_SELFTEST
|
||||
.selftest = trace_selftest_startup_preemptoff,
|
||||
@@ -456,11 +473,12 @@ static struct tracer preemptoff_tracer __read_mostly =
|
||||
#if defined(CONFIG_IRQSOFF_TRACER) && \
|
||||
defined(CONFIG_PREEMPT_TRACER)
|
||||
|
||||
static void preemptirqsoff_tracer_init(struct trace_array *tr)
|
||||
static int preemptirqsoff_tracer_init(struct trace_array *tr)
|
||||
{
|
||||
trace_type = TRACER_IRQS_OFF | TRACER_PREEMPT_OFF;
|
||||
|
||||
__irqsoff_tracer_init(tr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct tracer preemptirqsoff_tracer __read_mostly =
|
||||
@@ -468,9 +486,10 @@ static struct tracer preemptirqsoff_tracer __read_mostly =
|
||||
.name = "preemptirqsoff",
|
||||
.init = preemptirqsoff_tracer_init,
|
||||
.reset = irqsoff_tracer_reset,
|
||||
.start = irqsoff_tracer_start,
|
||||
.stop = irqsoff_tracer_stop,
|
||||
.open = irqsoff_tracer_open,
|
||||
.close = irqsoff_tracer_close,
|
||||
.ctrl_update = irqsoff_tracer_ctrl_update,
|
||||
.print_max = 1,
|
||||
#ifdef CONFIG_FTRACE_SELFTEST
|
||||
.selftest = trace_selftest_startup_preemptirqsoff,
|
||||
|
||||
@@ -22,44 +22,35 @@ static unsigned long prev_overruns;
|
||||
|
||||
static void mmio_reset_data(struct trace_array *tr)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
overrun_detected = false;
|
||||
prev_overruns = 0;
|
||||
tr->time_start = ftrace_now(tr->cpu);
|
||||
|
||||
for_each_online_cpu(cpu)
|
||||
tracing_reset(tr, cpu);
|
||||
tracing_reset_online_cpus(tr);
|
||||
}
|
||||
|
||||
static void mmio_trace_init(struct trace_array *tr)
|
||||
static int mmio_trace_init(struct trace_array *tr)
|
||||
{
|
||||
pr_debug("in %s\n", __func__);
|
||||
mmio_trace_array = tr;
|
||||
if (tr->ctrl) {
|
||||
mmio_reset_data(tr);
|
||||
enable_mmiotrace();
|
||||
}
|
||||
|
||||
mmio_reset_data(tr);
|
||||
enable_mmiotrace();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mmio_trace_reset(struct trace_array *tr)
|
||||
{
|
||||
pr_debug("in %s\n", __func__);
|
||||
if (tr->ctrl)
|
||||
disable_mmiotrace();
|
||||
|
||||
disable_mmiotrace();
|
||||
mmio_reset_data(tr);
|
||||
mmio_trace_array = NULL;
|
||||
}
|
||||
|
||||
static void mmio_trace_ctrl_update(struct trace_array *tr)
|
||||
static void mmio_trace_start(struct trace_array *tr)
|
||||
{
|
||||
pr_debug("in %s\n", __func__);
|
||||
if (tr->ctrl) {
|
||||
mmio_reset_data(tr);
|
||||
enable_mmiotrace();
|
||||
} else {
|
||||
disable_mmiotrace();
|
||||
}
|
||||
mmio_reset_data(tr);
|
||||
}
|
||||
|
||||
static int mmio_print_pcidev(struct trace_seq *s, const struct pci_dev *dev)
|
||||
@@ -296,10 +287,10 @@ static struct tracer mmio_tracer __read_mostly =
|
||||
.name = "mmiotrace",
|
||||
.init = mmio_trace_init,
|
||||
.reset = mmio_trace_reset,
|
||||
.start = mmio_trace_start,
|
||||
.pipe_open = mmio_pipe_open,
|
||||
.close = mmio_close,
|
||||
.read = mmio_read,
|
||||
.ctrl_update = mmio_trace_ctrl_update,
|
||||
.print_line = mmio_print_line,
|
||||
};
|
||||
|
||||
@@ -371,5 +362,5 @@ void mmio_trace_mapping(struct mmiotrace_map *map)
|
||||
|
||||
int mmio_trace_printk(const char *fmt, va_list args)
|
||||
{
|
||||
return trace_vprintk(0, fmt, args);
|
||||
return trace_vprintk(0, -1, fmt, args);
|
||||
}
|
||||
|
||||
+53
-12
@@ -12,6 +12,27 @@
|
||||
|
||||
#include "trace.h"
|
||||
|
||||
/* Our two options */
|
||||
enum {
|
||||
TRACE_NOP_OPT_ACCEPT = 0x1,
|
||||
TRACE_NOP_OPT_REFUSE = 0x2
|
||||
};
|
||||
|
||||
/* Options for the tracer (see trace_options file) */
|
||||
static struct tracer_opt nop_opts[] = {
|
||||
/* Option that will be accepted by set_flag callback */
|
||||
{ TRACER_OPT(test_nop_accept, TRACE_NOP_OPT_ACCEPT) },
|
||||
/* Option that will be refused by set_flag callback */
|
||||
{ TRACER_OPT(test_nop_refuse, TRACE_NOP_OPT_REFUSE) },
|
||||
{ } /* Always set a last empty entry */
|
||||
};
|
||||
|
||||
static struct tracer_flags nop_flags = {
|
||||
/* You can check your flags value here when you want. */
|
||||
.val = 0, /* By default: all flags disabled */
|
||||
.opts = nop_opts
|
||||
};
|
||||
|
||||
static struct trace_array *ctx_trace;
|
||||
|
||||
static void start_nop_trace(struct trace_array *tr)
|
||||
@@ -24,7 +45,7 @@ static void stop_nop_trace(struct trace_array *tr)
|
||||
/* Nothing to do! */
|
||||
}
|
||||
|
||||
static void nop_trace_init(struct trace_array *tr)
|
||||
static int nop_trace_init(struct trace_array *tr)
|
||||
{
|
||||
int cpu;
|
||||
ctx_trace = tr;
|
||||
@@ -32,33 +53,53 @@ static void nop_trace_init(struct trace_array *tr)
|
||||
for_each_online_cpu(cpu)
|
||||
tracing_reset(tr, cpu);
|
||||
|
||||
if (tr->ctrl)
|
||||
start_nop_trace(tr);
|
||||
start_nop_trace(tr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nop_trace_reset(struct trace_array *tr)
|
||||
{
|
||||
if (tr->ctrl)
|
||||
stop_nop_trace(tr);
|
||||
stop_nop_trace(tr);
|
||||
}
|
||||
|
||||
static void nop_trace_ctrl_update(struct trace_array *tr)
|
||||
/* It only serves as a signal handler and a callback to
|
||||
* accept or refuse tthe setting of a flag.
|
||||
* If you don't implement it, then the flag setting will be
|
||||
* automatically accepted.
|
||||
*/
|
||||
static int nop_set_flag(u32 old_flags, u32 bit, int set)
|
||||
{
|
||||
/* When starting a new trace, reset the buffers */
|
||||
if (tr->ctrl)
|
||||
start_nop_trace(tr);
|
||||
else
|
||||
stop_nop_trace(tr);
|
||||
/*
|
||||
* Note that you don't need to update nop_flags.val yourself.
|
||||
* The tracing Api will do it automatically if you return 0
|
||||
*/
|
||||
if (bit == TRACE_NOP_OPT_ACCEPT) {
|
||||
printk(KERN_DEBUG "nop_test_accept flag set to %d: we accept."
|
||||
" Now cat trace_options to see the result\n",
|
||||
set);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (bit == TRACE_NOP_OPT_REFUSE) {
|
||||
printk(KERN_DEBUG "nop_test_refuse flag set to %d: we refuse."
|
||||
"Now cat trace_options to see the result\n",
|
||||
set);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
struct tracer nop_trace __read_mostly =
|
||||
{
|
||||
.name = "nop",
|
||||
.init = nop_trace_init,
|
||||
.reset = nop_trace_reset,
|
||||
.ctrl_update = nop_trace_ctrl_update,
|
||||
#ifdef CONFIG_FTRACE_SELFTEST
|
||||
.selftest = trace_selftest_startup_nop,
|
||||
#endif
|
||||
.flags = &nop_flags,
|
||||
.set_flag = nop_set_flag
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* ring buffer based C-state tracer
|
||||
*
|
||||
* Arjan van de Ven <arjan@linux.intel.com>
|
||||
* Copyright (C) 2008 Intel Corporation
|
||||
*
|
||||
* Much is borrowed from trace_boot.c which is
|
||||
* Copyright (C) 2008 Frederic Weisbecker <fweisbec@gmail.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/ftrace.h>
|
||||
#include <linux/kallsyms.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "trace.h"
|
||||
|
||||
static struct trace_array *power_trace;
|
||||
static int __read_mostly trace_power_enabled;
|
||||
|
||||
|
||||
static void start_power_trace(struct trace_array *tr)
|
||||
{
|
||||
trace_power_enabled = 1;
|
||||
}
|
||||
|
||||
static void stop_power_trace(struct trace_array *tr)
|
||||
{
|
||||
trace_power_enabled = 0;
|
||||
}
|
||||
|
||||
|
||||
static int power_trace_init(struct trace_array *tr)
|
||||
{
|
||||
int cpu;
|
||||
power_trace = tr;
|
||||
|
||||
trace_power_enabled = 1;
|
||||
|
||||
for_each_cpu_mask(cpu, cpu_possible_map)
|
||||
tracing_reset(tr, cpu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static enum print_line_t power_print_line(struct trace_iterator *iter)
|
||||
{
|
||||
int ret = 0;
|
||||
struct trace_entry *entry = iter->ent;
|
||||
struct trace_power *field ;
|
||||
struct power_trace *it;
|
||||
struct trace_seq *s = &iter->seq;
|
||||
struct timespec stamp;
|
||||
struct timespec duration;
|
||||
|
||||
trace_assign_type(field, entry);
|
||||
it = &field->state_data;
|
||||
stamp = ktime_to_timespec(it->stamp);
|
||||
duration = ktime_to_timespec(ktime_sub(it->end, it->stamp));
|
||||
|
||||
if (entry->type == TRACE_POWER) {
|
||||
if (it->type == POWER_CSTATE)
|
||||
ret = trace_seq_printf(s, "[%5ld.%09ld] CSTATE: Going to C%i on cpu %i for %ld.%09ld\n",
|
||||
stamp.tv_sec,
|
||||
stamp.tv_nsec,
|
||||
it->state, iter->cpu,
|
||||
duration.tv_sec,
|
||||
duration.tv_nsec);
|
||||
if (it->type == POWER_PSTATE)
|
||||
ret = trace_seq_printf(s, "[%5ld.%09ld] PSTATE: Going to P%i on cpu %i\n",
|
||||
stamp.tv_sec,
|
||||
stamp.tv_nsec,
|
||||
it->state, iter->cpu);
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
return TRACE_TYPE_HANDLED;
|
||||
}
|
||||
return TRACE_TYPE_UNHANDLED;
|
||||
}
|
||||
|
||||
static struct tracer power_tracer __read_mostly =
|
||||
{
|
||||
.name = "power",
|
||||
.init = power_trace_init,
|
||||
.start = start_power_trace,
|
||||
.stop = stop_power_trace,
|
||||
.reset = stop_power_trace,
|
||||
.print_line = power_print_line,
|
||||
};
|
||||
|
||||
static int init_power_trace(void)
|
||||
{
|
||||
return register_tracer(&power_tracer);
|
||||
}
|
||||
device_initcall(init_power_trace);
|
||||
|
||||
void trace_power_start(struct power_trace *it, unsigned int type,
|
||||
unsigned int level)
|
||||
{
|
||||
if (!trace_power_enabled)
|
||||
return;
|
||||
|
||||
memset(it, 0, sizeof(struct power_trace));
|
||||
it->state = level;
|
||||
it->type = type;
|
||||
it->stamp = ktime_get();
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(trace_power_start);
|
||||
|
||||
|
||||
void trace_power_end(struct power_trace *it)
|
||||
{
|
||||
struct ring_buffer_event *event;
|
||||
struct trace_power *entry;
|
||||
struct trace_array_cpu *data;
|
||||
unsigned long irq_flags;
|
||||
struct trace_array *tr = power_trace;
|
||||
|
||||
if (!trace_power_enabled)
|
||||
return;
|
||||
|
||||
preempt_disable();
|
||||
it->end = ktime_get();
|
||||
data = tr->data[smp_processor_id()];
|
||||
|
||||
event = ring_buffer_lock_reserve(tr->buffer, sizeof(*entry),
|
||||
&irq_flags);
|
||||
if (!event)
|
||||
goto out;
|
||||
entry = ring_buffer_event_data(event);
|
||||
tracing_generic_entry_update(&entry->ent, 0, 0);
|
||||
entry->ent.type = TRACE_POWER;
|
||||
entry->state_data = *it;
|
||||
ring_buffer_unlock_commit(tr->buffer, event, irq_flags);
|
||||
|
||||
trace_wake_up();
|
||||
|
||||
out:
|
||||
preempt_enable();
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(trace_power_end);
|
||||
|
||||
void trace_power_mark(struct power_trace *it, unsigned int type,
|
||||
unsigned int level)
|
||||
{
|
||||
struct ring_buffer_event *event;
|
||||
struct trace_power *entry;
|
||||
struct trace_array_cpu *data;
|
||||
unsigned long irq_flags;
|
||||
struct trace_array *tr = power_trace;
|
||||
|
||||
if (!trace_power_enabled)
|
||||
return;
|
||||
|
||||
memset(it, 0, sizeof(struct power_trace));
|
||||
it->state = level;
|
||||
it->type = type;
|
||||
it->stamp = ktime_get();
|
||||
preempt_disable();
|
||||
it->end = it->stamp;
|
||||
data = tr->data[smp_processor_id()];
|
||||
|
||||
event = ring_buffer_lock_reserve(tr->buffer, sizeof(*entry),
|
||||
&irq_flags);
|
||||
if (!event)
|
||||
goto out;
|
||||
entry = ring_buffer_event_data(event);
|
||||
tracing_generic_entry_update(&entry->ent, 0, 0);
|
||||
entry->ent.type = TRACE_POWER;
|
||||
entry->state_data = *it;
|
||||
ring_buffer_unlock_commit(tr->buffer, event, irq_flags);
|
||||
|
||||
trace_wake_up();
|
||||
|
||||
out:
|
||||
preempt_enable();
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(trace_power_mark);
|
||||
@@ -16,7 +16,8 @@
|
||||
|
||||
static struct trace_array *ctx_trace;
|
||||
static int __read_mostly tracer_enabled;
|
||||
static atomic_t sched_ref;
|
||||
static int sched_ref;
|
||||
static DEFINE_MUTEX(sched_register_mutex);
|
||||
|
||||
static void
|
||||
probe_sched_switch(struct rq *__rq, struct task_struct *prev,
|
||||
@@ -27,7 +28,7 @@ probe_sched_switch(struct rq *__rq, struct task_struct *prev,
|
||||
int cpu;
|
||||
int pc;
|
||||
|
||||
if (!atomic_read(&sched_ref))
|
||||
if (!sched_ref)
|
||||
return;
|
||||
|
||||
tracing_record_cmdline(prev);
|
||||
@@ -48,7 +49,7 @@ probe_sched_switch(struct rq *__rq, struct task_struct *prev,
|
||||
}
|
||||
|
||||
static void
|
||||
probe_sched_wakeup(struct rq *__rq, struct task_struct *wakee)
|
||||
probe_sched_wakeup(struct rq *__rq, struct task_struct *wakee, int success)
|
||||
{
|
||||
struct trace_array_cpu *data;
|
||||
unsigned long flags;
|
||||
@@ -71,16 +72,6 @@ probe_sched_wakeup(struct rq *__rq, struct task_struct *wakee)
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
static void sched_switch_reset(struct trace_array *tr)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
tr->time_start = ftrace_now(tr->cpu);
|
||||
|
||||
for_each_online_cpu(cpu)
|
||||
tracing_reset(tr, cpu);
|
||||
}
|
||||
|
||||
static int tracing_sched_register(void)
|
||||
{
|
||||
int ret;
|
||||
@@ -123,20 +114,18 @@ static void tracing_sched_unregister(void)
|
||||
|
||||
static void tracing_start_sched_switch(void)
|
||||
{
|
||||
long ref;
|
||||
|
||||
ref = atomic_inc_return(&sched_ref);
|
||||
if (ref == 1)
|
||||
mutex_lock(&sched_register_mutex);
|
||||
if (!(sched_ref++))
|
||||
tracing_sched_register();
|
||||
mutex_unlock(&sched_register_mutex);
|
||||
}
|
||||
|
||||
static void tracing_stop_sched_switch(void)
|
||||
{
|
||||
long ref;
|
||||
|
||||
ref = atomic_dec_and_test(&sched_ref);
|
||||
if (ref)
|
||||
mutex_lock(&sched_register_mutex);
|
||||
if (!(--sched_ref))
|
||||
tracing_sched_unregister();
|
||||
mutex_unlock(&sched_register_mutex);
|
||||
}
|
||||
|
||||
void tracing_start_cmdline_record(void)
|
||||
@@ -149,40 +138,86 @@ void tracing_stop_cmdline_record(void)
|
||||
tracing_stop_sched_switch();
|
||||
}
|
||||
|
||||
/**
|
||||
* tracing_start_sched_switch_record - start tracing context switches
|
||||
*
|
||||
* Turns on context switch tracing for a tracer.
|
||||
*/
|
||||
void tracing_start_sched_switch_record(void)
|
||||
{
|
||||
if (unlikely(!ctx_trace)) {
|
||||
WARN_ON(1);
|
||||
return;
|
||||
}
|
||||
|
||||
tracing_start_sched_switch();
|
||||
|
||||
mutex_lock(&sched_register_mutex);
|
||||
tracer_enabled++;
|
||||
mutex_unlock(&sched_register_mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* tracing_stop_sched_switch_record - start tracing context switches
|
||||
*
|
||||
* Turns off context switch tracing for a tracer.
|
||||
*/
|
||||
void tracing_stop_sched_switch_record(void)
|
||||
{
|
||||
mutex_lock(&sched_register_mutex);
|
||||
tracer_enabled--;
|
||||
WARN_ON(tracer_enabled < 0);
|
||||
mutex_unlock(&sched_register_mutex);
|
||||
|
||||
tracing_stop_sched_switch();
|
||||
}
|
||||
|
||||
/**
|
||||
* tracing_sched_switch_assign_trace - assign a trace array for ctx switch
|
||||
* @tr: trace array pointer to assign
|
||||
*
|
||||
* Some tracers might want to record the context switches in their
|
||||
* trace. This function lets those tracers assign the trace array
|
||||
* to use.
|
||||
*/
|
||||
void tracing_sched_switch_assign_trace(struct trace_array *tr)
|
||||
{
|
||||
ctx_trace = tr;
|
||||
}
|
||||
|
||||
static void start_sched_trace(struct trace_array *tr)
|
||||
{
|
||||
sched_switch_reset(tr);
|
||||
tracing_start_cmdline_record();
|
||||
tracer_enabled = 1;
|
||||
tracing_reset_online_cpus(tr);
|
||||
tracing_start_sched_switch_record();
|
||||
}
|
||||
|
||||
static void stop_sched_trace(struct trace_array *tr)
|
||||
{
|
||||
tracer_enabled = 0;
|
||||
tracing_stop_cmdline_record();
|
||||
tracing_stop_sched_switch_record();
|
||||
}
|
||||
|
||||
static void sched_switch_trace_init(struct trace_array *tr)
|
||||
static int sched_switch_trace_init(struct trace_array *tr)
|
||||
{
|
||||
ctx_trace = tr;
|
||||
|
||||
if (tr->ctrl)
|
||||
start_sched_trace(tr);
|
||||
start_sched_trace(tr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sched_switch_trace_reset(struct trace_array *tr)
|
||||
{
|
||||
if (tr->ctrl)
|
||||
if (sched_ref)
|
||||
stop_sched_trace(tr);
|
||||
}
|
||||
|
||||
static void sched_switch_trace_ctrl_update(struct trace_array *tr)
|
||||
static void sched_switch_trace_start(struct trace_array *tr)
|
||||
{
|
||||
/* When starting a new trace, reset the buffers */
|
||||
if (tr->ctrl)
|
||||
start_sched_trace(tr);
|
||||
else
|
||||
stop_sched_trace(tr);
|
||||
tracing_reset_online_cpus(tr);
|
||||
tracing_start_sched_switch();
|
||||
}
|
||||
|
||||
static void sched_switch_trace_stop(struct trace_array *tr)
|
||||
{
|
||||
tracing_stop_sched_switch();
|
||||
}
|
||||
|
||||
static struct tracer sched_switch_trace __read_mostly =
|
||||
@@ -190,7 +225,8 @@ static struct tracer sched_switch_trace __read_mostly =
|
||||
.name = "sched_switch",
|
||||
.init = sched_switch_trace_init,
|
||||
.reset = sched_switch_trace_reset,
|
||||
.ctrl_update = sched_switch_trace_ctrl_update,
|
||||
.start = sched_switch_trace_start,
|
||||
.stop = sched_switch_trace_stop,
|
||||
#ifdef CONFIG_FTRACE_SELFTEST
|
||||
.selftest = trace_selftest_startup_sched_switch,
|
||||
#endif
|
||||
@@ -198,14 +234,7 @@ static struct tracer sched_switch_trace __read_mostly =
|
||||
|
||||
__init static int init_sched_switch_trace(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (atomic_read(&sched_ref))
|
||||
ret = tracing_sched_register();
|
||||
if (ret) {
|
||||
pr_info("error registering scheduler trace\n");
|
||||
return ret;
|
||||
}
|
||||
return register_tracer(&sched_switch_trace);
|
||||
}
|
||||
device_initcall(init_sched_switch_trace);
|
||||
|
||||
|
||||
@@ -50,8 +50,7 @@ wakeup_tracer_call(unsigned long ip, unsigned long parent_ip)
|
||||
return;
|
||||
|
||||
pc = preempt_count();
|
||||
resched = need_resched();
|
||||
preempt_disable_notrace();
|
||||
resched = ftrace_preempt_disable();
|
||||
|
||||
cpu = raw_smp_processor_id();
|
||||
data = tr->data[cpu];
|
||||
@@ -81,15 +80,7 @@ wakeup_tracer_call(unsigned long ip, unsigned long parent_ip)
|
||||
out:
|
||||
atomic_dec(&data->disabled);
|
||||
|
||||
/*
|
||||
* To prevent recursion from the scheduler, if the
|
||||
* resched flag was set before we entered, then
|
||||
* don't reschedule.
|
||||
*/
|
||||
if (resched)
|
||||
preempt_enable_no_resched_notrace();
|
||||
else
|
||||
preempt_enable_notrace();
|
||||
ftrace_preempt_enable(resched);
|
||||
}
|
||||
|
||||
static struct ftrace_ops trace_ops __read_mostly =
|
||||
@@ -220,7 +211,7 @@ static void wakeup_reset(struct trace_array *tr)
|
||||
}
|
||||
|
||||
static void
|
||||
probe_wakeup(struct rq *rq, struct task_struct *p)
|
||||
probe_wakeup(struct rq *rq, struct task_struct *p, int success)
|
||||
{
|
||||
int cpu = smp_processor_id();
|
||||
unsigned long flags;
|
||||
@@ -271,6 +262,12 @@ out:
|
||||
atomic_dec(&wakeup_trace->data[cpu]->disabled);
|
||||
}
|
||||
|
||||
/*
|
||||
* save_tracer_enabled is used to save the state of the tracer_enabled
|
||||
* variable when we disable it when we open a trace output file.
|
||||
*/
|
||||
static int save_tracer_enabled;
|
||||
|
||||
static void start_wakeup_tracer(struct trace_array *tr)
|
||||
{
|
||||
int ret;
|
||||
@@ -309,7 +306,13 @@ static void start_wakeup_tracer(struct trace_array *tr)
|
||||
|
||||
register_ftrace_function(&trace_ops);
|
||||
|
||||
tracer_enabled = 1;
|
||||
if (tracing_is_enabled()) {
|
||||
tracer_enabled = 1;
|
||||
save_tracer_enabled = 1;
|
||||
} else {
|
||||
tracer_enabled = 0;
|
||||
save_tracer_enabled = 0;
|
||||
}
|
||||
|
||||
return;
|
||||
fail_deprobe_wake_new:
|
||||
@@ -321,49 +324,53 @@ fail_deprobe:
|
||||
static void stop_wakeup_tracer(struct trace_array *tr)
|
||||
{
|
||||
tracer_enabled = 0;
|
||||
save_tracer_enabled = 0;
|
||||
unregister_ftrace_function(&trace_ops);
|
||||
unregister_trace_sched_switch(probe_wakeup_sched_switch);
|
||||
unregister_trace_sched_wakeup_new(probe_wakeup);
|
||||
unregister_trace_sched_wakeup(probe_wakeup);
|
||||
}
|
||||
|
||||
static void wakeup_tracer_init(struct trace_array *tr)
|
||||
static int wakeup_tracer_init(struct trace_array *tr)
|
||||
{
|
||||
wakeup_trace = tr;
|
||||
|
||||
if (tr->ctrl)
|
||||
start_wakeup_tracer(tr);
|
||||
start_wakeup_tracer(tr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void wakeup_tracer_reset(struct trace_array *tr)
|
||||
{
|
||||
if (tr->ctrl) {
|
||||
stop_wakeup_tracer(tr);
|
||||
/* make sure we put back any tasks we are tracing */
|
||||
wakeup_reset(tr);
|
||||
}
|
||||
stop_wakeup_tracer(tr);
|
||||
/* make sure we put back any tasks we are tracing */
|
||||
wakeup_reset(tr);
|
||||
}
|
||||
|
||||
static void wakeup_tracer_ctrl_update(struct trace_array *tr)
|
||||
static void wakeup_tracer_start(struct trace_array *tr)
|
||||
{
|
||||
if (tr->ctrl)
|
||||
start_wakeup_tracer(tr);
|
||||
else
|
||||
stop_wakeup_tracer(tr);
|
||||
wakeup_reset(tr);
|
||||
tracer_enabled = 1;
|
||||
save_tracer_enabled = 1;
|
||||
}
|
||||
|
||||
static void wakeup_tracer_stop(struct trace_array *tr)
|
||||
{
|
||||
tracer_enabled = 0;
|
||||
save_tracer_enabled = 0;
|
||||
}
|
||||
|
||||
static void wakeup_tracer_open(struct trace_iterator *iter)
|
||||
{
|
||||
/* stop the trace while dumping */
|
||||
if (iter->tr->ctrl)
|
||||
stop_wakeup_tracer(iter->tr);
|
||||
tracer_enabled = 0;
|
||||
}
|
||||
|
||||
static void wakeup_tracer_close(struct trace_iterator *iter)
|
||||
{
|
||||
/* forget about any processes we were recording */
|
||||
if (iter->tr->ctrl)
|
||||
start_wakeup_tracer(iter->tr);
|
||||
if (save_tracer_enabled) {
|
||||
wakeup_reset(iter->tr);
|
||||
tracer_enabled = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static struct tracer wakeup_tracer __read_mostly =
|
||||
@@ -371,9 +378,10 @@ static struct tracer wakeup_tracer __read_mostly =
|
||||
.name = "wakeup",
|
||||
.init = wakeup_tracer_init,
|
||||
.reset = wakeup_tracer_reset,
|
||||
.start = wakeup_tracer_start,
|
||||
.stop = wakeup_tracer_stop,
|
||||
.open = wakeup_tracer_open,
|
||||
.close = wakeup_tracer_close,
|
||||
.ctrl_update = wakeup_tracer_ctrl_update,
|
||||
.print_max = 1,
|
||||
#ifdef CONFIG_FTRACE_SELFTEST
|
||||
.selftest = trace_selftest_startup_wakeup,
|
||||
|
||||
+133
-40
@@ -13,6 +13,7 @@ static inline int trace_valid_entry(struct trace_entry *entry)
|
||||
case TRACE_STACK:
|
||||
case TRACE_PRINT:
|
||||
case TRACE_SPECIAL:
|
||||
case TRACE_BRANCH:
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
@@ -51,7 +52,7 @@ static int trace_test_buffer(struct trace_array *tr, unsigned long *count)
|
||||
int cpu, ret = 0;
|
||||
|
||||
/* Don't allow flipping of max traces now */
|
||||
raw_local_irq_save(flags);
|
||||
local_irq_save(flags);
|
||||
__raw_spin_lock(&ftrace_max_lock);
|
||||
|
||||
cnt = ring_buffer_entries(tr->buffer);
|
||||
@@ -62,7 +63,7 @@ static int trace_test_buffer(struct trace_array *tr, unsigned long *count)
|
||||
break;
|
||||
}
|
||||
__raw_spin_unlock(&ftrace_max_lock);
|
||||
raw_local_irq_restore(flags);
|
||||
local_irq_restore(flags);
|
||||
|
||||
if (count)
|
||||
*count = cnt;
|
||||
@@ -70,6 +71,11 @@ static int trace_test_buffer(struct trace_array *tr, unsigned long *count)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void warn_failed_init_tracer(struct tracer *trace, int init_ret)
|
||||
{
|
||||
printk(KERN_WARNING "Failed to init %s tracer, init returned %d\n",
|
||||
trace->name, init_ret);
|
||||
}
|
||||
#ifdef CONFIG_FUNCTION_TRACER
|
||||
|
||||
#ifdef CONFIG_DYNAMIC_FTRACE
|
||||
@@ -110,8 +116,11 @@ int trace_selftest_startup_dynamic_tracing(struct tracer *trace,
|
||||
ftrace_set_filter(func_name, strlen(func_name), 1);
|
||||
|
||||
/* enable tracing */
|
||||
tr->ctrl = 1;
|
||||
trace->init(tr);
|
||||
ret = trace->init(tr);
|
||||
if (ret) {
|
||||
warn_failed_init_tracer(trace, ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Sleep for a 1/10 of a second */
|
||||
msleep(100);
|
||||
@@ -134,13 +143,13 @@ int trace_selftest_startup_dynamic_tracing(struct tracer *trace,
|
||||
msleep(100);
|
||||
|
||||
/* stop the tracing. */
|
||||
tr->ctrl = 0;
|
||||
trace->ctrl_update(tr);
|
||||
tracing_stop();
|
||||
ftrace_enabled = 0;
|
||||
|
||||
/* check the trace buffer */
|
||||
ret = trace_test_buffer(tr, &count);
|
||||
trace->reset(tr);
|
||||
tracing_start();
|
||||
|
||||
/* we should only have one item */
|
||||
if (!ret && count != 1) {
|
||||
@@ -148,6 +157,7 @@ int trace_selftest_startup_dynamic_tracing(struct tracer *trace,
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
ftrace_enabled = save_ftrace_enabled;
|
||||
tracer_enabled = save_tracer_enabled;
|
||||
@@ -180,18 +190,22 @@ trace_selftest_startup_function(struct tracer *trace, struct trace_array *tr)
|
||||
ftrace_enabled = 1;
|
||||
tracer_enabled = 1;
|
||||
|
||||
tr->ctrl = 1;
|
||||
trace->init(tr);
|
||||
ret = trace->init(tr);
|
||||
if (ret) {
|
||||
warn_failed_init_tracer(trace, ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Sleep for a 1/10 of a second */
|
||||
msleep(100);
|
||||
/* stop the tracing. */
|
||||
tr->ctrl = 0;
|
||||
trace->ctrl_update(tr);
|
||||
tracing_stop();
|
||||
ftrace_enabled = 0;
|
||||
|
||||
/* check the trace buffer */
|
||||
ret = trace_test_buffer(tr, &count);
|
||||
trace->reset(tr);
|
||||
tracing_start();
|
||||
|
||||
if (!ret && !count) {
|
||||
printk(KERN_CONT ".. no entries found ..");
|
||||
@@ -223,8 +237,12 @@ trace_selftest_startup_irqsoff(struct tracer *trace, struct trace_array *tr)
|
||||
int ret;
|
||||
|
||||
/* start the tracing */
|
||||
tr->ctrl = 1;
|
||||
trace->init(tr);
|
||||
ret = trace->init(tr);
|
||||
if (ret) {
|
||||
warn_failed_init_tracer(trace, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* reset the max latency */
|
||||
tracing_max_latency = 0;
|
||||
/* disable interrupts for a bit */
|
||||
@@ -232,13 +250,13 @@ trace_selftest_startup_irqsoff(struct tracer *trace, struct trace_array *tr)
|
||||
udelay(100);
|
||||
local_irq_enable();
|
||||
/* stop the tracing. */
|
||||
tr->ctrl = 0;
|
||||
trace->ctrl_update(tr);
|
||||
tracing_stop();
|
||||
/* check both trace buffers */
|
||||
ret = trace_test_buffer(tr, NULL);
|
||||
if (!ret)
|
||||
ret = trace_test_buffer(&max_tr, &count);
|
||||
trace->reset(tr);
|
||||
tracing_start();
|
||||
|
||||
if (!ret && !count) {
|
||||
printk(KERN_CONT ".. no entries found ..");
|
||||
@@ -259,9 +277,26 @@ trace_selftest_startup_preemptoff(struct tracer *trace, struct trace_array *tr)
|
||||
unsigned long count;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Now that the big kernel lock is no longer preemptable,
|
||||
* and this is called with the BKL held, it will always
|
||||
* fail. If preemption is already disabled, simply
|
||||
* pass the test. When the BKL is removed, or becomes
|
||||
* preemptible again, we will once again test this,
|
||||
* so keep it in.
|
||||
*/
|
||||
if (preempt_count()) {
|
||||
printk(KERN_CONT "can not test ... force ");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* start the tracing */
|
||||
tr->ctrl = 1;
|
||||
trace->init(tr);
|
||||
ret = trace->init(tr);
|
||||
if (ret) {
|
||||
warn_failed_init_tracer(trace, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* reset the max latency */
|
||||
tracing_max_latency = 0;
|
||||
/* disable preemption for a bit */
|
||||
@@ -269,13 +304,13 @@ trace_selftest_startup_preemptoff(struct tracer *trace, struct trace_array *tr)
|
||||
udelay(100);
|
||||
preempt_enable();
|
||||
/* stop the tracing. */
|
||||
tr->ctrl = 0;
|
||||
trace->ctrl_update(tr);
|
||||
tracing_stop();
|
||||
/* check both trace buffers */
|
||||
ret = trace_test_buffer(tr, NULL);
|
||||
if (!ret)
|
||||
ret = trace_test_buffer(&max_tr, &count);
|
||||
trace->reset(tr);
|
||||
tracing_start();
|
||||
|
||||
if (!ret && !count) {
|
||||
printk(KERN_CONT ".. no entries found ..");
|
||||
@@ -296,9 +331,25 @@ trace_selftest_startup_preemptirqsoff(struct tracer *trace, struct trace_array *
|
||||
unsigned long count;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Now that the big kernel lock is no longer preemptable,
|
||||
* and this is called with the BKL held, it will always
|
||||
* fail. If preemption is already disabled, simply
|
||||
* pass the test. When the BKL is removed, or becomes
|
||||
* preemptible again, we will once again test this,
|
||||
* so keep it in.
|
||||
*/
|
||||
if (preempt_count()) {
|
||||
printk(KERN_CONT "can not test ... force ");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* start the tracing */
|
||||
tr->ctrl = 1;
|
||||
trace->init(tr);
|
||||
ret = trace->init(tr);
|
||||
if (ret) {
|
||||
warn_failed_init_tracer(trace, ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* reset the max latency */
|
||||
tracing_max_latency = 0;
|
||||
@@ -312,27 +363,30 @@ trace_selftest_startup_preemptirqsoff(struct tracer *trace, struct trace_array *
|
||||
local_irq_enable();
|
||||
|
||||
/* stop the tracing. */
|
||||
tr->ctrl = 0;
|
||||
trace->ctrl_update(tr);
|
||||
tracing_stop();
|
||||
/* check both trace buffers */
|
||||
ret = trace_test_buffer(tr, NULL);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
tracing_start();
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = trace_test_buffer(&max_tr, &count);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
tracing_start();
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!ret && !count) {
|
||||
printk(KERN_CONT ".. no entries found ..");
|
||||
ret = -1;
|
||||
tracing_start();
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* do the test by disabling interrupts first this time */
|
||||
tracing_max_latency = 0;
|
||||
tr->ctrl = 1;
|
||||
trace->ctrl_update(tr);
|
||||
tracing_start();
|
||||
preempt_disable();
|
||||
local_irq_disable();
|
||||
udelay(100);
|
||||
@@ -341,8 +395,7 @@ trace_selftest_startup_preemptirqsoff(struct tracer *trace, struct trace_array *
|
||||
local_irq_enable();
|
||||
|
||||
/* stop the tracing. */
|
||||
tr->ctrl = 0;
|
||||
trace->ctrl_update(tr);
|
||||
tracing_stop();
|
||||
/* check both trace buffers */
|
||||
ret = trace_test_buffer(tr, NULL);
|
||||
if (ret)
|
||||
@@ -358,6 +411,7 @@ trace_selftest_startup_preemptirqsoff(struct tracer *trace, struct trace_array *
|
||||
|
||||
out:
|
||||
trace->reset(tr);
|
||||
tracing_start();
|
||||
tracing_max_latency = save_max;
|
||||
|
||||
return ret;
|
||||
@@ -423,8 +477,12 @@ trace_selftest_startup_wakeup(struct tracer *trace, struct trace_array *tr)
|
||||
wait_for_completion(&isrt);
|
||||
|
||||
/* start the tracing */
|
||||
tr->ctrl = 1;
|
||||
trace->init(tr);
|
||||
ret = trace->init(tr);
|
||||
if (ret) {
|
||||
warn_failed_init_tracer(trace, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* reset the max latency */
|
||||
tracing_max_latency = 0;
|
||||
|
||||
@@ -448,8 +506,7 @@ trace_selftest_startup_wakeup(struct tracer *trace, struct trace_array *tr)
|
||||
msleep(100);
|
||||
|
||||
/* stop the tracing. */
|
||||
tr->ctrl = 0;
|
||||
trace->ctrl_update(tr);
|
||||
tracing_stop();
|
||||
/* check both trace buffers */
|
||||
ret = trace_test_buffer(tr, NULL);
|
||||
if (!ret)
|
||||
@@ -457,6 +514,7 @@ trace_selftest_startup_wakeup(struct tracer *trace, struct trace_array *tr)
|
||||
|
||||
|
||||
trace->reset(tr);
|
||||
tracing_start();
|
||||
|
||||
tracing_max_latency = save_max;
|
||||
|
||||
@@ -480,16 +538,20 @@ trace_selftest_startup_sched_switch(struct tracer *trace, struct trace_array *tr
|
||||
int ret;
|
||||
|
||||
/* start the tracing */
|
||||
tr->ctrl = 1;
|
||||
trace->init(tr);
|
||||
ret = trace->init(tr);
|
||||
if (ret) {
|
||||
warn_failed_init_tracer(trace, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Sleep for a 1/10 of a second */
|
||||
msleep(100);
|
||||
/* stop the tracing. */
|
||||
tr->ctrl = 0;
|
||||
trace->ctrl_update(tr);
|
||||
tracing_stop();
|
||||
/* check the trace buffer */
|
||||
ret = trace_test_buffer(tr, &count);
|
||||
trace->reset(tr);
|
||||
tracing_start();
|
||||
|
||||
if (!ret && !count) {
|
||||
printk(KERN_CONT ".. no entries found ..");
|
||||
@@ -508,17 +570,48 @@ trace_selftest_startup_sysprof(struct tracer *trace, struct trace_array *tr)
|
||||
int ret;
|
||||
|
||||
/* start the tracing */
|
||||
tr->ctrl = 1;
|
||||
trace->init(tr);
|
||||
ret = trace->init(tr);
|
||||
if (ret) {
|
||||
warn_failed_init_tracer(trace, ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Sleep for a 1/10 of a second */
|
||||
msleep(100);
|
||||
/* stop the tracing. */
|
||||
tr->ctrl = 0;
|
||||
trace->ctrl_update(tr);
|
||||
tracing_stop();
|
||||
/* check the trace buffer */
|
||||
ret = trace_test_buffer(tr, &count);
|
||||
trace->reset(tr);
|
||||
tracing_start();
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_SYSPROF_TRACER */
|
||||
|
||||
#ifdef CONFIG_BRANCH_TRACER
|
||||
int
|
||||
trace_selftest_startup_branch(struct tracer *trace, struct trace_array *tr)
|
||||
{
|
||||
unsigned long count;
|
||||
int ret;
|
||||
|
||||
/* start the tracing */
|
||||
ret = trace->init(tr);
|
||||
if (ret) {
|
||||
warn_failed_init_tracer(trace, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Sleep for a 1/10 of a second */
|
||||
msleep(100);
|
||||
/* stop the tracing. */
|
||||
tracing_stop();
|
||||
/* check the trace buffer */
|
||||
ret = trace_test_buffer(tr, &count);
|
||||
trace->reset(tr);
|
||||
tracing_start();
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_BRANCH_TRACER */
|
||||
|
||||
+55
-15
@@ -10,6 +10,7 @@
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/ftrace.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/sysctl.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/fs.h>
|
||||
#include "trace.h"
|
||||
@@ -31,6 +32,10 @@ static raw_spinlock_t max_stack_lock =
|
||||
|
||||
static int stack_trace_disabled __read_mostly;
|
||||
static DEFINE_PER_CPU(int, trace_active);
|
||||
static DEFINE_MUTEX(stack_sysctl_mutex);
|
||||
|
||||
int stack_tracer_enabled;
|
||||
static int last_stack_tracer_enabled;
|
||||
|
||||
static inline void check_stack(void)
|
||||
{
|
||||
@@ -48,7 +53,7 @@ static inline void check_stack(void)
|
||||
if (!object_is_on_stack(&this_size))
|
||||
return;
|
||||
|
||||
raw_local_irq_save(flags);
|
||||
local_irq_save(flags);
|
||||
__raw_spin_lock(&max_stack_lock);
|
||||
|
||||
/* a race could have already updated it */
|
||||
@@ -78,6 +83,7 @@ static inline void check_stack(void)
|
||||
* on a new max, so it is far from a fast path.
|
||||
*/
|
||||
while (i < max_stack_trace.nr_entries) {
|
||||
int found = 0;
|
||||
|
||||
stack_dump_index[i] = this_size;
|
||||
p = start;
|
||||
@@ -86,17 +92,19 @@ static inline void check_stack(void)
|
||||
if (*p == stack_dump_trace[i]) {
|
||||
this_size = stack_dump_index[i++] =
|
||||
(top - p) * sizeof(unsigned long);
|
||||
found = 1;
|
||||
/* Start the search from here */
|
||||
start = p + 1;
|
||||
}
|
||||
}
|
||||
|
||||
i++;
|
||||
if (!found)
|
||||
i++;
|
||||
}
|
||||
|
||||
out:
|
||||
__raw_spin_unlock(&max_stack_lock);
|
||||
raw_local_irq_restore(flags);
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -107,8 +115,7 @@ stack_trace_call(unsigned long ip, unsigned long parent_ip)
|
||||
if (unlikely(!ftrace_enabled || stack_trace_disabled))
|
||||
return;
|
||||
|
||||
resched = need_resched();
|
||||
preempt_disable_notrace();
|
||||
resched = ftrace_preempt_disable();
|
||||
|
||||
cpu = raw_smp_processor_id();
|
||||
/* no atomic needed, we only modify this variable by this cpu */
|
||||
@@ -120,10 +127,7 @@ stack_trace_call(unsigned long ip, unsigned long parent_ip)
|
||||
out:
|
||||
per_cpu(trace_active, cpu)--;
|
||||
/* prevent recursion in schedule */
|
||||
if (resched)
|
||||
preempt_enable_no_resched_notrace();
|
||||
else
|
||||
preempt_enable_notrace();
|
||||
ftrace_preempt_enable(resched);
|
||||
}
|
||||
|
||||
static struct ftrace_ops trace_ops __read_mostly =
|
||||
@@ -166,16 +170,16 @@ stack_max_size_write(struct file *filp, const char __user *ubuf,
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
raw_local_irq_save(flags);
|
||||
local_irq_save(flags);
|
||||
__raw_spin_lock(&max_stack_lock);
|
||||
*ptr = val;
|
||||
__raw_spin_unlock(&max_stack_lock);
|
||||
raw_local_irq_restore(flags);
|
||||
local_irq_restore(flags);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static struct file_operations stack_max_size_fops = {
|
||||
static const struct file_operations stack_max_size_fops = {
|
||||
.open = tracing_open_generic,
|
||||
.read = stack_max_size_read,
|
||||
.write = stack_max_size_write,
|
||||
@@ -273,7 +277,7 @@ static int t_show(struct seq_file *m, void *v)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct seq_operations stack_trace_seq_ops = {
|
||||
static const struct seq_operations stack_trace_seq_ops = {
|
||||
.start = t_start,
|
||||
.next = t_next,
|
||||
.stop = t_stop,
|
||||
@@ -289,12 +293,47 @@ static int stack_trace_open(struct inode *inode, struct file *file)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct file_operations stack_trace_fops = {
|
||||
static const struct file_operations stack_trace_fops = {
|
||||
.open = stack_trace_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
};
|
||||
|
||||
int
|
||||
stack_trace_sysctl(struct ctl_table *table, int write,
|
||||
struct file *file, void __user *buffer, size_t *lenp,
|
||||
loff_t *ppos)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&stack_sysctl_mutex);
|
||||
|
||||
ret = proc_dointvec(table, write, file, buffer, lenp, ppos);
|
||||
|
||||
if (ret || !write ||
|
||||
(last_stack_tracer_enabled == stack_tracer_enabled))
|
||||
goto out;
|
||||
|
||||
last_stack_tracer_enabled = stack_tracer_enabled;
|
||||
|
||||
if (stack_tracer_enabled)
|
||||
register_ftrace_function(&trace_ops);
|
||||
else
|
||||
unregister_ftrace_function(&trace_ops);
|
||||
|
||||
out:
|
||||
mutex_unlock(&stack_sysctl_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static __init int enable_stacktrace(char *str)
|
||||
{
|
||||
stack_tracer_enabled = 1;
|
||||
last_stack_tracer_enabled = 1;
|
||||
return 1;
|
||||
}
|
||||
__setup("stacktrace", enable_stacktrace);
|
||||
|
||||
static __init int stack_trace_init(void)
|
||||
{
|
||||
struct dentry *d_tracer;
|
||||
@@ -312,7 +351,8 @@ static __init int stack_trace_init(void)
|
||||
if (!entry)
|
||||
pr_warning("Could not create debugfs 'stack_trace' entry\n");
|
||||
|
||||
register_ftrace_function(&trace_ops);
|
||||
if (stack_tracer_enabled)
|
||||
register_ftrace_function(&trace_ops);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -234,20 +234,10 @@ static void stop_stack_timers(void)
|
||||
stop_stack_timer(cpu);
|
||||
}
|
||||
|
||||
static void stack_reset(struct trace_array *tr)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
tr->time_start = ftrace_now(tr->cpu);
|
||||
|
||||
for_each_online_cpu(cpu)
|
||||
tracing_reset(tr, cpu);
|
||||
}
|
||||
|
||||
static void start_stack_trace(struct trace_array *tr)
|
||||
{
|
||||
mutex_lock(&sample_timer_lock);
|
||||
stack_reset(tr);
|
||||
tracing_reset_online_cpus(tr);
|
||||
start_stack_timers();
|
||||
tracer_enabled = 1;
|
||||
mutex_unlock(&sample_timer_lock);
|
||||
@@ -261,27 +251,17 @@ static void stop_stack_trace(struct trace_array *tr)
|
||||
mutex_unlock(&sample_timer_lock);
|
||||
}
|
||||
|
||||
static void stack_trace_init(struct trace_array *tr)
|
||||
static int stack_trace_init(struct trace_array *tr)
|
||||
{
|
||||
sysprof_trace = tr;
|
||||
|
||||
if (tr->ctrl)
|
||||
start_stack_trace(tr);
|
||||
start_stack_trace(tr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void stack_trace_reset(struct trace_array *tr)
|
||||
{
|
||||
if (tr->ctrl)
|
||||
stop_stack_trace(tr);
|
||||
}
|
||||
|
||||
static void stack_trace_ctrl_update(struct trace_array *tr)
|
||||
{
|
||||
/* When starting a new trace, reset the buffers */
|
||||
if (tr->ctrl)
|
||||
start_stack_trace(tr);
|
||||
else
|
||||
stop_stack_trace(tr);
|
||||
stop_stack_trace(tr);
|
||||
}
|
||||
|
||||
static struct tracer stack_trace __read_mostly =
|
||||
@@ -289,7 +269,6 @@ static struct tracer stack_trace __read_mostly =
|
||||
.name = "sysprof",
|
||||
.init = stack_trace_init,
|
||||
.reset = stack_trace_reset,
|
||||
.ctrl_update = stack_trace_ctrl_update,
|
||||
#ifdef CONFIG_FTRACE_SELFTEST
|
||||
.selftest = trace_selftest_startup_sysprof,
|
||||
#endif
|
||||
|
||||
+194
-103
@@ -43,6 +43,7 @@ static DEFINE_MUTEX(tracepoints_mutex);
|
||||
*/
|
||||
#define TRACEPOINT_HASH_BITS 6
|
||||
#define TRACEPOINT_TABLE_SIZE (1 << TRACEPOINT_HASH_BITS)
|
||||
static struct hlist_head tracepoint_table[TRACEPOINT_TABLE_SIZE];
|
||||
|
||||
/*
|
||||
* Note about RCU :
|
||||
@@ -54,40 +55,43 @@ struct tracepoint_entry {
|
||||
struct hlist_node hlist;
|
||||
void **funcs;
|
||||
int refcount; /* Number of times armed. 0 if disarmed. */
|
||||
struct rcu_head rcu;
|
||||
void *oldptr;
|
||||
unsigned char rcu_pending:1;
|
||||
char name[0];
|
||||
};
|
||||
|
||||
static struct hlist_head tracepoint_table[TRACEPOINT_TABLE_SIZE];
|
||||
struct tp_probes {
|
||||
union {
|
||||
struct rcu_head rcu;
|
||||
struct list_head list;
|
||||
} u;
|
||||
void *probes[0];
|
||||
};
|
||||
|
||||
static void free_old_closure(struct rcu_head *head)
|
||||
static inline void *allocate_probes(int count)
|
||||
{
|
||||
struct tracepoint_entry *entry = container_of(head,
|
||||
struct tracepoint_entry, rcu);
|
||||
kfree(entry->oldptr);
|
||||
/* Make sure we free the data before setting the pending flag to 0 */
|
||||
smp_wmb();
|
||||
entry->rcu_pending = 0;
|
||||
struct tp_probes *p = kmalloc(count * sizeof(void *)
|
||||
+ sizeof(struct tp_probes), GFP_KERNEL);
|
||||
return p == NULL ? NULL : p->probes;
|
||||
}
|
||||
|
||||
static void tracepoint_entry_free_old(struct tracepoint_entry *entry, void *old)
|
||||
static void rcu_free_old_probes(struct rcu_head *head)
|
||||
{
|
||||
if (!old)
|
||||
return;
|
||||
entry->oldptr = old;
|
||||
entry->rcu_pending = 1;
|
||||
/* write rcu_pending before calling the RCU callback */
|
||||
smp_wmb();
|
||||
call_rcu_sched(&entry->rcu, free_old_closure);
|
||||
kfree(container_of(head, struct tp_probes, u.rcu));
|
||||
}
|
||||
|
||||
static inline void release_probes(void *old)
|
||||
{
|
||||
if (old) {
|
||||
struct tp_probes *tp_probes = container_of(old,
|
||||
struct tp_probes, probes[0]);
|
||||
call_rcu_sched(&tp_probes->u.rcu, rcu_free_old_probes);
|
||||
}
|
||||
}
|
||||
|
||||
static void debug_print_probes(struct tracepoint_entry *entry)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!tracepoint_debug)
|
||||
if (!tracepoint_debug || !entry->funcs)
|
||||
return;
|
||||
|
||||
for (i = 0; entry->funcs[i]; i++)
|
||||
@@ -111,12 +115,13 @@ tracepoint_entry_add_probe(struct tracepoint_entry *entry, void *probe)
|
||||
return ERR_PTR(-EEXIST);
|
||||
}
|
||||
/* + 2 : one for new probe, one for NULL func */
|
||||
new = kzalloc((nr_probes + 2) * sizeof(void *), GFP_KERNEL);
|
||||
new = allocate_probes(nr_probes + 2);
|
||||
if (new == NULL)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
if (old)
|
||||
memcpy(new, old, nr_probes * sizeof(void *));
|
||||
new[nr_probes] = probe;
|
||||
new[nr_probes + 1] = NULL;
|
||||
entry->refcount = nr_probes + 1;
|
||||
entry->funcs = new;
|
||||
debug_print_probes(entry);
|
||||
@@ -132,7 +137,7 @@ tracepoint_entry_remove_probe(struct tracepoint_entry *entry, void *probe)
|
||||
old = entry->funcs;
|
||||
|
||||
if (!old)
|
||||
return NULL;
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
debug_print_probes(entry);
|
||||
/* (N -> M), (N > 1, M >= 0) probes */
|
||||
@@ -151,13 +156,13 @@ tracepoint_entry_remove_probe(struct tracepoint_entry *entry, void *probe)
|
||||
int j = 0;
|
||||
/* N -> M, (N > 1, M > 0) */
|
||||
/* + 1 for NULL */
|
||||
new = kzalloc((nr_probes - nr_del + 1)
|
||||
* sizeof(void *), GFP_KERNEL);
|
||||
new = allocate_probes(nr_probes - nr_del + 1);
|
||||
if (new == NULL)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
for (i = 0; old[i]; i++)
|
||||
if ((probe && old[i] != probe))
|
||||
new[j++] = old[i];
|
||||
new[nr_probes - nr_del] = NULL;
|
||||
entry->refcount = nr_probes - nr_del;
|
||||
entry->funcs = new;
|
||||
}
|
||||
@@ -215,7 +220,6 @@ static struct tracepoint_entry *add_tracepoint(const char *name)
|
||||
memcpy(&e->name[0], name, name_len);
|
||||
e->funcs = NULL;
|
||||
e->refcount = 0;
|
||||
e->rcu_pending = 0;
|
||||
hlist_add_head(&e->hlist, head);
|
||||
return e;
|
||||
}
|
||||
@@ -224,32 +228,10 @@ static struct tracepoint_entry *add_tracepoint(const char *name)
|
||||
* Remove the tracepoint from the tracepoint hash table. Must be called with
|
||||
* mutex_lock held.
|
||||
*/
|
||||
static int remove_tracepoint(const char *name)
|
||||
static inline void remove_tracepoint(struct tracepoint_entry *e)
|
||||
{
|
||||
struct hlist_head *head;
|
||||
struct hlist_node *node;
|
||||
struct tracepoint_entry *e;
|
||||
int found = 0;
|
||||
size_t len = strlen(name) + 1;
|
||||
u32 hash = jhash(name, len-1, 0);
|
||||
|
||||
head = &tracepoint_table[hash & (TRACEPOINT_TABLE_SIZE - 1)];
|
||||
hlist_for_each_entry(e, node, head, hlist) {
|
||||
if (!strcmp(name, e->name)) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
return -ENOENT;
|
||||
if (e->refcount)
|
||||
return -EBUSY;
|
||||
hlist_del(&e->hlist);
|
||||
/* Make sure the call_rcu_sched has been executed */
|
||||
if (e->rcu_pending)
|
||||
rcu_barrier_sched();
|
||||
kfree(e);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -280,6 +262,7 @@ static void set_tracepoint(struct tracepoint_entry **entry,
|
||||
static void disable_tracepoint(struct tracepoint *elem)
|
||||
{
|
||||
elem->state = 0;
|
||||
rcu_assign_pointer(elem->funcs, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -320,6 +303,23 @@ static void tracepoint_update_probes(void)
|
||||
module_update_tracepoints();
|
||||
}
|
||||
|
||||
static void *tracepoint_add_probe(const char *name, void *probe)
|
||||
{
|
||||
struct tracepoint_entry *entry;
|
||||
void *old;
|
||||
|
||||
entry = get_tracepoint(name);
|
||||
if (!entry) {
|
||||
entry = add_tracepoint(name);
|
||||
if (IS_ERR(entry))
|
||||
return entry;
|
||||
}
|
||||
old = tracepoint_entry_add_probe(entry, probe);
|
||||
if (IS_ERR(old) && !entry->refcount)
|
||||
remove_tracepoint(entry);
|
||||
return old;
|
||||
}
|
||||
|
||||
/**
|
||||
* tracepoint_probe_register - Connect a probe to a tracepoint
|
||||
* @name: tracepoint name
|
||||
@@ -330,44 +330,36 @@ static void tracepoint_update_probes(void)
|
||||
*/
|
||||
int tracepoint_probe_register(const char *name, void *probe)
|
||||
{
|
||||
struct tracepoint_entry *entry;
|
||||
int ret = 0;
|
||||
void *old;
|
||||
|
||||
mutex_lock(&tracepoints_mutex);
|
||||
entry = get_tracepoint(name);
|
||||
if (!entry) {
|
||||
entry = add_tracepoint(name);
|
||||
if (IS_ERR(entry)) {
|
||||
ret = PTR_ERR(entry);
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* If we detect that a call_rcu_sched is pending for this tracepoint,
|
||||
* make sure it's executed now.
|
||||
*/
|
||||
if (entry->rcu_pending)
|
||||
rcu_barrier_sched();
|
||||
old = tracepoint_entry_add_probe(entry, probe);
|
||||
if (IS_ERR(old)) {
|
||||
ret = PTR_ERR(old);
|
||||
goto end;
|
||||
}
|
||||
old = tracepoint_add_probe(name, probe);
|
||||
mutex_unlock(&tracepoints_mutex);
|
||||
if (IS_ERR(old))
|
||||
return PTR_ERR(old);
|
||||
|
||||
tracepoint_update_probes(); /* may update entry */
|
||||
mutex_lock(&tracepoints_mutex);
|
||||
entry = get_tracepoint(name);
|
||||
WARN_ON(!entry);
|
||||
if (entry->rcu_pending)
|
||||
rcu_barrier_sched();
|
||||
tracepoint_entry_free_old(entry, old);
|
||||
end:
|
||||
mutex_unlock(&tracepoints_mutex);
|
||||
return ret;
|
||||
release_probes(old);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tracepoint_probe_register);
|
||||
|
||||
static void *tracepoint_remove_probe(const char *name, void *probe)
|
||||
{
|
||||
struct tracepoint_entry *entry;
|
||||
void *old;
|
||||
|
||||
entry = get_tracepoint(name);
|
||||
if (!entry)
|
||||
return ERR_PTR(-ENOENT);
|
||||
old = tracepoint_entry_remove_probe(entry, probe);
|
||||
if (IS_ERR(old))
|
||||
return old;
|
||||
if (!entry->refcount)
|
||||
remove_tracepoint(entry);
|
||||
return old;
|
||||
}
|
||||
|
||||
/**
|
||||
* tracepoint_probe_unregister - Disconnect a probe from a tracepoint
|
||||
* @name: tracepoint name
|
||||
@@ -380,39 +372,105 @@ EXPORT_SYMBOL_GPL(tracepoint_probe_register);
|
||||
*/
|
||||
int tracepoint_probe_unregister(const char *name, void *probe)
|
||||
{
|
||||
struct tracepoint_entry *entry;
|
||||
void *old;
|
||||
int ret = -ENOENT;
|
||||
|
||||
mutex_lock(&tracepoints_mutex);
|
||||
entry = get_tracepoint(name);
|
||||
if (!entry)
|
||||
goto end;
|
||||
if (entry->rcu_pending)
|
||||
rcu_barrier_sched();
|
||||
old = tracepoint_entry_remove_probe(entry, probe);
|
||||
if (!old) {
|
||||
printk(KERN_WARNING "Warning: Trying to unregister a probe"
|
||||
"that doesn't exist\n");
|
||||
goto end;
|
||||
}
|
||||
old = tracepoint_remove_probe(name, probe);
|
||||
mutex_unlock(&tracepoints_mutex);
|
||||
if (IS_ERR(old))
|
||||
return PTR_ERR(old);
|
||||
|
||||
tracepoint_update_probes(); /* may update entry */
|
||||
mutex_lock(&tracepoints_mutex);
|
||||
entry = get_tracepoint(name);
|
||||
if (!entry)
|
||||
goto end;
|
||||
if (entry->rcu_pending)
|
||||
rcu_barrier_sched();
|
||||
tracepoint_entry_free_old(entry, old);
|
||||
remove_tracepoint(name); /* Ignore busy error message */
|
||||
ret = 0;
|
||||
end:
|
||||
mutex_unlock(&tracepoints_mutex);
|
||||
return ret;
|
||||
release_probes(old);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tracepoint_probe_unregister);
|
||||
|
||||
static LIST_HEAD(old_probes);
|
||||
static int need_update;
|
||||
|
||||
static void tracepoint_add_old_probes(void *old)
|
||||
{
|
||||
need_update = 1;
|
||||
if (old) {
|
||||
struct tp_probes *tp_probes = container_of(old,
|
||||
struct tp_probes, probes[0]);
|
||||
list_add(&tp_probes->u.list, &old_probes);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* tracepoint_probe_register_noupdate - register a probe but not connect
|
||||
* @name: tracepoint name
|
||||
* @probe: probe handler
|
||||
*
|
||||
* caller must call tracepoint_probe_update_all()
|
||||
*/
|
||||
int tracepoint_probe_register_noupdate(const char *name, void *probe)
|
||||
{
|
||||
void *old;
|
||||
|
||||
mutex_lock(&tracepoints_mutex);
|
||||
old = tracepoint_add_probe(name, probe);
|
||||
if (IS_ERR(old)) {
|
||||
mutex_unlock(&tracepoints_mutex);
|
||||
return PTR_ERR(old);
|
||||
}
|
||||
tracepoint_add_old_probes(old);
|
||||
mutex_unlock(&tracepoints_mutex);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tracepoint_probe_register_noupdate);
|
||||
|
||||
/**
|
||||
* tracepoint_probe_unregister_noupdate - remove a probe but not disconnect
|
||||
* @name: tracepoint name
|
||||
* @probe: probe function pointer
|
||||
*
|
||||
* caller must call tracepoint_probe_update_all()
|
||||
*/
|
||||
int tracepoint_probe_unregister_noupdate(const char *name, void *probe)
|
||||
{
|
||||
void *old;
|
||||
|
||||
mutex_lock(&tracepoints_mutex);
|
||||
old = tracepoint_remove_probe(name, probe);
|
||||
if (IS_ERR(old)) {
|
||||
mutex_unlock(&tracepoints_mutex);
|
||||
return PTR_ERR(old);
|
||||
}
|
||||
tracepoint_add_old_probes(old);
|
||||
mutex_unlock(&tracepoints_mutex);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tracepoint_probe_unregister_noupdate);
|
||||
|
||||
/**
|
||||
* tracepoint_probe_update_all - update tracepoints
|
||||
*/
|
||||
void tracepoint_probe_update_all(void)
|
||||
{
|
||||
LIST_HEAD(release_probes);
|
||||
struct tp_probes *pos, *next;
|
||||
|
||||
mutex_lock(&tracepoints_mutex);
|
||||
if (!need_update) {
|
||||
mutex_unlock(&tracepoints_mutex);
|
||||
return;
|
||||
}
|
||||
if (!list_empty(&old_probes))
|
||||
list_replace_init(&old_probes, &release_probes);
|
||||
need_update = 0;
|
||||
mutex_unlock(&tracepoints_mutex);
|
||||
|
||||
tracepoint_update_probes();
|
||||
list_for_each_entry_safe(pos, next, &release_probes, u.list) {
|
||||
list_del(&pos->u.list);
|
||||
call_rcu_sched(&pos->u.rcu, rcu_free_old_probes);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tracepoint_probe_update_all);
|
||||
|
||||
/**
|
||||
* tracepoint_get_iter_range - Get a next tracepoint iterator given a range.
|
||||
* @tracepoint: current tracepoints (in), next tracepoint (out)
|
||||
@@ -483,3 +541,36 @@ void tracepoint_iter_reset(struct tracepoint_iter *iter)
|
||||
iter->tracepoint = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tracepoint_iter_reset);
|
||||
|
||||
#ifdef CONFIG_MODULES
|
||||
|
||||
int tracepoint_module_notify(struct notifier_block *self,
|
||||
unsigned long val, void *data)
|
||||
{
|
||||
struct module *mod = data;
|
||||
|
||||
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);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct notifier_block tracepoint_module_nb = {
|
||||
.notifier_call = tracepoint_module_notify,
|
||||
.priority = 0,
|
||||
};
|
||||
|
||||
static int init_tracepoints(void)
|
||||
{
|
||||
return register_module_notifier(&tracepoint_module_nb);
|
||||
}
|
||||
__initcall(init_tracepoints);
|
||||
|
||||
#endif /* CONFIG_MODULES */
|
||||
|
||||
+4
-2
@@ -27,6 +27,7 @@
|
||||
*/
|
||||
void bacct_add_tsk(struct taskstats *stats, struct task_struct *tsk)
|
||||
{
|
||||
const struct cred *tcred;
|
||||
struct timespec uptime, ts;
|
||||
u64 ac_etime;
|
||||
|
||||
@@ -53,10 +54,11 @@ void bacct_add_tsk(struct taskstats *stats, struct task_struct *tsk)
|
||||
stats->ac_flag |= AXSIG;
|
||||
stats->ac_nice = task_nice(tsk);
|
||||
stats->ac_sched = tsk->policy;
|
||||
stats->ac_uid = tsk->uid;
|
||||
stats->ac_gid = tsk->gid;
|
||||
stats->ac_pid = tsk->pid;
|
||||
rcu_read_lock();
|
||||
tcred = __task_cred(tsk);
|
||||
stats->ac_uid = tcred->uid;
|
||||
stats->ac_gid = tcred->gid;
|
||||
stats->ac_ppid = pid_alive(tsk) ?
|
||||
rcu_dereference(tsk->real_parent)->tgid : 0;
|
||||
rcu_read_unlock();
|
||||
|
||||
+16
-15
@@ -84,11 +84,12 @@ asmlinkage long sys_setresuid16(old_uid_t ruid, old_uid_t euid, old_uid_t suid)
|
||||
|
||||
asmlinkage long sys_getresuid16(old_uid_t __user *ruid, old_uid_t __user *euid, old_uid_t __user *suid)
|
||||
{
|
||||
const struct cred *cred = current_cred();
|
||||
int retval;
|
||||
|
||||
if (!(retval = put_user(high2lowuid(current->uid), ruid)) &&
|
||||
!(retval = put_user(high2lowuid(current->euid), euid)))
|
||||
retval = put_user(high2lowuid(current->suid), suid);
|
||||
if (!(retval = put_user(high2lowuid(cred->uid), ruid)) &&
|
||||
!(retval = put_user(high2lowuid(cred->euid), euid)))
|
||||
retval = put_user(high2lowuid(cred->suid), suid);
|
||||
|
||||
return retval;
|
||||
}
|
||||
@@ -104,11 +105,12 @@ asmlinkage long sys_setresgid16(old_gid_t rgid, old_gid_t egid, old_gid_t sgid)
|
||||
|
||||
asmlinkage long sys_getresgid16(old_gid_t __user *rgid, old_gid_t __user *egid, old_gid_t __user *sgid)
|
||||
{
|
||||
const struct cred *cred = current_cred();
|
||||
int retval;
|
||||
|
||||
if (!(retval = put_user(high2lowgid(current->gid), rgid)) &&
|
||||
!(retval = put_user(high2lowgid(current->egid), egid)))
|
||||
retval = put_user(high2lowgid(current->sgid), sgid);
|
||||
if (!(retval = put_user(high2lowgid(cred->gid), rgid)) &&
|
||||
!(retval = put_user(high2lowgid(cred->egid), egid)))
|
||||
retval = put_user(high2lowgid(cred->sgid), sgid);
|
||||
|
||||
return retval;
|
||||
}
|
||||
@@ -161,25 +163,24 @@ static int groups16_from_user(struct group_info *group_info,
|
||||
|
||||
asmlinkage long sys_getgroups16(int gidsetsize, old_gid_t __user *grouplist)
|
||||
{
|
||||
int i = 0;
|
||||
const struct cred *cred = current_cred();
|
||||
int i;
|
||||
|
||||
if (gidsetsize < 0)
|
||||
return -EINVAL;
|
||||
|
||||
get_group_info(current->group_info);
|
||||
i = current->group_info->ngroups;
|
||||
i = cred->group_info->ngroups;
|
||||
if (gidsetsize) {
|
||||
if (i > gidsetsize) {
|
||||
i = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (groups16_to_user(grouplist, current->group_info)) {
|
||||
if (groups16_to_user(grouplist, cred->group_info)) {
|
||||
i = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
out:
|
||||
put_group_info(current->group_info);
|
||||
return i;
|
||||
}
|
||||
|
||||
@@ -210,20 +211,20 @@ asmlinkage long sys_setgroups16(int gidsetsize, old_gid_t __user *grouplist)
|
||||
|
||||
asmlinkage long sys_getuid16(void)
|
||||
{
|
||||
return high2lowuid(current->uid);
|
||||
return high2lowuid(current_uid());
|
||||
}
|
||||
|
||||
asmlinkage long sys_geteuid16(void)
|
||||
{
|
||||
return high2lowuid(current->euid);
|
||||
return high2lowuid(current_euid());
|
||||
}
|
||||
|
||||
asmlinkage long sys_getgid16(void)
|
||||
{
|
||||
return high2lowgid(current->gid);
|
||||
return high2lowgid(current_gid());
|
||||
}
|
||||
|
||||
asmlinkage long sys_getegid16(void)
|
||||
{
|
||||
return high2lowgid(current->egid);
|
||||
return high2lowgid(current_egid());
|
||||
}
|
||||
|
||||
+27
-71
@@ -16,12 +16,13 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/user_namespace.h>
|
||||
#include "cred-internals.h"
|
||||
|
||||
struct user_namespace init_user_ns = {
|
||||
.kref = {
|
||||
.refcount = ATOMIC_INIT(2),
|
||||
.refcount = ATOMIC_INIT(1),
|
||||
},
|
||||
.root_user = &root_user,
|
||||
.creator = &root_user,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(init_user_ns);
|
||||
|
||||
@@ -47,12 +48,14 @@ static struct kmem_cache *uid_cachep;
|
||||
*/
|
||||
static DEFINE_SPINLOCK(uidhash_lock);
|
||||
|
||||
/* root_user.__count is 2, 1 for init task cred, 1 for init_user_ns->creator */
|
||||
struct user_struct root_user = {
|
||||
.__count = ATOMIC_INIT(1),
|
||||
.__count = ATOMIC_INIT(2),
|
||||
.processes = ATOMIC_INIT(1),
|
||||
.files = ATOMIC_INIT(0),
|
||||
.sigpending = ATOMIC_INIT(0),
|
||||
.locked_shm = 0,
|
||||
.user_ns = &init_user_ns,
|
||||
#ifdef CONFIG_USER_SCHED
|
||||
.tg = &init_task_group,
|
||||
#endif
|
||||
@@ -101,19 +104,15 @@ static int sched_create_user(struct user_struct *up)
|
||||
if (IS_ERR(up->tg))
|
||||
rc = -ENOMEM;
|
||||
|
||||
return rc;
|
||||
}
|
||||
set_tg_uid(up);
|
||||
|
||||
static void sched_switch_user(struct task_struct *p)
|
||||
{
|
||||
sched_move_task(p);
|
||||
return rc;
|
||||
}
|
||||
|
||||
#else /* CONFIG_USER_SCHED */
|
||||
|
||||
static void sched_destroy_user(struct user_struct *up) { }
|
||||
static int sched_create_user(struct user_struct *up) { return 0; }
|
||||
static void sched_switch_user(struct task_struct *p) { }
|
||||
|
||||
#endif /* CONFIG_USER_SCHED */
|
||||
|
||||
@@ -242,13 +241,21 @@ static struct kobj_type uids_ktype = {
|
||||
.release = uids_release,
|
||||
};
|
||||
|
||||
/* create /sys/kernel/uids/<uid>/cpu_share file for this user */
|
||||
/*
|
||||
* Create /sys/kernel/uids/<uid>/cpu_share file for this user
|
||||
* We do not create this file for users in a user namespace (until
|
||||
* sysfs tagging is implemented).
|
||||
*
|
||||
* See Documentation/scheduler/sched-design-CFS.txt for ramifications.
|
||||
*/
|
||||
static int uids_user_create(struct user_struct *up)
|
||||
{
|
||||
struct kobject *kobj = &up->kobj;
|
||||
int error;
|
||||
|
||||
memset(kobj, 0, sizeof(struct kobject));
|
||||
if (up->user_ns != &init_user_ns)
|
||||
return 0;
|
||||
kobj->kset = uids_kset;
|
||||
error = kobject_init_and_add(kobj, &uids_ktype, NULL, "%d", up->uid);
|
||||
if (error) {
|
||||
@@ -284,6 +291,8 @@ static void remove_user_sysfs_dir(struct work_struct *w)
|
||||
unsigned long flags;
|
||||
int remove_user = 0;
|
||||
|
||||
if (up->user_ns != &init_user_ns)
|
||||
return;
|
||||
/* Make uid_hash_remove() + sysfs_remove_file() + kobject_del()
|
||||
* atomic.
|
||||
*/
|
||||
@@ -319,12 +328,13 @@ done:
|
||||
* IRQ state (as stored in flags) is restored and uidhash_lock released
|
||||
* upon function exit.
|
||||
*/
|
||||
static inline void free_user(struct user_struct *up, unsigned long flags)
|
||||
static void free_user(struct user_struct *up, unsigned long flags)
|
||||
{
|
||||
/* restore back the count */
|
||||
atomic_inc(&up->__count);
|
||||
spin_unlock_irqrestore(&uidhash_lock, flags);
|
||||
|
||||
put_user_ns(up->user_ns);
|
||||
INIT_WORK(&up->work, remove_user_sysfs_dir);
|
||||
schedule_work(&up->work);
|
||||
}
|
||||
@@ -340,13 +350,14 @@ static inline void uids_mutex_unlock(void) { }
|
||||
* IRQ state (as stored in flags) is restored and uidhash_lock released
|
||||
* upon function exit.
|
||||
*/
|
||||
static inline void free_user(struct user_struct *up, unsigned long flags)
|
||||
static void free_user(struct user_struct *up, unsigned long flags)
|
||||
{
|
||||
uid_hash_remove(up);
|
||||
spin_unlock_irqrestore(&uidhash_lock, flags);
|
||||
sched_destroy_user(up);
|
||||
key_put(up->uid_keyring);
|
||||
key_put(up->session_keyring);
|
||||
put_user_ns(up->user_ns);
|
||||
kmem_cache_free(uid_cachep, up);
|
||||
}
|
||||
|
||||
@@ -362,7 +373,7 @@ struct user_struct *find_user(uid_t uid)
|
||||
{
|
||||
struct user_struct *ret;
|
||||
unsigned long flags;
|
||||
struct user_namespace *ns = current->nsproxy->user_ns;
|
||||
struct user_namespace *ns = current_user_ns();
|
||||
|
||||
spin_lock_irqsave(&uidhash_lock, flags);
|
||||
ret = uid_hash_find(uid, uidhashentry(ns, uid));
|
||||
@@ -409,6 +420,8 @@ struct user_struct *alloc_uid(struct user_namespace *ns, uid_t uid)
|
||||
if (sched_create_user(new) < 0)
|
||||
goto out_free_user;
|
||||
|
||||
new->user_ns = get_user_ns(ns);
|
||||
|
||||
if (uids_user_create(new))
|
||||
goto out_destoy_sched;
|
||||
|
||||
@@ -432,7 +445,6 @@ struct user_struct *alloc_uid(struct user_namespace *ns, uid_t uid)
|
||||
up = new;
|
||||
}
|
||||
spin_unlock_irq(&uidhash_lock);
|
||||
|
||||
}
|
||||
|
||||
uids_mutex_unlock();
|
||||
@@ -441,6 +453,7 @@ struct user_struct *alloc_uid(struct user_namespace *ns, uid_t uid)
|
||||
|
||||
out_destoy_sched:
|
||||
sched_destroy_user(new);
|
||||
put_user_ns(new->user_ns);
|
||||
out_free_user:
|
||||
kmem_cache_free(uid_cachep, new);
|
||||
out_unlock:
|
||||
@@ -448,63 +461,6 @@ out_unlock:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void switch_uid(struct user_struct *new_user)
|
||||
{
|
||||
struct user_struct *old_user;
|
||||
|
||||
/* What if a process setreuid()'s and this brings the
|
||||
* new uid over his NPROC rlimit? We can check this now
|
||||
* cheaply with the new uid cache, so if it matters
|
||||
* we should be checking for it. -DaveM
|
||||
*/
|
||||
old_user = current->user;
|
||||
atomic_inc(&new_user->processes);
|
||||
atomic_dec(&old_user->processes);
|
||||
switch_uid_keyring(new_user);
|
||||
current->user = new_user;
|
||||
sched_switch_user(current);
|
||||
|
||||
/*
|
||||
* We need to synchronize with __sigqueue_alloc()
|
||||
* doing a get_uid(p->user).. If that saw the old
|
||||
* user value, we need to wait until it has exited
|
||||
* its critical region before we can free the old
|
||||
* structure.
|
||||
*/
|
||||
smp_mb();
|
||||
spin_unlock_wait(¤t->sighand->siglock);
|
||||
|
||||
free_uid(old_user);
|
||||
suid_keys(current);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USER_NS
|
||||
void release_uids(struct user_namespace *ns)
|
||||
{
|
||||
int i;
|
||||
unsigned long flags;
|
||||
struct hlist_head *head;
|
||||
struct hlist_node *nd;
|
||||
|
||||
spin_lock_irqsave(&uidhash_lock, flags);
|
||||
/*
|
||||
* collapse the chains so that the user_struct-s will
|
||||
* be still alive, but not in hashes. subsequent free_uid()
|
||||
* will free them.
|
||||
*/
|
||||
for (i = 0; i < UIDHASH_SZ; i++) {
|
||||
head = ns->uidhash_table + i;
|
||||
while (!hlist_empty(head)) {
|
||||
nd = head->first;
|
||||
hlist_del_init(nd);
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&uidhash_lock, flags);
|
||||
|
||||
free_uid(ns->root_user);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int __init uid_cache_init(void)
|
||||
{
|
||||
int n;
|
||||
|
||||
+30
-35
@@ -9,60 +9,55 @@
|
||||
#include <linux/nsproxy.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/user_namespace.h>
|
||||
#include <linux/cred.h>
|
||||
|
||||
/*
|
||||
* Clone a new ns copying an original user ns, setting refcount to 1
|
||||
* @old_ns: namespace to clone
|
||||
* Return NULL on error (failure to kmalloc), new ns otherwise
|
||||
* Create a new user namespace, deriving the creator from the user in the
|
||||
* passed credentials, and replacing that user with the new root user for the
|
||||
* new namespace.
|
||||
*
|
||||
* This is called by copy_creds(), which will finish setting the target task's
|
||||
* credentials.
|
||||
*/
|
||||
static struct user_namespace *clone_user_ns(struct user_namespace *old_ns)
|
||||
int create_user_ns(struct cred *new)
|
||||
{
|
||||
struct user_namespace *ns;
|
||||
struct user_struct *new_user;
|
||||
struct user_struct *root_user;
|
||||
int n;
|
||||
|
||||
ns = kmalloc(sizeof(struct user_namespace), GFP_KERNEL);
|
||||
if (!ns)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
return -ENOMEM;
|
||||
|
||||
kref_init(&ns->kref);
|
||||
|
||||
for (n = 0; n < UIDHASH_SZ; ++n)
|
||||
INIT_HLIST_HEAD(ns->uidhash_table + n);
|
||||
|
||||
/* Insert new root user. */
|
||||
ns->root_user = alloc_uid(ns, 0);
|
||||
if (!ns->root_user) {
|
||||
/* Alloc new root user. */
|
||||
root_user = alloc_uid(ns, 0);
|
||||
if (!root_user) {
|
||||
kfree(ns);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Reset current->user with a new one */
|
||||
new_user = alloc_uid(ns, current->uid);
|
||||
if (!new_user) {
|
||||
free_uid(ns->root_user);
|
||||
kfree(ns);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
/* set the new root user in the credentials under preparation */
|
||||
ns->creator = new->user;
|
||||
new->user = root_user;
|
||||
new->uid = new->euid = new->suid = new->fsuid = 0;
|
||||
new->gid = new->egid = new->sgid = new->fsgid = 0;
|
||||
put_group_info(new->group_info);
|
||||
new->group_info = get_group_info(&init_groups);
|
||||
#ifdef CONFIG_KEYS
|
||||
key_put(new->request_key_auth);
|
||||
new->request_key_auth = NULL;
|
||||
#endif
|
||||
/* tgcred will be cleared in our caller bc CLONE_THREAD won't be set */
|
||||
|
||||
switch_uid(new_user);
|
||||
return ns;
|
||||
}
|
||||
/* alloc_uid() incremented the userns refcount. Just set it to 1 */
|
||||
kref_set(&ns->kref, 1);
|
||||
|
||||
struct user_namespace * copy_user_ns(int flags, struct user_namespace *old_ns)
|
||||
{
|
||||
struct user_namespace *new_ns;
|
||||
|
||||
BUG_ON(!old_ns);
|
||||
get_user_ns(old_ns);
|
||||
|
||||
if (!(flags & CLONE_NEWUSER))
|
||||
return old_ns;
|
||||
|
||||
new_ns = clone_user_ns(old_ns);
|
||||
|
||||
put_user_ns(old_ns);
|
||||
return new_ns;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void free_user_ns(struct kref *kref)
|
||||
@@ -70,7 +65,7 @@ void free_user_ns(struct kref *kref)
|
||||
struct user_namespace *ns;
|
||||
|
||||
ns = container_of(kref, struct user_namespace, kref);
|
||||
release_uids(ns);
|
||||
free_uid(ns->creator);
|
||||
kfree(ns);
|
||||
}
|
||||
EXPORT_SYMBOL(free_user_ns);
|
||||
|
||||
+4
-4
@@ -84,21 +84,21 @@ static cpumask_t cpu_singlethread_map __read_mostly;
|
||||
static cpumask_t cpu_populated_map __read_mostly;
|
||||
|
||||
/* If it's single threaded, it isn't in the list of workqueues. */
|
||||
static inline int is_single_threaded(struct workqueue_struct *wq)
|
||||
static inline int is_wq_single_threaded(struct workqueue_struct *wq)
|
||||
{
|
||||
return wq->singlethread;
|
||||
}
|
||||
|
||||
static const cpumask_t *wq_cpu_map(struct workqueue_struct *wq)
|
||||
{
|
||||
return is_single_threaded(wq)
|
||||
return is_wq_single_threaded(wq)
|
||||
? &cpu_singlethread_map : &cpu_populated_map;
|
||||
}
|
||||
|
||||
static
|
||||
struct cpu_workqueue_struct *wq_per_cpu(struct workqueue_struct *wq, int cpu)
|
||||
{
|
||||
if (unlikely(is_single_threaded(wq)))
|
||||
if (unlikely(is_wq_single_threaded(wq)))
|
||||
cpu = singlethread_cpu;
|
||||
return per_cpu_ptr(wq->cpu_wq, cpu);
|
||||
}
|
||||
@@ -769,7 +769,7 @@ static int create_workqueue_thread(struct cpu_workqueue_struct *cwq, int cpu)
|
||||
{
|
||||
struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 };
|
||||
struct workqueue_struct *wq = cwq->wq;
|
||||
const char *fmt = is_single_threaded(wq) ? "%s" : "%s/%d";
|
||||
const char *fmt = is_wq_single_threaded(wq) ? "%s" : "%s/%d";
|
||||
struct task_struct *p;
|
||||
|
||||
p = kthread_create(worker_thread, cwq, fmt, wq->name, cpu);
|
||||
|
||||
Reference in New Issue
Block a user