mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-24 05:29:04 -04:00
Linux 3.8-rc4
-----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.19 (GNU/Linux) iQEcBAABAgAGBQJQ+MDOAAoJEHm+PkMAQRiGFL8H/39bXl3xs/ZkBct3ciICtaAt UeRcdjvBdUrObQmmw8FEFJA62Az74g1UYyNyc0yVJoB6DwL2oawXhGXQXqDXMBMs xJa/dpx41gmcrvsLOJ1v2Tmokk32hgdpLNWwzcn36xZMsrhYUxjxPhVNK4aDbwrW 3uzBj83dK1rvi2PEz3hgHizRhmdD1cNgLXKrczNbT6y6rLp6Jkng2q5UhNfaZUvE A7Ub5/nW+X6gb1PctAjulz2CoySq0FbH4viUc+zLj3NN0L9ImeJvpREauE2QPVgJ a8Sg3E6/cL4wIXBEV1E1KapVTd1XIEGa8yB5X6q8HWNKI8nf7Kwuj65VgvwJIMc= =+VR8 -----END PGP SIGNATURE----- Merge tag 'v3.8-rc4' into irq/core Merge Linux 3.8-rc4 before pulling in new commits - we were on an old v3.7 base. Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
commit
befddb21c8
11344 changed files with 526976 additions and 301009 deletions
|
|
@ -54,7 +54,7 @@ obj-$(CONFIG_DEBUG_SPINLOCK) += spinlock.o
|
|||
obj-$(CONFIG_PROVE_LOCKING) += spinlock.o
|
||||
obj-$(CONFIG_UID16) += uid16.o
|
||||
obj-$(CONFIG_MODULES) += module.o
|
||||
obj-$(CONFIG_MODULE_SIG) += module_signing.o modsign_pubkey.o
|
||||
obj-$(CONFIG_MODULE_SIG) += module_signing.o modsign_pubkey.o modsign_certificate.o
|
||||
obj-$(CONFIG_KALLSYMS) += kallsyms.o
|
||||
obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o
|
||||
obj-$(CONFIG_KEXEC) += kexec.o
|
||||
|
|
@ -110,6 +110,7 @@ obj-$(CONFIG_USER_RETURN_NOTIFIER) += user-return-notifier.o
|
|||
obj-$(CONFIG_PADATA) += padata.o
|
||||
obj-$(CONFIG_CRASH_DUMP) += crash_dump.o
|
||||
obj-$(CONFIG_JUMP_LABEL) += jump_label.o
|
||||
obj-$(CONFIG_CONTEXT_TRACKING) += context_tracking.o
|
||||
|
||||
$(obj)/configs.o: $(obj)/config_data.h
|
||||
|
||||
|
|
@ -136,10 +137,14 @@ ifeq ($(CONFIG_MODULE_SIG),y)
|
|||
#
|
||||
# Pull the signing certificate and any extra certificates into the kernel
|
||||
#
|
||||
extra_certificates:
|
||||
touch $@
|
||||
|
||||
kernel/modsign_pubkey.o: signing_key.x509 extra_certificates
|
||||
quiet_cmd_touch = TOUCH $@
|
||||
cmd_touch = touch $@
|
||||
|
||||
extra_certificates:
|
||||
$(call cmd,touch)
|
||||
|
||||
kernel/modsign_certificate.o: signing_key.x509 extra_certificates
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
|
|
|
|||
|
|
@ -196,6 +196,9 @@ static async_cookie_t __async_schedule(async_func_ptr *ptr, void *data, struct a
|
|||
atomic_inc(&entry_count);
|
||||
spin_unlock_irqrestore(&async_lock, flags);
|
||||
|
||||
/* mark that this task has queued an async job, used by module init */
|
||||
current->flags |= PF_USED_ASYNC;
|
||||
|
||||
/* schedule for execution */
|
||||
queue_work(system_unbound_wq, &entry->work);
|
||||
|
||||
|
|
|
|||
|
|
@ -272,6 +272,8 @@ static int audit_log_config_change(char *function_name, int new, int old,
|
|||
int rc = 0;
|
||||
|
||||
ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE);
|
||||
if (unlikely(!ab))
|
||||
return rc;
|
||||
audit_log_format(ab, "%s=%d old=%d auid=%u ses=%u", function_name, new,
|
||||
old, from_kuid(&init_user_ns, loginuid), sessionid);
|
||||
if (sid) {
|
||||
|
|
@ -619,6 +621,8 @@ static int audit_log_common_recv_msg(struct audit_buffer **ab, u16 msg_type,
|
|||
}
|
||||
|
||||
*ab = audit_log_start(NULL, GFP_KERNEL, msg_type);
|
||||
if (unlikely(!*ab))
|
||||
return rc;
|
||||
audit_log_format(*ab, "pid=%d uid=%u auid=%u ses=%u",
|
||||
task_tgid_vnr(current),
|
||||
from_kuid(&init_user_ns, current_uid()),
|
||||
|
|
@ -1097,6 +1101,23 @@ static inline void audit_get_stamp(struct audit_context *ctx,
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for auditd to drain the queue a little
|
||||
*/
|
||||
static void wait_for_auditd(unsigned long sleep_time)
|
||||
{
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
add_wait_queue(&audit_backlog_wait, &wait);
|
||||
|
||||
if (audit_backlog_limit &&
|
||||
skb_queue_len(&audit_skb_queue) > audit_backlog_limit)
|
||||
schedule_timeout(sleep_time);
|
||||
|
||||
__set_current_state(TASK_RUNNING);
|
||||
remove_wait_queue(&audit_backlog_wait, &wait);
|
||||
}
|
||||
|
||||
/* Obtain an audit buffer. This routine does locking to obtain the
|
||||
* audit buffer, but then no locking is required for calls to
|
||||
* audit_log_*format. If the tsk is a task that is currently in a
|
||||
|
|
@ -1142,20 +1163,13 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask,
|
|||
|
||||
while (audit_backlog_limit
|
||||
&& skb_queue_len(&audit_skb_queue) > audit_backlog_limit + reserve) {
|
||||
if (gfp_mask & __GFP_WAIT && audit_backlog_wait_time
|
||||
&& time_before(jiffies, timeout_start + audit_backlog_wait_time)) {
|
||||
if (gfp_mask & __GFP_WAIT && audit_backlog_wait_time) {
|
||||
unsigned long sleep_time;
|
||||
|
||||
/* Wait for auditd to drain the queue a little */
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
add_wait_queue(&audit_backlog_wait, &wait);
|
||||
|
||||
if (audit_backlog_limit &&
|
||||
skb_queue_len(&audit_skb_queue) > audit_backlog_limit)
|
||||
schedule_timeout(timeout_start + audit_backlog_wait_time - jiffies);
|
||||
|
||||
__set_current_state(TASK_RUNNING);
|
||||
remove_wait_queue(&audit_backlog_wait, &wait);
|
||||
sleep_time = timeout_start + audit_backlog_wait_time -
|
||||
jiffies;
|
||||
if ((long)sleep_time > 0)
|
||||
wait_for_auditd(sleep_time);
|
||||
continue;
|
||||
}
|
||||
if (audit_rate_check() && printk_ratelimit())
|
||||
|
|
|
|||
|
|
@ -249,7 +249,7 @@ static void untag_chunk(struct node *p)
|
|||
list_del_rcu(&chunk->hash);
|
||||
spin_unlock(&hash_lock);
|
||||
spin_unlock(&entry->lock);
|
||||
fsnotify_destroy_mark(entry);
|
||||
fsnotify_destroy_mark(entry, audit_tree_group);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
|
@ -291,7 +291,7 @@ static void untag_chunk(struct node *p)
|
|||
owner->root = new;
|
||||
spin_unlock(&hash_lock);
|
||||
spin_unlock(&entry->lock);
|
||||
fsnotify_destroy_mark(entry);
|
||||
fsnotify_destroy_mark(entry, audit_tree_group);
|
||||
fsnotify_put_mark(&new->mark); /* drop initial reference */
|
||||
goto out;
|
||||
|
||||
|
|
@ -331,7 +331,7 @@ static int create_chunk(struct inode *inode, struct audit_tree *tree)
|
|||
spin_unlock(&hash_lock);
|
||||
chunk->dead = 1;
|
||||
spin_unlock(&entry->lock);
|
||||
fsnotify_destroy_mark(entry);
|
||||
fsnotify_destroy_mark(entry, audit_tree_group);
|
||||
fsnotify_put_mark(entry);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -412,7 +412,7 @@ static int tag_chunk(struct inode *inode, struct audit_tree *tree)
|
|||
spin_unlock(&chunk_entry->lock);
|
||||
spin_unlock(&old_entry->lock);
|
||||
|
||||
fsnotify_destroy_mark(chunk_entry);
|
||||
fsnotify_destroy_mark(chunk_entry, audit_tree_group);
|
||||
|
||||
fsnotify_put_mark(chunk_entry);
|
||||
fsnotify_put_mark(old_entry);
|
||||
|
|
@ -443,17 +443,32 @@ static int tag_chunk(struct inode *inode, struct audit_tree *tree)
|
|||
spin_unlock(&hash_lock);
|
||||
spin_unlock(&chunk_entry->lock);
|
||||
spin_unlock(&old_entry->lock);
|
||||
fsnotify_destroy_mark(old_entry);
|
||||
fsnotify_destroy_mark(old_entry, audit_tree_group);
|
||||
fsnotify_put_mark(chunk_entry); /* drop initial reference */
|
||||
fsnotify_put_mark(old_entry); /* pair to fsnotify_find mark_entry */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void audit_log_remove_rule(struct audit_krule *rule)
|
||||
{
|
||||
struct audit_buffer *ab;
|
||||
|
||||
ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE);
|
||||
if (unlikely(!ab))
|
||||
return;
|
||||
audit_log_format(ab, "op=");
|
||||
audit_log_string(ab, "remove rule");
|
||||
audit_log_format(ab, " dir=");
|
||||
audit_log_untrustedstring(ab, rule->tree->pathname);
|
||||
audit_log_key(ab, rule->filterkey);
|
||||
audit_log_format(ab, " list=%d res=1", rule->listnr);
|
||||
audit_log_end(ab);
|
||||
}
|
||||
|
||||
static void kill_rules(struct audit_tree *tree)
|
||||
{
|
||||
struct audit_krule *rule, *next;
|
||||
struct audit_entry *entry;
|
||||
struct audit_buffer *ab;
|
||||
|
||||
list_for_each_entry_safe(rule, next, &tree->rules, rlist) {
|
||||
entry = container_of(rule, struct audit_entry, rule);
|
||||
|
|
@ -461,14 +476,7 @@ static void kill_rules(struct audit_tree *tree)
|
|||
list_del_init(&rule->rlist);
|
||||
if (rule->tree) {
|
||||
/* not a half-baked one */
|
||||
ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE);
|
||||
audit_log_format(ab, "op=");
|
||||
audit_log_string(ab, "remove rule");
|
||||
audit_log_format(ab, " dir=");
|
||||
audit_log_untrustedstring(ab, rule->tree->pathname);
|
||||
audit_log_key(ab, rule->filterkey);
|
||||
audit_log_format(ab, " list=%d res=1", rule->listnr);
|
||||
audit_log_end(ab);
|
||||
audit_log_remove_rule(rule);
|
||||
rule->tree = NULL;
|
||||
list_del_rcu(&entry->list);
|
||||
list_del(&entry->rule.list);
|
||||
|
|
|
|||
|
|
@ -240,6 +240,8 @@ static void audit_watch_log_rule_change(struct audit_krule *r, struct audit_watc
|
|||
if (audit_enabled) {
|
||||
struct audit_buffer *ab;
|
||||
ab = audit_log_start(NULL, GFP_NOFS, AUDIT_CONFIG_CHANGE);
|
||||
if (unlikely(!ab))
|
||||
return;
|
||||
audit_log_format(ab, "auid=%u ses=%u op=",
|
||||
from_kuid(&init_user_ns, audit_get_loginuid(current)),
|
||||
audit_get_sessionid(current));
|
||||
|
|
@ -350,7 +352,7 @@ static void audit_remove_parent_watches(struct audit_parent *parent)
|
|||
}
|
||||
mutex_unlock(&audit_filter_mutex);
|
||||
|
||||
fsnotify_destroy_mark(&parent->mark);
|
||||
fsnotify_destroy_mark(&parent->mark, audit_watch_group);
|
||||
}
|
||||
|
||||
/* Get path information necessary for adding watches. */
|
||||
|
|
@ -457,7 +459,7 @@ void audit_remove_watch_rule(struct audit_krule *krule)
|
|||
|
||||
if (list_empty(&parent->watches)) {
|
||||
audit_get_parent(parent);
|
||||
fsnotify_destroy_mark(&parent->mark);
|
||||
fsnotify_destroy_mark(&parent->mark, audit_watch_group);
|
||||
audit_put_parent(parent);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1144,7 +1144,6 @@ static void audit_log_rule_change(kuid_t loginuid, u32 sessionid, u32 sid,
|
|||
* audit_receive_filter - apply all rules to the specified message type
|
||||
* @type: audit message type
|
||||
* @pid: target pid for netlink audit messages
|
||||
* @uid: target uid for netlink audit messages
|
||||
* @seq: netlink audit message sequence (serial) number
|
||||
* @data: payload data
|
||||
* @datasz: size of payload data
|
||||
|
|
|
|||
124
kernel/auditsc.c
124
kernel/auditsc.c
|
|
@ -200,7 +200,6 @@ struct audit_context {
|
|||
struct list_head names_list; /* anchor for struct audit_names->list */
|
||||
char * filterkey; /* key for rule that triggered record */
|
||||
struct path pwd;
|
||||
struct audit_context *previous; /* For nested syscalls */
|
||||
struct audit_aux_data *aux;
|
||||
struct audit_aux_data *aux_pids;
|
||||
struct sockaddr_storage *sockaddr;
|
||||
|
|
@ -1091,29 +1090,13 @@ int audit_alloc(struct task_struct *tsk)
|
|||
|
||||
static inline void audit_free_context(struct audit_context *context)
|
||||
{
|
||||
struct audit_context *previous;
|
||||
int count = 0;
|
||||
|
||||
do {
|
||||
previous = context->previous;
|
||||
if (previous || (count && count < 10)) {
|
||||
++count;
|
||||
printk(KERN_ERR "audit(:%d): major=%d name_count=%d:"
|
||||
" freeing multiple contexts (%d)\n",
|
||||
context->serial, context->major,
|
||||
context->name_count, count);
|
||||
}
|
||||
audit_free_names(context);
|
||||
unroll_tree_refs(context, NULL, 0);
|
||||
free_tree_refs(context);
|
||||
audit_free_aux(context);
|
||||
kfree(context->filterkey);
|
||||
kfree(context->sockaddr);
|
||||
kfree(context);
|
||||
context = previous;
|
||||
} while (context);
|
||||
if (count >= 10)
|
||||
printk(KERN_ERR "audit: freed %d contexts\n", count);
|
||||
audit_free_names(context);
|
||||
unroll_tree_refs(context, NULL, 0);
|
||||
free_tree_refs(context);
|
||||
audit_free_aux(context);
|
||||
kfree(context->filterkey);
|
||||
kfree(context->sockaddr);
|
||||
kfree(context);
|
||||
}
|
||||
|
||||
void audit_log_task_context(struct audit_buffer *ab)
|
||||
|
|
@ -1159,7 +1142,7 @@ void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk)
|
|||
cred = current_cred();
|
||||
|
||||
spin_lock_irq(&tsk->sighand->siglock);
|
||||
if (tsk->signal && tsk->signal->tty && tsk->signal->tty->name)
|
||||
if (tsk->signal && tsk->signal->tty)
|
||||
tty = tsk->signal->tty->name;
|
||||
else
|
||||
tty = "(none)";
|
||||
|
|
@ -1481,14 +1464,14 @@ static void show_special(struct audit_context *context, int *call_panic)
|
|||
audit_log_end(ab);
|
||||
ab = audit_log_start(context, GFP_KERNEL,
|
||||
AUDIT_IPC_SET_PERM);
|
||||
if (unlikely(!ab))
|
||||
return;
|
||||
audit_log_format(ab,
|
||||
"qbytes=%lx ouid=%u ogid=%u mode=%#ho",
|
||||
context->ipc.qbytes,
|
||||
context->ipc.perm_uid,
|
||||
context->ipc.perm_gid,
|
||||
context->ipc.perm_mode);
|
||||
if (!ab)
|
||||
return;
|
||||
}
|
||||
break; }
|
||||
case AUDIT_MQ_OPEN: {
|
||||
|
|
@ -1783,42 +1766,6 @@ void __audit_syscall_entry(int arch, int major,
|
|||
if (!context)
|
||||
return;
|
||||
|
||||
/*
|
||||
* This happens only on certain architectures that make system
|
||||
* calls in kernel_thread via the entry.S interface, instead of
|
||||
* with direct calls. (If you are porting to a new
|
||||
* architecture, hitting this condition can indicate that you
|
||||
* got the _exit/_leave calls backward in entry.S.)
|
||||
*
|
||||
* i386 no
|
||||
* x86_64 no
|
||||
* ppc64 yes (see arch/powerpc/platforms/iseries/misc.S)
|
||||
*
|
||||
* This also happens with vm86 emulation in a non-nested manner
|
||||
* (entries without exits), so this case must be caught.
|
||||
*/
|
||||
if (context->in_syscall) {
|
||||
struct audit_context *newctx;
|
||||
|
||||
#if AUDIT_DEBUG
|
||||
printk(KERN_ERR
|
||||
"audit(:%d) pid=%d in syscall=%d;"
|
||||
" entering syscall=%d\n",
|
||||
context->serial, tsk->pid, context->major, major);
|
||||
#endif
|
||||
newctx = audit_alloc_context(context->state);
|
||||
if (newctx) {
|
||||
newctx->previous = context;
|
||||
context = newctx;
|
||||
tsk->audit_context = newctx;
|
||||
} else {
|
||||
/* If we can't alloc a new context, the best we
|
||||
* can do is to leak memory (any pending putname
|
||||
* will be lost). The only other alternative is
|
||||
* to abandon auditing. */
|
||||
audit_zero_context(context, context->state);
|
||||
}
|
||||
}
|
||||
BUG_ON(context->in_syscall || context->name_count);
|
||||
|
||||
if (!audit_enabled)
|
||||
|
|
@ -1881,28 +1828,21 @@ void __audit_syscall_exit(int success, long return_code)
|
|||
if (!list_empty(&context->killed_trees))
|
||||
audit_kill_trees(&context->killed_trees);
|
||||
|
||||
if (context->previous) {
|
||||
struct audit_context *new_context = context->previous;
|
||||
context->previous = NULL;
|
||||
audit_free_context(context);
|
||||
tsk->audit_context = new_context;
|
||||
} else {
|
||||
audit_free_names(context);
|
||||
unroll_tree_refs(context, NULL, 0);
|
||||
audit_free_aux(context);
|
||||
context->aux = NULL;
|
||||
context->aux_pids = NULL;
|
||||
context->target_pid = 0;
|
||||
context->target_sid = 0;
|
||||
context->sockaddr_len = 0;
|
||||
context->type = 0;
|
||||
context->fds[0] = -1;
|
||||
if (context->state != AUDIT_RECORD_CONTEXT) {
|
||||
kfree(context->filterkey);
|
||||
context->filterkey = NULL;
|
||||
}
|
||||
tsk->audit_context = context;
|
||||
audit_free_names(context);
|
||||
unroll_tree_refs(context, NULL, 0);
|
||||
audit_free_aux(context);
|
||||
context->aux = NULL;
|
||||
context->aux_pids = NULL;
|
||||
context->target_pid = 0;
|
||||
context->target_sid = 0;
|
||||
context->sockaddr_len = 0;
|
||||
context->type = 0;
|
||||
context->fds[0] = -1;
|
||||
if (context->state != AUDIT_RECORD_CONTEXT) {
|
||||
kfree(context->filterkey);
|
||||
context->filterkey = NULL;
|
||||
}
|
||||
tsk->audit_context = context;
|
||||
}
|
||||
|
||||
static inline void handle_one(const struct inode *inode)
|
||||
|
|
@ -2735,7 +2675,7 @@ void __audit_mmap_fd(int fd, int flags)
|
|||
context->type = AUDIT_MMAP;
|
||||
}
|
||||
|
||||
static void audit_log_abend(struct audit_buffer *ab, char *reason, long signr)
|
||||
static void audit_log_task(struct audit_buffer *ab)
|
||||
{
|
||||
kuid_t auid, uid;
|
||||
kgid_t gid;
|
||||
|
|
@ -2753,6 +2693,11 @@ static void audit_log_abend(struct audit_buffer *ab, char *reason, long signr)
|
|||
audit_log_task_context(ab);
|
||||
audit_log_format(ab, " pid=%d comm=", current->pid);
|
||||
audit_log_untrustedstring(ab, current->comm);
|
||||
}
|
||||
|
||||
static void audit_log_abend(struct audit_buffer *ab, char *reason, long signr)
|
||||
{
|
||||
audit_log_task(ab);
|
||||
audit_log_format(ab, " reason=");
|
||||
audit_log_string(ab, reason);
|
||||
audit_log_format(ab, " sig=%ld", signr);
|
||||
|
|
@ -2775,6 +2720,8 @@ void audit_core_dumps(long signr)
|
|||
return;
|
||||
|
||||
ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_ANOM_ABEND);
|
||||
if (unlikely(!ab))
|
||||
return;
|
||||
audit_log_abend(ab, "memory violation", signr);
|
||||
audit_log_end(ab);
|
||||
}
|
||||
|
|
@ -2783,8 +2730,11 @@ void __audit_seccomp(unsigned long syscall, long signr, int code)
|
|||
{
|
||||
struct audit_buffer *ab;
|
||||
|
||||
ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_ANOM_ABEND);
|
||||
audit_log_abend(ab, "seccomp", signr);
|
||||
ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_SECCOMP);
|
||||
if (unlikely(!ab))
|
||||
return;
|
||||
audit_log_task(ab);
|
||||
audit_log_format(ab, " sig=%ld", signr);
|
||||
audit_log_format(ab, " syscall=%ld", syscall);
|
||||
audit_log_format(ab, " compat=%d", is_compat_task());
|
||||
audit_log_format(ab, " ip=0x%lx", KSTK_EIP(current));
|
||||
|
|
|
|||
759
kernel/cgroup.c
759
kernel/cgroup.c
File diff suppressed because it is too large
Load diff
|
|
@ -22,24 +22,33 @@
|
|||
#include <linux/freezer.h>
|
||||
#include <linux/seq_file.h>
|
||||
|
||||
enum freezer_state {
|
||||
CGROUP_THAWED = 0,
|
||||
CGROUP_FREEZING,
|
||||
CGROUP_FROZEN,
|
||||
/*
|
||||
* A cgroup is freezing if any FREEZING flags are set. FREEZING_SELF is
|
||||
* set if "FROZEN" is written to freezer.state cgroupfs file, and cleared
|
||||
* for "THAWED". FREEZING_PARENT is set if the parent freezer is FREEZING
|
||||
* for whatever reason. IOW, a cgroup has FREEZING_PARENT set if one of
|
||||
* its ancestors has FREEZING_SELF set.
|
||||
*/
|
||||
enum freezer_state_flags {
|
||||
CGROUP_FREEZER_ONLINE = (1 << 0), /* freezer is fully online */
|
||||
CGROUP_FREEZING_SELF = (1 << 1), /* this freezer is freezing */
|
||||
CGROUP_FREEZING_PARENT = (1 << 2), /* the parent freezer is freezing */
|
||||
CGROUP_FROZEN = (1 << 3), /* this and its descendants frozen */
|
||||
|
||||
/* mask for all FREEZING flags */
|
||||
CGROUP_FREEZING = CGROUP_FREEZING_SELF | CGROUP_FREEZING_PARENT,
|
||||
};
|
||||
|
||||
struct freezer {
|
||||
struct cgroup_subsys_state css;
|
||||
enum freezer_state state;
|
||||
spinlock_t lock; /* protects _writes_ to state */
|
||||
struct cgroup_subsys_state css;
|
||||
unsigned int state;
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
static inline struct freezer *cgroup_freezer(
|
||||
struct cgroup *cgroup)
|
||||
static inline struct freezer *cgroup_freezer(struct cgroup *cgroup)
|
||||
{
|
||||
return container_of(
|
||||
cgroup_subsys_state(cgroup, freezer_subsys_id),
|
||||
struct freezer, css);
|
||||
return container_of(cgroup_subsys_state(cgroup, freezer_subsys_id),
|
||||
struct freezer, css);
|
||||
}
|
||||
|
||||
static inline struct freezer *task_freezer(struct task_struct *task)
|
||||
|
|
@ -48,14 +57,21 @@ static inline struct freezer *task_freezer(struct task_struct *task)
|
|||
struct freezer, css);
|
||||
}
|
||||
|
||||
static struct freezer *parent_freezer(struct freezer *freezer)
|
||||
{
|
||||
struct cgroup *pcg = freezer->css.cgroup->parent;
|
||||
|
||||
if (pcg)
|
||||
return cgroup_freezer(pcg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool cgroup_freezing(struct task_struct *task)
|
||||
{
|
||||
enum freezer_state state;
|
||||
bool ret;
|
||||
|
||||
rcu_read_lock();
|
||||
state = task_freezer(task)->state;
|
||||
ret = state == CGROUP_FREEZING || state == CGROUP_FROZEN;
|
||||
ret = task_freezer(task)->state & CGROUP_FREEZING;
|
||||
rcu_read_unlock();
|
||||
|
||||
return ret;
|
||||
|
|
@ -65,70 +81,18 @@ bool cgroup_freezing(struct task_struct *task)
|
|||
* cgroups_write_string() limits the size of freezer state strings to
|
||||
* CGROUP_LOCAL_BUFFER_SIZE
|
||||
*/
|
||||
static const char *freezer_state_strs[] = {
|
||||
"THAWED",
|
||||
"FREEZING",
|
||||
"FROZEN",
|
||||
static const char *freezer_state_strs(unsigned int state)
|
||||
{
|
||||
if (state & CGROUP_FROZEN)
|
||||
return "FROZEN";
|
||||
if (state & CGROUP_FREEZING)
|
||||
return "FREEZING";
|
||||
return "THAWED";
|
||||
};
|
||||
|
||||
/*
|
||||
* State diagram
|
||||
* Transitions are caused by userspace writes to the freezer.state file.
|
||||
* The values in parenthesis are state labels. The rest are edge labels.
|
||||
*
|
||||
* (THAWED) --FROZEN--> (FREEZING) --FROZEN--> (FROZEN)
|
||||
* ^ ^ | |
|
||||
* | \_______THAWED_______/ |
|
||||
* \__________________________THAWED____________/
|
||||
*/
|
||||
|
||||
struct cgroup_subsys freezer_subsys;
|
||||
|
||||
/* Locks taken and their ordering
|
||||
* ------------------------------
|
||||
* cgroup_mutex (AKA cgroup_lock)
|
||||
* freezer->lock
|
||||
* css_set_lock
|
||||
* task->alloc_lock (AKA task_lock)
|
||||
* task->sighand->siglock
|
||||
*
|
||||
* cgroup code forces css_set_lock to be taken before task->alloc_lock
|
||||
*
|
||||
* freezer_create(), freezer_destroy():
|
||||
* cgroup_mutex [ by cgroup core ]
|
||||
*
|
||||
* freezer_can_attach():
|
||||
* cgroup_mutex (held by caller of can_attach)
|
||||
*
|
||||
* freezer_fork() (preserving fork() performance means can't take cgroup_mutex):
|
||||
* freezer->lock
|
||||
* sighand->siglock (if the cgroup is freezing)
|
||||
*
|
||||
* freezer_read():
|
||||
* cgroup_mutex
|
||||
* freezer->lock
|
||||
* write_lock css_set_lock (cgroup iterator start)
|
||||
* task->alloc_lock
|
||||
* read_lock css_set_lock (cgroup iterator start)
|
||||
*
|
||||
* freezer_write() (freeze):
|
||||
* cgroup_mutex
|
||||
* freezer->lock
|
||||
* write_lock css_set_lock (cgroup iterator start)
|
||||
* task->alloc_lock
|
||||
* read_lock css_set_lock (cgroup iterator start)
|
||||
* sighand->siglock (fake signal delivery inside freeze_task())
|
||||
*
|
||||
* freezer_write() (unfreeze):
|
||||
* cgroup_mutex
|
||||
* freezer->lock
|
||||
* write_lock css_set_lock (cgroup iterator start)
|
||||
* task->alloc_lock
|
||||
* read_lock css_set_lock (cgroup iterator start)
|
||||
* task->alloc_lock (inside __thaw_task(), prevents race with refrigerator())
|
||||
* sighand->siglock
|
||||
*/
|
||||
static struct cgroup_subsys_state *freezer_create(struct cgroup *cgroup)
|
||||
static struct cgroup_subsys_state *freezer_css_alloc(struct cgroup *cgroup)
|
||||
{
|
||||
struct freezer *freezer;
|
||||
|
||||
|
|
@ -137,160 +101,244 @@ static struct cgroup_subsys_state *freezer_create(struct cgroup *cgroup)
|
|||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
spin_lock_init(&freezer->lock);
|
||||
freezer->state = CGROUP_THAWED;
|
||||
return &freezer->css;
|
||||
}
|
||||
|
||||
static void freezer_destroy(struct cgroup *cgroup)
|
||||
/**
|
||||
* freezer_css_online - commit creation of a freezer cgroup
|
||||
* @cgroup: cgroup being created
|
||||
*
|
||||
* We're committing to creation of @cgroup. Mark it online and inherit
|
||||
* parent's freezing state while holding both parent's and our
|
||||
* freezer->lock.
|
||||
*/
|
||||
static int freezer_css_online(struct cgroup *cgroup)
|
||||
{
|
||||
struct freezer *freezer = cgroup_freezer(cgroup);
|
||||
struct freezer *parent = parent_freezer(freezer);
|
||||
|
||||
/*
|
||||
* The following double locking and freezing state inheritance
|
||||
* guarantee that @cgroup can never escape ancestors' freezing
|
||||
* states. See cgroup_for_each_descendant_pre() for details.
|
||||
*/
|
||||
if (parent)
|
||||
spin_lock_irq(&parent->lock);
|
||||
spin_lock_nested(&freezer->lock, SINGLE_DEPTH_NESTING);
|
||||
|
||||
freezer->state |= CGROUP_FREEZER_ONLINE;
|
||||
|
||||
if (parent && (parent->state & CGROUP_FREEZING)) {
|
||||
freezer->state |= CGROUP_FREEZING_PARENT | CGROUP_FROZEN;
|
||||
atomic_inc(&system_freezing_cnt);
|
||||
}
|
||||
|
||||
spin_unlock(&freezer->lock);
|
||||
if (parent)
|
||||
spin_unlock_irq(&parent->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* freezer_css_offline - initiate destruction of @cgroup
|
||||
* @cgroup: cgroup being destroyed
|
||||
*
|
||||
* @cgroup is going away. Mark it dead and decrement system_freezing_count
|
||||
* if it was holding one.
|
||||
*/
|
||||
static void freezer_css_offline(struct cgroup *cgroup)
|
||||
{
|
||||
struct freezer *freezer = cgroup_freezer(cgroup);
|
||||
|
||||
if (freezer->state != CGROUP_THAWED)
|
||||
spin_lock_irq(&freezer->lock);
|
||||
|
||||
if (freezer->state & CGROUP_FREEZING)
|
||||
atomic_dec(&system_freezing_cnt);
|
||||
kfree(freezer);
|
||||
|
||||
freezer->state = 0;
|
||||
|
||||
spin_unlock_irq(&freezer->lock);
|
||||
}
|
||||
|
||||
/* task is frozen or will freeze immediately when next it gets woken */
|
||||
static bool is_task_frozen_enough(struct task_struct *task)
|
||||
static void freezer_css_free(struct cgroup *cgroup)
|
||||
{
|
||||
return frozen(task) ||
|
||||
(task_is_stopped_or_traced(task) && freezing(task));
|
||||
kfree(cgroup_freezer(cgroup));
|
||||
}
|
||||
|
||||
/*
|
||||
* The call to cgroup_lock() in the freezer.state write method prevents
|
||||
* a write to that file racing against an attach, and hence the
|
||||
* can_attach() result will remain valid until the attach completes.
|
||||
* Tasks can be migrated into a different freezer anytime regardless of its
|
||||
* current state. freezer_attach() is responsible for making new tasks
|
||||
* conform to the current state.
|
||||
*
|
||||
* Freezer state changes and task migration are synchronized via
|
||||
* @freezer->lock. freezer_attach() makes the new tasks conform to the
|
||||
* current state and all following state changes can see the new tasks.
|
||||
*/
|
||||
static int freezer_can_attach(struct cgroup *new_cgroup,
|
||||
struct cgroup_taskset *tset)
|
||||
static void freezer_attach(struct cgroup *new_cgrp, struct cgroup_taskset *tset)
|
||||
{
|
||||
struct freezer *freezer;
|
||||
struct freezer *freezer = cgroup_freezer(new_cgrp);
|
||||
struct task_struct *task;
|
||||
bool clear_frozen = false;
|
||||
|
||||
spin_lock_irq(&freezer->lock);
|
||||
|
||||
/*
|
||||
* Anything frozen can't move or be moved to/from.
|
||||
* Make the new tasks conform to the current state of @new_cgrp.
|
||||
* For simplicity, when migrating any task to a FROZEN cgroup, we
|
||||
* revert it to FREEZING and let update_if_frozen() determine the
|
||||
* correct state later.
|
||||
*
|
||||
* Tasks in @tset are on @new_cgrp but may not conform to its
|
||||
* current state before executing the following - !frozen tasks may
|
||||
* be visible in a FROZEN cgroup and frozen tasks in a THAWED one.
|
||||
*/
|
||||
cgroup_taskset_for_each(task, new_cgroup, tset)
|
||||
if (cgroup_freezing(task))
|
||||
return -EBUSY;
|
||||
cgroup_taskset_for_each(task, new_cgrp, tset) {
|
||||
if (!(freezer->state & CGROUP_FREEZING)) {
|
||||
__thaw_task(task);
|
||||
} else {
|
||||
freeze_task(task);
|
||||
freezer->state &= ~CGROUP_FROZEN;
|
||||
clear_frozen = true;
|
||||
}
|
||||
}
|
||||
|
||||
freezer = cgroup_freezer(new_cgroup);
|
||||
if (freezer->state != CGROUP_THAWED)
|
||||
return -EBUSY;
|
||||
spin_unlock_irq(&freezer->lock);
|
||||
|
||||
return 0;
|
||||
/*
|
||||
* Propagate FROZEN clearing upwards. We may race with
|
||||
* update_if_frozen(), but as long as both work bottom-up, either
|
||||
* update_if_frozen() sees child's FROZEN cleared or we clear the
|
||||
* parent's FROZEN later. No parent w/ !FROZEN children can be
|
||||
* left FROZEN.
|
||||
*/
|
||||
while (clear_frozen && (freezer = parent_freezer(freezer))) {
|
||||
spin_lock_irq(&freezer->lock);
|
||||
freezer->state &= ~CGROUP_FROZEN;
|
||||
clear_frozen = freezer->state & CGROUP_FREEZING;
|
||||
spin_unlock_irq(&freezer->lock);
|
||||
}
|
||||
}
|
||||
|
||||
static void freezer_fork(struct task_struct *task)
|
||||
{
|
||||
struct freezer *freezer;
|
||||
|
||||
/*
|
||||
* No lock is needed, since the task isn't on tasklist yet,
|
||||
* so it can't be moved to another cgroup, which means the
|
||||
* freezer won't be removed and will be valid during this
|
||||
* function call. Nevertheless, apply RCU read-side critical
|
||||
* section to suppress RCU lockdep false positives.
|
||||
*/
|
||||
rcu_read_lock();
|
||||
freezer = task_freezer(task);
|
||||
rcu_read_unlock();
|
||||
|
||||
/*
|
||||
* The root cgroup is non-freezable, so we can skip the
|
||||
* following check.
|
||||
*/
|
||||
if (!freezer->css.cgroup->parent)
|
||||
return;
|
||||
goto out;
|
||||
|
||||
spin_lock_irq(&freezer->lock);
|
||||
BUG_ON(freezer->state == CGROUP_FROZEN);
|
||||
|
||||
/* Locking avoids race with FREEZING -> THAWED transitions. */
|
||||
if (freezer->state == CGROUP_FREEZING)
|
||||
if (freezer->state & CGROUP_FREEZING)
|
||||
freeze_task(task);
|
||||
spin_unlock_irq(&freezer->lock);
|
||||
out:
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
/*
|
||||
* caller must hold freezer->lock
|
||||
/**
|
||||
* update_if_frozen - update whether a cgroup finished freezing
|
||||
* @cgroup: cgroup of interest
|
||||
*
|
||||
* Once FREEZING is initiated, transition to FROZEN is lazily updated by
|
||||
* calling this function. If the current state is FREEZING but not FROZEN,
|
||||
* this function checks whether all tasks of this cgroup and the descendant
|
||||
* cgroups finished freezing and, if so, sets FROZEN.
|
||||
*
|
||||
* The caller is responsible for grabbing RCU read lock and calling
|
||||
* update_if_frozen() on all descendants prior to invoking this function.
|
||||
*
|
||||
* Task states and freezer state might disagree while tasks are being
|
||||
* migrated into or out of @cgroup, so we can't verify task states against
|
||||
* @freezer state here. See freezer_attach() for details.
|
||||
*/
|
||||
static void update_if_frozen(struct cgroup *cgroup,
|
||||
struct freezer *freezer)
|
||||
static void update_if_frozen(struct cgroup *cgroup)
|
||||
{
|
||||
struct freezer *freezer = cgroup_freezer(cgroup);
|
||||
struct cgroup *pos;
|
||||
struct cgroup_iter it;
|
||||
struct task_struct *task;
|
||||
unsigned int nfrozen = 0, ntotal = 0;
|
||||
enum freezer_state old_state = freezer->state;
|
||||
|
||||
WARN_ON_ONCE(!rcu_read_lock_held());
|
||||
|
||||
spin_lock_irq(&freezer->lock);
|
||||
|
||||
if (!(freezer->state & CGROUP_FREEZING) ||
|
||||
(freezer->state & CGROUP_FROZEN))
|
||||
goto out_unlock;
|
||||
|
||||
/* are all (live) children frozen? */
|
||||
cgroup_for_each_child(pos, cgroup) {
|
||||
struct freezer *child = cgroup_freezer(pos);
|
||||
|
||||
if ((child->state & CGROUP_FREEZER_ONLINE) &&
|
||||
!(child->state & CGROUP_FROZEN))
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
/* are all tasks frozen? */
|
||||
cgroup_iter_start(cgroup, &it);
|
||||
|
||||
while ((task = cgroup_iter_next(cgroup, &it))) {
|
||||
ntotal++;
|
||||
if (freezing(task) && is_task_frozen_enough(task))
|
||||
nfrozen++;
|
||||
}
|
||||
|
||||
if (old_state == CGROUP_THAWED) {
|
||||
BUG_ON(nfrozen > 0);
|
||||
} else if (old_state == CGROUP_FREEZING) {
|
||||
if (nfrozen == ntotal)
|
||||
freezer->state = CGROUP_FROZEN;
|
||||
} else { /* old_state == CGROUP_FROZEN */
|
||||
BUG_ON(nfrozen != ntotal);
|
||||
if (freezing(task)) {
|
||||
/*
|
||||
* freezer_should_skip() indicates that the task
|
||||
* should be skipped when determining freezing
|
||||
* completion. Consider it frozen in addition to
|
||||
* the usual frozen condition.
|
||||
*/
|
||||
if (!frozen(task) && !freezer_should_skip(task))
|
||||
goto out_iter_end;
|
||||
}
|
||||
}
|
||||
|
||||
freezer->state |= CGROUP_FROZEN;
|
||||
out_iter_end:
|
||||
cgroup_iter_end(cgroup, &it);
|
||||
out_unlock:
|
||||
spin_unlock_irq(&freezer->lock);
|
||||
}
|
||||
|
||||
static int freezer_read(struct cgroup *cgroup, struct cftype *cft,
|
||||
struct seq_file *m)
|
||||
{
|
||||
struct freezer *freezer;
|
||||
enum freezer_state state;
|
||||
struct cgroup *pos;
|
||||
|
||||
if (!cgroup_lock_live_group(cgroup))
|
||||
return -ENODEV;
|
||||
rcu_read_lock();
|
||||
|
||||
freezer = cgroup_freezer(cgroup);
|
||||
spin_lock_irq(&freezer->lock);
|
||||
state = freezer->state;
|
||||
if (state == CGROUP_FREEZING) {
|
||||
/* We change from FREEZING to FROZEN lazily if the cgroup was
|
||||
* only partially frozen when we exitted write. */
|
||||
update_if_frozen(cgroup, freezer);
|
||||
state = freezer->state;
|
||||
}
|
||||
spin_unlock_irq(&freezer->lock);
|
||||
cgroup_unlock();
|
||||
/* update states bottom-up */
|
||||
cgroup_for_each_descendant_post(pos, cgroup)
|
||||
update_if_frozen(pos);
|
||||
update_if_frozen(cgroup);
|
||||
|
||||
seq_puts(m, freezer_state_strs[state]);
|
||||
rcu_read_unlock();
|
||||
|
||||
seq_puts(m, freezer_state_strs(cgroup_freezer(cgroup)->state));
|
||||
seq_putc(m, '\n');
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int try_to_freeze_cgroup(struct cgroup *cgroup, struct freezer *freezer)
|
||||
static void freeze_cgroup(struct freezer *freezer)
|
||||
{
|
||||
struct cgroup *cgroup = freezer->css.cgroup;
|
||||
struct cgroup_iter it;
|
||||
struct task_struct *task;
|
||||
unsigned int num_cant_freeze_now = 0;
|
||||
|
||||
cgroup_iter_start(cgroup, &it);
|
||||
while ((task = cgroup_iter_next(cgroup, &it))) {
|
||||
if (!freeze_task(task))
|
||||
continue;
|
||||
if (is_task_frozen_enough(task))
|
||||
continue;
|
||||
if (!freezing(task) && !freezer_should_skip(task))
|
||||
num_cant_freeze_now++;
|
||||
}
|
||||
while ((task = cgroup_iter_next(cgroup, &it)))
|
||||
freeze_task(task);
|
||||
cgroup_iter_end(cgroup, &it);
|
||||
|
||||
return num_cant_freeze_now ? -EBUSY : 0;
|
||||
}
|
||||
|
||||
static void unfreeze_cgroup(struct cgroup *cgroup, struct freezer *freezer)
|
||||
static void unfreeze_cgroup(struct freezer *freezer)
|
||||
{
|
||||
struct cgroup *cgroup = freezer->css.cgroup;
|
||||
struct cgroup_iter it;
|
||||
struct task_struct *task;
|
||||
|
||||
|
|
@ -300,59 +348,111 @@ static void unfreeze_cgroup(struct cgroup *cgroup, struct freezer *freezer)
|
|||
cgroup_iter_end(cgroup, &it);
|
||||
}
|
||||
|
||||
static int freezer_change_state(struct cgroup *cgroup,
|
||||
enum freezer_state goal_state)
|
||||
/**
|
||||
* freezer_apply_state - apply state change to a single cgroup_freezer
|
||||
* @freezer: freezer to apply state change to
|
||||
* @freeze: whether to freeze or unfreeze
|
||||
* @state: CGROUP_FREEZING_* flag to set or clear
|
||||
*
|
||||
* Set or clear @state on @cgroup according to @freeze, and perform
|
||||
* freezing or thawing as necessary.
|
||||
*/
|
||||
static void freezer_apply_state(struct freezer *freezer, bool freeze,
|
||||
unsigned int state)
|
||||
{
|
||||
struct freezer *freezer;
|
||||
int retval = 0;
|
||||
/* also synchronizes against task migration, see freezer_attach() */
|
||||
lockdep_assert_held(&freezer->lock);
|
||||
|
||||
freezer = cgroup_freezer(cgroup);
|
||||
if (!(freezer->state & CGROUP_FREEZER_ONLINE))
|
||||
return;
|
||||
|
||||
spin_lock_irq(&freezer->lock);
|
||||
|
||||
update_if_frozen(cgroup, freezer);
|
||||
|
||||
switch (goal_state) {
|
||||
case CGROUP_THAWED:
|
||||
if (freezer->state != CGROUP_THAWED)
|
||||
atomic_dec(&system_freezing_cnt);
|
||||
freezer->state = CGROUP_THAWED;
|
||||
unfreeze_cgroup(cgroup, freezer);
|
||||
break;
|
||||
case CGROUP_FROZEN:
|
||||
if (freezer->state == CGROUP_THAWED)
|
||||
if (freeze) {
|
||||
if (!(freezer->state & CGROUP_FREEZING))
|
||||
atomic_inc(&system_freezing_cnt);
|
||||
freezer->state = CGROUP_FREEZING;
|
||||
retval = try_to_freeze_cgroup(cgroup, freezer);
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
freezer->state |= state;
|
||||
freeze_cgroup(freezer);
|
||||
} else {
|
||||
bool was_freezing = freezer->state & CGROUP_FREEZING;
|
||||
|
||||
freezer->state &= ~state;
|
||||
|
||||
if (!(freezer->state & CGROUP_FREEZING)) {
|
||||
if (was_freezing)
|
||||
atomic_dec(&system_freezing_cnt);
|
||||
freezer->state &= ~CGROUP_FROZEN;
|
||||
unfreeze_cgroup(freezer);
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock_irq(&freezer->lock);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int freezer_write(struct cgroup *cgroup,
|
||||
struct cftype *cft,
|
||||
/**
|
||||
* freezer_change_state - change the freezing state of a cgroup_freezer
|
||||
* @freezer: freezer of interest
|
||||
* @freeze: whether to freeze or thaw
|
||||
*
|
||||
* Freeze or thaw @freezer according to @freeze. The operations are
|
||||
* recursive - all descendants of @freezer will be affected.
|
||||
*/
|
||||
static void freezer_change_state(struct freezer *freezer, bool freeze)
|
||||
{
|
||||
struct cgroup *pos;
|
||||
|
||||
/* update @freezer */
|
||||
spin_lock_irq(&freezer->lock);
|
||||
freezer_apply_state(freezer, freeze, CGROUP_FREEZING_SELF);
|
||||
spin_unlock_irq(&freezer->lock);
|
||||
|
||||
/*
|
||||
* Update all its descendants in pre-order traversal. Each
|
||||
* descendant will try to inherit its parent's FREEZING state as
|
||||
* CGROUP_FREEZING_PARENT.
|
||||
*/
|
||||
rcu_read_lock();
|
||||
cgroup_for_each_descendant_pre(pos, freezer->css.cgroup) {
|
||||
struct freezer *pos_f = cgroup_freezer(pos);
|
||||
struct freezer *parent = parent_freezer(pos_f);
|
||||
|
||||
/*
|
||||
* Our update to @parent->state is already visible which is
|
||||
* all we need. No need to lock @parent. For more info on
|
||||
* synchronization, see freezer_post_create().
|
||||
*/
|
||||
spin_lock_irq(&pos_f->lock);
|
||||
freezer_apply_state(pos_f, parent->state & CGROUP_FREEZING,
|
||||
CGROUP_FREEZING_PARENT);
|
||||
spin_unlock_irq(&pos_f->lock);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
static int freezer_write(struct cgroup *cgroup, struct cftype *cft,
|
||||
const char *buffer)
|
||||
{
|
||||
int retval;
|
||||
enum freezer_state goal_state;
|
||||
bool freeze;
|
||||
|
||||
if (strcmp(buffer, freezer_state_strs[CGROUP_THAWED]) == 0)
|
||||
goal_state = CGROUP_THAWED;
|
||||
else if (strcmp(buffer, freezer_state_strs[CGROUP_FROZEN]) == 0)
|
||||
goal_state = CGROUP_FROZEN;
|
||||
if (strcmp(buffer, freezer_state_strs(0)) == 0)
|
||||
freeze = false;
|
||||
else if (strcmp(buffer, freezer_state_strs(CGROUP_FROZEN)) == 0)
|
||||
freeze = true;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
if (!cgroup_lock_live_group(cgroup))
|
||||
return -ENODEV;
|
||||
retval = freezer_change_state(cgroup, goal_state);
|
||||
cgroup_unlock();
|
||||
return retval;
|
||||
freezer_change_state(cgroup_freezer(cgroup), freeze);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u64 freezer_self_freezing_read(struct cgroup *cgroup, struct cftype *cft)
|
||||
{
|
||||
struct freezer *freezer = cgroup_freezer(cgroup);
|
||||
|
||||
return (bool)(freezer->state & CGROUP_FREEZING_SELF);
|
||||
}
|
||||
|
||||
static u64 freezer_parent_freezing_read(struct cgroup *cgroup, struct cftype *cft)
|
||||
{
|
||||
struct freezer *freezer = cgroup_freezer(cgroup);
|
||||
|
||||
return (bool)(freezer->state & CGROUP_FREEZING_PARENT);
|
||||
}
|
||||
|
||||
static struct cftype files[] = {
|
||||
|
|
@ -362,23 +462,27 @@ static struct cftype files[] = {
|
|||
.read_seq_string = freezer_read,
|
||||
.write_string = freezer_write,
|
||||
},
|
||||
{
|
||||
.name = "self_freezing",
|
||||
.flags = CFTYPE_NOT_ON_ROOT,
|
||||
.read_u64 = freezer_self_freezing_read,
|
||||
},
|
||||
{
|
||||
.name = "parent_freezing",
|
||||
.flags = CFTYPE_NOT_ON_ROOT,
|
||||
.read_u64 = freezer_parent_freezing_read,
|
||||
},
|
||||
{ } /* terminate */
|
||||
};
|
||||
|
||||
struct cgroup_subsys freezer_subsys = {
|
||||
.name = "freezer",
|
||||
.create = freezer_create,
|
||||
.destroy = freezer_destroy,
|
||||
.css_alloc = freezer_css_alloc,
|
||||
.css_online = freezer_css_online,
|
||||
.css_offline = freezer_css_offline,
|
||||
.css_free = freezer_css_free,
|
||||
.subsys_id = freezer_subsys_id,
|
||||
.can_attach = freezer_can_attach,
|
||||
.attach = freezer_attach,
|
||||
.fork = freezer_fork,
|
||||
.base_cftypes = files,
|
||||
|
||||
/*
|
||||
* freezer subsys doesn't handle hierarchy at all. Frozen state
|
||||
* should be inherited through the hierarchy - if a parent is
|
||||
* frozen, all its children should be frozen. Fix it and remove
|
||||
* the following.
|
||||
*/
|
||||
.broken_hierarchy = true,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1215,6 +1215,23 @@ compat_sys_sysinfo(struct compat_sysinfo __user *info)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef __ARCH_WANT_COMPAT_SYS_SCHED_RR_GET_INTERVAL
|
||||
asmlinkage long compat_sys_sched_rr_get_interval(compat_pid_t pid,
|
||||
struct compat_timespec __user *interval)
|
||||
{
|
||||
struct timespec t;
|
||||
int ret;
|
||||
mm_segment_t old_fs = get_fs();
|
||||
|
||||
set_fs(KERNEL_DS);
|
||||
ret = sys_sched_rr_get_interval(pid, (struct timespec __user *)&t);
|
||||
set_fs(old_fs);
|
||||
if (put_compat_timespec(&t, interval))
|
||||
return -EFAULT;
|
||||
return ret;
|
||||
}
|
||||
#endif /* __ARCH_WANT_COMPAT_SYS_SCHED_RR_GET_INTERVAL */
|
||||
|
||||
/*
|
||||
* Allocate user-space memory for the duration of a single system call,
|
||||
* in order to marshall parameters inside a compat thunk.
|
||||
|
|
|
|||
83
kernel/context_tracking.c
Normal file
83
kernel/context_tracking.c
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
#include <linux/context_tracking.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/hardirq.h>
|
||||
|
||||
struct context_tracking {
|
||||
/*
|
||||
* When active is false, hooks are not set to
|
||||
* minimize overhead: TIF flags are cleared
|
||||
* and calls to user_enter/exit are ignored. This
|
||||
* may be further optimized using static keys.
|
||||
*/
|
||||
bool active;
|
||||
enum {
|
||||
IN_KERNEL = 0,
|
||||
IN_USER,
|
||||
} state;
|
||||
};
|
||||
|
||||
static DEFINE_PER_CPU(struct context_tracking, context_tracking) = {
|
||||
#ifdef CONFIG_CONTEXT_TRACKING_FORCE
|
||||
.active = true,
|
||||
#endif
|
||||
};
|
||||
|
||||
void user_enter(void)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
/*
|
||||
* Some contexts may involve an exception occuring in an irq,
|
||||
* leading to that nesting:
|
||||
* rcu_irq_enter() rcu_user_exit() rcu_user_exit() rcu_irq_exit()
|
||||
* This would mess up the dyntick_nesting count though. And rcu_irq_*()
|
||||
* helpers are enough to protect RCU uses inside the exception. So
|
||||
* just return immediately if we detect we are in an IRQ.
|
||||
*/
|
||||
if (in_interrupt())
|
||||
return;
|
||||
|
||||
WARN_ON_ONCE(!current->mm);
|
||||
|
||||
local_irq_save(flags);
|
||||
if (__this_cpu_read(context_tracking.active) &&
|
||||
__this_cpu_read(context_tracking.state) != IN_USER) {
|
||||
__this_cpu_write(context_tracking.state, IN_USER);
|
||||
rcu_user_enter();
|
||||
}
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
void user_exit(void)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
/*
|
||||
* Some contexts may involve an exception occuring in an irq,
|
||||
* leading to that nesting:
|
||||
* rcu_irq_enter() rcu_user_exit() rcu_user_exit() rcu_irq_exit()
|
||||
* This would mess up the dyntick_nesting count though. And rcu_irq_*()
|
||||
* helpers are enough to protect RCU uses inside the exception. So
|
||||
* just return immediately if we detect we are in an IRQ.
|
||||
*/
|
||||
if (in_interrupt())
|
||||
return;
|
||||
|
||||
local_irq_save(flags);
|
||||
if (__this_cpu_read(context_tracking.state) == IN_USER) {
|
||||
__this_cpu_write(context_tracking.state, IN_KERNEL);
|
||||
rcu_user_exit();
|
||||
}
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
void context_tracking_task_switch(struct task_struct *prev,
|
||||
struct task_struct *next)
|
||||
{
|
||||
if (__this_cpu_read(context_tracking.active)) {
|
||||
clear_tsk_thread_flag(prev, TIF_NOHZ);
|
||||
set_tsk_thread_flag(next, TIF_NOHZ);
|
||||
}
|
||||
}
|
||||
13
kernel/cpu.c
13
kernel/cpu.c
|
|
@ -348,11 +348,13 @@ static int __cpuinit _cpu_up(unsigned int cpu, int tasks_frozen)
|
|||
unsigned long mod = tasks_frozen ? CPU_TASKS_FROZEN : 0;
|
||||
struct task_struct *idle;
|
||||
|
||||
if (cpu_online(cpu) || !cpu_present(cpu))
|
||||
return -EINVAL;
|
||||
|
||||
cpu_hotplug_begin();
|
||||
|
||||
if (cpu_online(cpu) || !cpu_present(cpu)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
idle = idle_thread_get(cpu);
|
||||
if (IS_ERR(idle)) {
|
||||
ret = PTR_ERR(idle);
|
||||
|
|
@ -601,6 +603,11 @@ cpu_hotplug_pm_callback(struct notifier_block *nb,
|
|||
|
||||
static int __init cpu_hotplug_pm_sync_init(void)
|
||||
{
|
||||
/*
|
||||
* cpu_hotplug_pm_callback has higher priority than x86
|
||||
* bsp_pm_callback which depends on cpu_hotplug_pm_callback
|
||||
* to disable cpu hotplug to avoid cpu hotplug race.
|
||||
*/
|
||||
pm_notifier(cpu_hotplug_pm_callback, 0);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
122
kernel/cpuset.c
122
kernel/cpuset.c
|
|
@ -302,10 +302,10 @@ static void guarantee_online_cpus(const struct cpuset *cs,
|
|||
* are online, with memory. If none are online with memory, walk
|
||||
* up the cpuset hierarchy until we find one that does have some
|
||||
* online mems. If we get all the way to the top and still haven't
|
||||
* found any online mems, return node_states[N_HIGH_MEMORY].
|
||||
* found any online mems, return node_states[N_MEMORY].
|
||||
*
|
||||
* One way or another, we guarantee to return some non-empty subset
|
||||
* of node_states[N_HIGH_MEMORY].
|
||||
* of node_states[N_MEMORY].
|
||||
*
|
||||
* Call with callback_mutex held.
|
||||
*/
|
||||
|
|
@ -313,14 +313,14 @@ static void guarantee_online_cpus(const struct cpuset *cs,
|
|||
static void guarantee_online_mems(const struct cpuset *cs, nodemask_t *pmask)
|
||||
{
|
||||
while (cs && !nodes_intersects(cs->mems_allowed,
|
||||
node_states[N_HIGH_MEMORY]))
|
||||
node_states[N_MEMORY]))
|
||||
cs = cs->parent;
|
||||
if (cs)
|
||||
nodes_and(*pmask, cs->mems_allowed,
|
||||
node_states[N_HIGH_MEMORY]);
|
||||
node_states[N_MEMORY]);
|
||||
else
|
||||
*pmask = node_states[N_HIGH_MEMORY];
|
||||
BUG_ON(!nodes_intersects(*pmask, node_states[N_HIGH_MEMORY]));
|
||||
*pmask = node_states[N_MEMORY];
|
||||
BUG_ON(!nodes_intersects(*pmask, node_states[N_MEMORY]));
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1100,7 +1100,7 @@ static int update_nodemask(struct cpuset *cs, struct cpuset *trialcs,
|
|||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* top_cpuset.mems_allowed tracks node_stats[N_HIGH_MEMORY];
|
||||
* top_cpuset.mems_allowed tracks node_stats[N_MEMORY];
|
||||
* it's read-only
|
||||
*/
|
||||
if (cs == &top_cpuset) {
|
||||
|
|
@ -1122,7 +1122,7 @@ static int update_nodemask(struct cpuset *cs, struct cpuset *trialcs,
|
|||
goto done;
|
||||
|
||||
if (!nodes_subset(trialcs->mems_allowed,
|
||||
node_states[N_HIGH_MEMORY])) {
|
||||
node_states[N_MEMORY])) {
|
||||
retval = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -1784,56 +1784,20 @@ static struct cftype files[] = {
|
|||
};
|
||||
|
||||
/*
|
||||
* post_clone() is called during cgroup_create() when the
|
||||
* clone_children mount argument was specified. The cgroup
|
||||
* can not yet have any tasks.
|
||||
*
|
||||
* Currently we refuse to set up the cgroup - thereby
|
||||
* refusing the task to be entered, and as a result refusing
|
||||
* the sys_unshare() or clone() which initiated it - if any
|
||||
* sibling cpusets have exclusive cpus or mem.
|
||||
*
|
||||
* If this becomes a problem for some users who wish to
|
||||
* allow that scenario, then cpuset_post_clone() could be
|
||||
* changed to grant parent->cpus_allowed-sibling_cpus_exclusive
|
||||
* (and likewise for mems) to the new cgroup. Called with cgroup_mutex
|
||||
* held.
|
||||
*/
|
||||
static void cpuset_post_clone(struct cgroup *cgroup)
|
||||
{
|
||||
struct cgroup *parent, *child;
|
||||
struct cpuset *cs, *parent_cs;
|
||||
|
||||
parent = cgroup->parent;
|
||||
list_for_each_entry(child, &parent->children, sibling) {
|
||||
cs = cgroup_cs(child);
|
||||
if (is_mem_exclusive(cs) || is_cpu_exclusive(cs))
|
||||
return;
|
||||
}
|
||||
cs = cgroup_cs(cgroup);
|
||||
parent_cs = cgroup_cs(parent);
|
||||
|
||||
mutex_lock(&callback_mutex);
|
||||
cs->mems_allowed = parent_cs->mems_allowed;
|
||||
cpumask_copy(cs->cpus_allowed, parent_cs->cpus_allowed);
|
||||
mutex_unlock(&callback_mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* cpuset_create - create a cpuset
|
||||
* cpuset_css_alloc - allocate a cpuset css
|
||||
* cont: control group that the new cpuset will be part of
|
||||
*/
|
||||
|
||||
static struct cgroup_subsys_state *cpuset_create(struct cgroup *cont)
|
||||
static struct cgroup_subsys_state *cpuset_css_alloc(struct cgroup *cont)
|
||||
{
|
||||
struct cpuset *cs;
|
||||
struct cpuset *parent;
|
||||
struct cgroup *parent_cg = cont->parent;
|
||||
struct cgroup *tmp_cg;
|
||||
struct cpuset *parent, *cs;
|
||||
|
||||
if (!cont->parent) {
|
||||
if (!parent_cg)
|
||||
return &top_cpuset.css;
|
||||
}
|
||||
parent = cgroup_cs(cont->parent);
|
||||
parent = cgroup_cs(parent_cg);
|
||||
|
||||
cs = kmalloc(sizeof(*cs), GFP_KERNEL);
|
||||
if (!cs)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
|
@ -1855,7 +1819,36 @@ static struct cgroup_subsys_state *cpuset_create(struct cgroup *cont)
|
|||
|
||||
cs->parent = parent;
|
||||
number_of_cpusets++;
|
||||
return &cs->css ;
|
||||
|
||||
if (!test_bit(CGRP_CPUSET_CLONE_CHILDREN, &cont->flags))
|
||||
goto skip_clone;
|
||||
|
||||
/*
|
||||
* Clone @parent's configuration if CGRP_CPUSET_CLONE_CHILDREN is
|
||||
* set. This flag handling is implemented in cgroup core for
|
||||
* histrical reasons - the flag may be specified during mount.
|
||||
*
|
||||
* Currently, if any sibling cpusets have exclusive cpus or mem, we
|
||||
* refuse to clone the configuration - thereby refusing the task to
|
||||
* be entered, and as a result refusing the sys_unshare() or
|
||||
* clone() which initiated it. If this becomes a problem for some
|
||||
* users who wish to allow that scenario, then this could be
|
||||
* changed to grant parent->cpus_allowed-sibling_cpus_exclusive
|
||||
* (and likewise for mems) to the new cgroup.
|
||||
*/
|
||||
list_for_each_entry(tmp_cg, &parent_cg->children, sibling) {
|
||||
struct cpuset *tmp_cs = cgroup_cs(tmp_cg);
|
||||
|
||||
if (is_mem_exclusive(tmp_cs) || is_cpu_exclusive(tmp_cs))
|
||||
goto skip_clone;
|
||||
}
|
||||
|
||||
mutex_lock(&callback_mutex);
|
||||
cs->mems_allowed = parent->mems_allowed;
|
||||
cpumask_copy(cs->cpus_allowed, parent->cpus_allowed);
|
||||
mutex_unlock(&callback_mutex);
|
||||
skip_clone:
|
||||
return &cs->css;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1864,7 +1857,7 @@ static struct cgroup_subsys_state *cpuset_create(struct cgroup *cont)
|
|||
* will call async_rebuild_sched_domains().
|
||||
*/
|
||||
|
||||
static void cpuset_destroy(struct cgroup *cont)
|
||||
static void cpuset_css_free(struct cgroup *cont)
|
||||
{
|
||||
struct cpuset *cs = cgroup_cs(cont);
|
||||
|
||||
|
|
@ -1878,11 +1871,10 @@ static void cpuset_destroy(struct cgroup *cont)
|
|||
|
||||
struct cgroup_subsys cpuset_subsys = {
|
||||
.name = "cpuset",
|
||||
.create = cpuset_create,
|
||||
.destroy = cpuset_destroy,
|
||||
.css_alloc = cpuset_css_alloc,
|
||||
.css_free = cpuset_css_free,
|
||||
.can_attach = cpuset_can_attach,
|
||||
.attach = cpuset_attach,
|
||||
.post_clone = cpuset_post_clone,
|
||||
.subsys_id = cpuset_subsys_id,
|
||||
.base_cftypes = files,
|
||||
.early_init = 1,
|
||||
|
|
@ -2034,7 +2026,7 @@ static struct cpuset *cpuset_next(struct list_head *queue)
|
|||
* before dropping down to the next. It always processes a node before
|
||||
* any of its children.
|
||||
*
|
||||
* In the case of memory hot-unplug, it will remove nodes from N_HIGH_MEMORY
|
||||
* In the case of memory hot-unplug, it will remove nodes from N_MEMORY
|
||||
* if all present pages from a node are offlined.
|
||||
*/
|
||||
static void
|
||||
|
|
@ -2073,7 +2065,7 @@ scan_cpusets_upon_hotplug(struct cpuset *root, enum hotplug_event event)
|
|||
|
||||
/* Continue past cpusets with all mems online */
|
||||
if (nodes_subset(cp->mems_allowed,
|
||||
node_states[N_HIGH_MEMORY]))
|
||||
node_states[N_MEMORY]))
|
||||
continue;
|
||||
|
||||
oldmems = cp->mems_allowed;
|
||||
|
|
@ -2081,7 +2073,7 @@ scan_cpusets_upon_hotplug(struct cpuset *root, enum hotplug_event event)
|
|||
/* Remove offline mems from this cpuset. */
|
||||
mutex_lock(&callback_mutex);
|
||||
nodes_and(cp->mems_allowed, cp->mems_allowed,
|
||||
node_states[N_HIGH_MEMORY]);
|
||||
node_states[N_MEMORY]);
|
||||
mutex_unlock(&callback_mutex);
|
||||
|
||||
/* Move tasks from the empty cpuset to a parent */
|
||||
|
|
@ -2134,8 +2126,8 @@ void cpuset_update_active_cpus(bool cpu_online)
|
|||
|
||||
#ifdef CONFIG_MEMORY_HOTPLUG
|
||||
/*
|
||||
* Keep top_cpuset.mems_allowed tracking node_states[N_HIGH_MEMORY].
|
||||
* Call this routine anytime after node_states[N_HIGH_MEMORY] changes.
|
||||
* Keep top_cpuset.mems_allowed tracking node_states[N_MEMORY].
|
||||
* Call this routine anytime after node_states[N_MEMORY] changes.
|
||||
* See cpuset_update_active_cpus() for CPU hotplug handling.
|
||||
*/
|
||||
static int cpuset_track_online_nodes(struct notifier_block *self,
|
||||
|
|
@ -2148,7 +2140,7 @@ static int cpuset_track_online_nodes(struct notifier_block *self,
|
|||
case MEM_ONLINE:
|
||||
oldmems = top_cpuset.mems_allowed;
|
||||
mutex_lock(&callback_mutex);
|
||||
top_cpuset.mems_allowed = node_states[N_HIGH_MEMORY];
|
||||
top_cpuset.mems_allowed = node_states[N_MEMORY];
|
||||
mutex_unlock(&callback_mutex);
|
||||
update_tasks_nodemask(&top_cpuset, &oldmems, NULL);
|
||||
break;
|
||||
|
|
@ -2177,7 +2169,7 @@ static int cpuset_track_online_nodes(struct notifier_block *self,
|
|||
void __init cpuset_init_smp(void)
|
||||
{
|
||||
cpumask_copy(top_cpuset.cpus_allowed, cpu_active_mask);
|
||||
top_cpuset.mems_allowed = node_states[N_HIGH_MEMORY];
|
||||
top_cpuset.mems_allowed = node_states[N_MEMORY];
|
||||
|
||||
hotplug_memory_notifier(cpuset_track_online_nodes, 10);
|
||||
|
||||
|
|
@ -2245,7 +2237,7 @@ void cpuset_init_current_mems_allowed(void)
|
|||
*
|
||||
* Description: Returns the nodemask_t mems_allowed of the cpuset
|
||||
* attached to the specified @tsk. Guaranteed to return some non-empty
|
||||
* subset of node_states[N_HIGH_MEMORY], even if this means going outside the
|
||||
* subset of node_states[N_MEMORY], even if this means going outside the
|
||||
* tasks cpuset.
|
||||
**/
|
||||
|
||||
|
|
|
|||
154
kernel/cred.c
154
kernel/cred.c
|
|
@ -29,17 +29,6 @@
|
|||
|
||||
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(init_cred.tgcred.lock),
|
||||
};
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The initial credentials for the initial task
|
||||
*/
|
||||
|
|
@ -65,9 +54,6 @@ struct cred init_cred = {
|
|||
.user = INIT_USER,
|
||||
.user_ns = &init_user_ns,
|
||||
.group_info = &init_groups,
|
||||
#ifdef CONFIG_KEYS
|
||||
.tgcred = &init_tgcred,
|
||||
#endif
|
||||
};
|
||||
|
||||
static inline void set_cred_subscribers(struct cred *cred, int n)
|
||||
|
|
@ -95,36 +81,6 @@ static inline void alter_cred_subscribers(const struct cred *_cred, int n)
|
|||
#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
|
||||
*/
|
||||
|
|
@ -150,9 +106,10 @@ static void put_cred_rcu(struct rcu_head *rcu)
|
|||
#endif
|
||||
|
||||
security_cred_free(cred);
|
||||
key_put(cred->session_keyring);
|
||||
key_put(cred->process_keyring);
|
||||
key_put(cred->thread_keyring);
|
||||
key_put(cred->request_key_auth);
|
||||
release_tgcred(cred);
|
||||
if (cred->group_info)
|
||||
put_group_info(cred->group_info);
|
||||
free_uid(cred->user);
|
||||
|
|
@ -246,15 +203,6 @@ struct cred *cred_alloc_blank(void)
|
|||
if (!new)
|
||||
return NULL;
|
||||
|
||||
#ifdef CONFIG_KEYS
|
||||
new->tgcred = kzalloc(sizeof(*new->tgcred), GFP_KERNEL);
|
||||
if (!new->tgcred) {
|
||||
kmem_cache_free(cred_jar, new);
|
||||
return NULL;
|
||||
}
|
||||
atomic_set(&new->tgcred->usage, 1);
|
||||
#endif
|
||||
|
||||
atomic_set(&new->usage, 1);
|
||||
#ifdef CONFIG_DEBUG_CREDENTIALS
|
||||
new->magic = CRED_MAGIC;
|
||||
|
|
@ -308,9 +256,10 @@ struct cred *prepare_creds(void)
|
|||
get_user_ns(new->user_ns);
|
||||
|
||||
#ifdef CONFIG_KEYS
|
||||
key_get(new->session_keyring);
|
||||
key_get(new->process_keyring);
|
||||
key_get(new->thread_keyring);
|
||||
key_get(new->request_key_auth);
|
||||
atomic_inc(&new->tgcred->usage);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SECURITY
|
||||
|
|
@ -334,39 +283,20 @@ EXPORT_SYMBOL(prepare_creds);
|
|||
*/
|
||||
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);
|
||||
if (!new)
|
||||
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;
|
||||
key_put(new->process_keyring);
|
||||
new->process_keyring = NULL;
|
||||
#endif
|
||||
|
||||
return new;
|
||||
|
|
@ -383,9 +313,6 @@ struct cred *prepare_exec_creds(void)
|
|||
*/
|
||||
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;
|
||||
|
||||
|
|
@ -425,22 +352,12 @@ int copy_creds(struct task_struct *p, unsigned long clone_flags)
|
|||
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 */
|
||||
/* The process keyring is only shared between the threads in a process;
|
||||
* anything outside of those threads doesn't inherit.
|
||||
*/
|
||||
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;
|
||||
key_put(new->process_keyring);
|
||||
new->process_keyring = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
@ -455,6 +372,31 @@ error_put:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static bool cred_cap_issubset(const struct cred *set, const struct cred *subset)
|
||||
{
|
||||
const struct user_namespace *set_ns = set->user_ns;
|
||||
const struct user_namespace *subset_ns = subset->user_ns;
|
||||
|
||||
/* If the two credentials are in the same user namespace see if
|
||||
* the capabilities of subset are a subset of set.
|
||||
*/
|
||||
if (set_ns == subset_ns)
|
||||
return cap_issubset(subset->cap_permitted, set->cap_permitted);
|
||||
|
||||
/* The credentials are in a different user namespaces
|
||||
* therefore one is a subset of the other only if a set is an
|
||||
* ancestor of subset and set->euid is owner of subset or one
|
||||
* of subsets ancestors.
|
||||
*/
|
||||
for (;subset_ns != &init_user_ns; subset_ns = subset_ns->parent) {
|
||||
if ((set_ns == subset_ns->parent) &&
|
||||
uid_eq(subset_ns->owner, set->euid))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* commit_creds - Install new credentials upon the current task
|
||||
* @new: The credentials to be assigned
|
||||
|
|
@ -493,7 +435,7 @@ int commit_creds(struct cred *new)
|
|||
!gid_eq(old->egid, new->egid) ||
|
||||
!uid_eq(old->fsuid, new->fsuid) ||
|
||||
!gid_eq(old->fsgid, new->fsgid) ||
|
||||
!cap_issubset(new->cap_permitted, old->cap_permitted)) {
|
||||
!cred_cap_issubset(old, new)) {
|
||||
if (task->mm)
|
||||
set_dumpable(task->mm, suid_dumpable);
|
||||
task->pdeath_signal = 0;
|
||||
|
|
@ -643,9 +585,6 @@ void __init cred_init(void)
|
|||
*/
|
||||
struct cred *prepare_kernel_cred(struct task_struct *daemon)
|
||||
{
|
||||
#ifdef CONFIG_KEYS
|
||||
struct thread_group_cred *tgcred;
|
||||
#endif
|
||||
const struct cred *old;
|
||||
struct cred *new;
|
||||
|
||||
|
|
@ -653,14 +592,6 @@ struct cred *prepare_kernel_cred(struct task_struct *daemon)
|
|||
if (!new)
|
||||
return NULL;
|
||||
|
||||
#ifdef CONFIG_KEYS
|
||||
tgcred = kmalloc(sizeof(*tgcred), GFP_KERNEL);
|
||||
if (!tgcred) {
|
||||
kmem_cache_free(cred_jar, new);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
kdebug("prepare_kernel_cred() alloc %p", new);
|
||||
|
||||
if (daemon)
|
||||
|
|
@ -678,13 +609,10 @@ struct cred *prepare_kernel_cred(struct task_struct *daemon)
|
|||
get_group_info(new->group_info);
|
||||
|
||||
#ifdef CONFIG_KEYS
|
||||
atomic_set(&tgcred->usage, 1);
|
||||
spin_lock_init(&tgcred->lock);
|
||||
tgcred->process_keyring = NULL;
|
||||
tgcred->session_keyring = NULL;
|
||||
new->tgcred = tgcred;
|
||||
new->request_key_auth = NULL;
|
||||
new->session_keyring = NULL;
|
||||
new->process_keyring = NULL;
|
||||
new->thread_keyring = NULL;
|
||||
new->request_key_auth = NULL;
|
||||
new->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING;
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -6155,7 +6155,7 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu,
|
|||
|
||||
event->parent = parent_event;
|
||||
|
||||
event->ns = get_pid_ns(current->nsproxy->pid_ns);
|
||||
event->ns = get_pid_ns(task_active_pid_ns(current));
|
||||
event->id = atomic64_inc_return(&perf_event_id);
|
||||
|
||||
event->state = PERF_EVENT_STATE_INACTIVE;
|
||||
|
|
@ -7434,7 +7434,7 @@ unlock:
|
|||
device_initcall(perf_event_sysfs_init);
|
||||
|
||||
#ifdef CONFIG_CGROUP_PERF
|
||||
static struct cgroup_subsys_state *perf_cgroup_create(struct cgroup *cont)
|
||||
static struct cgroup_subsys_state *perf_cgroup_css_alloc(struct cgroup *cont)
|
||||
{
|
||||
struct perf_cgroup *jc;
|
||||
|
||||
|
|
@ -7451,7 +7451,7 @@ static struct cgroup_subsys_state *perf_cgroup_create(struct cgroup *cont)
|
|||
return &jc->css;
|
||||
}
|
||||
|
||||
static void perf_cgroup_destroy(struct cgroup *cont)
|
||||
static void perf_cgroup_css_free(struct cgroup *cont)
|
||||
{
|
||||
struct perf_cgroup *jc;
|
||||
jc = container_of(cgroup_subsys_state(cont, perf_subsys_id),
|
||||
|
|
@ -7492,8 +7492,8 @@ static void perf_cgroup_exit(struct cgroup *cgrp, struct cgroup *old_cgrp,
|
|||
struct cgroup_subsys perf_subsys = {
|
||||
.name = "perf_event",
|
||||
.subsys_id = perf_subsys_id,
|
||||
.create = perf_cgroup_create,
|
||||
.destroy = perf_cgroup_destroy,
|
||||
.css_alloc = perf_cgroup_css_alloc,
|
||||
.css_free = perf_cgroup_css_free,
|
||||
.exit = perf_cgroup_exit,
|
||||
.attach = perf_cgroup_attach,
|
||||
|
||||
|
|
|
|||
|
|
@ -111,14 +111,16 @@ static unsigned int max_task_bp_pinned(int cpu, enum bp_type_idx type)
|
|||
* Count the number of breakpoints of the same type and same task.
|
||||
* The given event must be not on the list.
|
||||
*/
|
||||
static int task_bp_pinned(struct perf_event *bp, enum bp_type_idx type)
|
||||
static int task_bp_pinned(int cpu, struct perf_event *bp, enum bp_type_idx type)
|
||||
{
|
||||
struct task_struct *tsk = bp->hw.bp_target;
|
||||
struct perf_event *iter;
|
||||
int count = 0;
|
||||
|
||||
list_for_each_entry(iter, &bp_task_head, hw.bp_list) {
|
||||
if (iter->hw.bp_target == tsk && find_slot_idx(iter) == type)
|
||||
if (iter->hw.bp_target == tsk &&
|
||||
find_slot_idx(iter) == type &&
|
||||
cpu == iter->cpu)
|
||||
count += hw_breakpoint_weight(iter);
|
||||
}
|
||||
|
||||
|
|
@ -141,7 +143,7 @@ fetch_bp_busy_slots(struct bp_busy_slots *slots, struct perf_event *bp,
|
|||
if (!tsk)
|
||||
slots->pinned += max_task_bp_pinned(cpu, type);
|
||||
else
|
||||
slots->pinned += task_bp_pinned(bp, type);
|
||||
slots->pinned += task_bp_pinned(cpu, bp, type);
|
||||
slots->flexible = per_cpu(nr_bp_flexible[type], cpu);
|
||||
|
||||
return;
|
||||
|
|
@ -154,7 +156,7 @@ fetch_bp_busy_slots(struct bp_busy_slots *slots, struct perf_event *bp,
|
|||
if (!tsk)
|
||||
nr += max_task_bp_pinned(cpu, type);
|
||||
else
|
||||
nr += task_bp_pinned(bp, type);
|
||||
nr += task_bp_pinned(cpu, bp, type);
|
||||
|
||||
if (nr > slots->pinned)
|
||||
slots->pinned = nr;
|
||||
|
|
@ -188,7 +190,7 @@ static void toggle_bp_task_slot(struct perf_event *bp, int cpu, bool enable,
|
|||
int old_idx = 0;
|
||||
int idx = 0;
|
||||
|
||||
old_count = task_bp_pinned(bp, type);
|
||||
old_count = task_bp_pinned(cpu, bp, type);
|
||||
old_idx = old_count - 1;
|
||||
idx = old_idx + weight;
|
||||
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
#include <linux/ptrace.h> /* user_enable_single_step */
|
||||
#include <linux/kdebug.h> /* notifier mechanism */
|
||||
#include "../../mm/internal.h" /* munlock_vma_page */
|
||||
#include <linux/percpu-rwsem.h>
|
||||
|
||||
#include <linux/uprobes.h>
|
||||
|
||||
|
|
@ -71,6 +72,8 @@ static struct mutex uprobes_mutex[UPROBES_HASH_SZ];
|
|||
static struct mutex uprobes_mmap_mutex[UPROBES_HASH_SZ];
|
||||
#define uprobes_mmap_hash(v) (&uprobes_mmap_mutex[((unsigned long)(v)) % UPROBES_HASH_SZ])
|
||||
|
||||
static struct percpu_rw_semaphore dup_mmap_sem;
|
||||
|
||||
/*
|
||||
* uprobe_events allows us to skip the uprobe_mmap if there are no uprobe
|
||||
* events active at this time. Probably a fine grained per inode count is
|
||||
|
|
@ -766,10 +769,13 @@ static int register_for_each_vma(struct uprobe *uprobe, bool is_register)
|
|||
struct map_info *info;
|
||||
int err = 0;
|
||||
|
||||
percpu_down_write(&dup_mmap_sem);
|
||||
info = build_map_info(uprobe->inode->i_mapping,
|
||||
uprobe->offset, is_register);
|
||||
if (IS_ERR(info))
|
||||
return PTR_ERR(info);
|
||||
if (IS_ERR(info)) {
|
||||
err = PTR_ERR(info);
|
||||
goto out;
|
||||
}
|
||||
|
||||
while (info) {
|
||||
struct mm_struct *mm = info->mm;
|
||||
|
|
@ -799,7 +805,8 @@ static int register_for_each_vma(struct uprobe *uprobe, bool is_register)
|
|||
mmput(mm);
|
||||
info = free_map_info(info);
|
||||
}
|
||||
|
||||
out:
|
||||
percpu_up_write(&dup_mmap_sem);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
@ -1131,6 +1138,16 @@ void uprobe_clear_state(struct mm_struct *mm)
|
|||
kfree(area);
|
||||
}
|
||||
|
||||
void uprobe_start_dup_mmap(void)
|
||||
{
|
||||
percpu_down_read(&dup_mmap_sem);
|
||||
}
|
||||
|
||||
void uprobe_end_dup_mmap(void)
|
||||
{
|
||||
percpu_up_read(&dup_mmap_sem);
|
||||
}
|
||||
|
||||
void uprobe_dup_mmap(struct mm_struct *oldmm, struct mm_struct *newmm)
|
||||
{
|
||||
newmm->uprobes_state.xol_area = NULL;
|
||||
|
|
@ -1199,6 +1216,11 @@ static unsigned long xol_get_insn_slot(struct uprobe *uprobe, unsigned long slot
|
|||
vaddr = kmap_atomic(area->page);
|
||||
memcpy(vaddr + offset, uprobe->arch.insn, MAX_UINSN_BYTES);
|
||||
kunmap_atomic(vaddr);
|
||||
/*
|
||||
* We probably need flush_icache_user_range() but it needs vma.
|
||||
* This should work on supported architectures too.
|
||||
*/
|
||||
flush_dcache_page(area->page);
|
||||
|
||||
return current->utask->xol_vaddr;
|
||||
}
|
||||
|
|
@ -1430,16 +1452,6 @@ static struct uprobe *find_active_uprobe(unsigned long bp_vaddr, int *is_swbp)
|
|||
return uprobe;
|
||||
}
|
||||
|
||||
void __weak arch_uprobe_enable_step(struct arch_uprobe *arch)
|
||||
{
|
||||
user_enable_single_step(current);
|
||||
}
|
||||
|
||||
void __weak arch_uprobe_disable_step(struct arch_uprobe *arch)
|
||||
{
|
||||
user_disable_single_step(current);
|
||||
}
|
||||
|
||||
/*
|
||||
* Run handler and ask thread to singlestep.
|
||||
* Ensure all non-fatal signals cannot interrupt thread while it singlesteps.
|
||||
|
|
@ -1493,7 +1505,6 @@ static void handle_swbp(struct pt_regs *regs)
|
|||
goto out;
|
||||
|
||||
if (!pre_ssout(uprobe, regs, bp_vaddr)) {
|
||||
arch_uprobe_enable_step(&uprobe->arch);
|
||||
utask->active_uprobe = uprobe;
|
||||
utask->state = UTASK_SSTEP;
|
||||
return;
|
||||
|
|
@ -1525,7 +1536,6 @@ static void handle_singlestep(struct uprobe_task *utask, struct pt_regs *regs)
|
|||
else
|
||||
WARN_ON_ONCE(1);
|
||||
|
||||
arch_uprobe_disable_step(&uprobe->arch);
|
||||
put_uprobe(uprobe);
|
||||
utask->active_uprobe = NULL;
|
||||
utask->state = UTASK_RUNNING;
|
||||
|
|
@ -1604,6 +1614,9 @@ static int __init init_uprobes(void)
|
|||
mutex_init(&uprobes_mmap_mutex[i]);
|
||||
}
|
||||
|
||||
if (percpu_init_rwsem(&dup_mmap_sem))
|
||||
return -ENOMEM;
|
||||
|
||||
return register_die_notifier(&uprobe_exception_nb);
|
||||
}
|
||||
module_init(init_uprobes);
|
||||
|
|
|
|||
108
kernel/exit.c
108
kernel/exit.c
|
|
@ -72,18 +72,6 @@ static void __unhash_process(struct task_struct *p, bool group_dead)
|
|||
list_del_rcu(&p->tasks);
|
||||
list_del_init(&p->sibling);
|
||||
__this_cpu_dec(process_counts);
|
||||
/*
|
||||
* If we are the last child process in a pid namespace to be
|
||||
* reaped, notify the reaper sleeping zap_pid_ns_processes().
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_PID_NS)) {
|
||||
struct task_struct *parent = p->real_parent;
|
||||
|
||||
if ((task_active_pid_ns(parent)->child_reaper == parent) &&
|
||||
list_empty(&parent->children) &&
|
||||
(parent->flags & PF_EXITING))
|
||||
wake_up_process(parent);
|
||||
}
|
||||
}
|
||||
list_del_rcu(&p->thread_group);
|
||||
}
|
||||
|
|
@ -322,43 +310,6 @@ kill_orphaned_pgrp(struct task_struct *tsk, struct task_struct *parent)
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* reparent_to_kthreadd - Reparent the calling kernel thread to kthreadd
|
||||
*
|
||||
* If a kernel thread is launched as a result of a system call, or if
|
||||
* it ever exits, it should generally reparent itself to kthreadd so it
|
||||
* isn't in the way of other processes and is correctly cleaned up on exit.
|
||||
*
|
||||
* The various task state such as scheduling policy and priority may have
|
||||
* been inherited from a user process, so we reset them to sane values here.
|
||||
*
|
||||
* NOTE that reparent_to_kthreadd() gives the caller full capabilities.
|
||||
*/
|
||||
static void reparent_to_kthreadd(void)
|
||||
{
|
||||
write_lock_irq(&tasklist_lock);
|
||||
|
||||
ptrace_unlink(current);
|
||||
/* Reparent to init */
|
||||
current->real_parent = current->parent = kthreadd_task;
|
||||
list_move_tail(¤t->sibling, ¤t->real_parent->children);
|
||||
|
||||
/* Set the exit signal to SIGCHLD so we signal init on exit */
|
||||
current->exit_signal = SIGCHLD;
|
||||
|
||||
if (task_nice(current) < 0)
|
||||
set_user_nice(current, 0);
|
||||
/* cpus_allowed? */
|
||||
/* rt_priority? */
|
||||
/* signals? */
|
||||
memcpy(current->signal->rlim, init_task.signal->rlim,
|
||||
sizeof(current->signal->rlim));
|
||||
|
||||
atomic_inc(&init_cred.usage);
|
||||
commit_creds(&init_cred);
|
||||
write_unlock_irq(&tasklist_lock);
|
||||
}
|
||||
|
||||
void __set_special_pids(struct pid *pid)
|
||||
{
|
||||
struct task_struct *curr = current->group_leader;
|
||||
|
|
@ -370,13 +321,6 @@ void __set_special_pids(struct pid *pid)
|
|||
change_pid(curr, PIDTYPE_PGID, pid);
|
||||
}
|
||||
|
||||
static void set_special_pids(struct pid *pid)
|
||||
{
|
||||
write_lock_irq(&tasklist_lock);
|
||||
__set_special_pids(pid);
|
||||
write_unlock_irq(&tasklist_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Let kernel threads use this to say that they allow a certain signal.
|
||||
* Must not be used if kthread was cloned with CLONE_SIGHAND.
|
||||
|
|
@ -416,54 +360,6 @@ int disallow_signal(int sig)
|
|||
|
||||
EXPORT_SYMBOL(disallow_signal);
|
||||
|
||||
/*
|
||||
* Put all the gunge required to become a kernel thread without
|
||||
* attached user resources in one place where it belongs.
|
||||
*/
|
||||
|
||||
void daemonize(const char *name, ...)
|
||||
{
|
||||
va_list args;
|
||||
sigset_t blocked;
|
||||
|
||||
va_start(args, name);
|
||||
vsnprintf(current->comm, sizeof(current->comm), name, args);
|
||||
va_end(args);
|
||||
|
||||
/*
|
||||
* If we were started as result of loading a module, close all of the
|
||||
* user space pages. We don't need them, and if we didn't close them
|
||||
* they would be locked into memory.
|
||||
*/
|
||||
exit_mm(current);
|
||||
/*
|
||||
* We don't want to get frozen, in case system-wide hibernation
|
||||
* or suspend transition begins right now.
|
||||
*/
|
||||
current->flags |= (PF_NOFREEZE | PF_KTHREAD);
|
||||
|
||||
if (current->nsproxy != &init_nsproxy) {
|
||||
get_nsproxy(&init_nsproxy);
|
||||
switch_task_namespaces(current, &init_nsproxy);
|
||||
}
|
||||
set_special_pids(&init_struct_pid);
|
||||
proc_clear_tty(current);
|
||||
|
||||
/* Block and flush all signals */
|
||||
sigfillset(&blocked);
|
||||
sigprocmask(SIG_BLOCK, &blocked, NULL);
|
||||
flush_signals(current);
|
||||
|
||||
/* Become as one with the init task */
|
||||
|
||||
daemonize_fs_struct();
|
||||
daemonize_descriptors();
|
||||
|
||||
reparent_to_kthreadd();
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(daemonize);
|
||||
|
||||
#ifdef CONFIG_MM_OWNER
|
||||
/*
|
||||
* A task is exiting. If it owned this mm, find a new owner for the mm.
|
||||
|
|
@ -1186,11 +1082,11 @@ static int wait_task_zombie(struct wait_opts *wo, struct task_struct *p)
|
|||
* as other threads in the parent group can be right
|
||||
* here reaping other children at the same time.
|
||||
*
|
||||
* We use thread_group_times() to get times for the thread
|
||||
* We use thread_group_cputime_adjusted() to get times for the thread
|
||||
* group, which consolidates times for all threads in the
|
||||
* group including the group leader.
|
||||
*/
|
||||
thread_group_times(p, &tgutime, &tgstime);
|
||||
thread_group_cputime_adjusted(p, &tgutime, &tgstime);
|
||||
spin_lock_irq(&p->real_parent->sighand->siglock);
|
||||
psig = p->real_parent->signal;
|
||||
sig = p->signal;
|
||||
|
|
|
|||
161
kernel/fork.c
161
kernel/fork.c
|
|
@ -146,7 +146,7 @@ void __weak arch_release_thread_info(struct thread_info *ti)
|
|||
static struct thread_info *alloc_thread_info_node(struct task_struct *tsk,
|
||||
int node)
|
||||
{
|
||||
struct page *page = alloc_pages_node(node, THREADINFO_GFP,
|
||||
struct page *page = alloc_pages_node(node, THREADINFO_GFP_ACCOUNTED,
|
||||
THREAD_SIZE_ORDER);
|
||||
|
||||
return page ? page_address(page) : NULL;
|
||||
|
|
@ -154,7 +154,7 @@ static struct thread_info *alloc_thread_info_node(struct task_struct *tsk,
|
|||
|
||||
static inline void free_thread_info(struct thread_info *ti)
|
||||
{
|
||||
free_pages((unsigned long)ti, THREAD_SIZE_ORDER);
|
||||
free_memcg_kmem_pages((unsigned long)ti, THREAD_SIZE_ORDER);
|
||||
}
|
||||
# else
|
||||
static struct kmem_cache *thread_info_cache;
|
||||
|
|
@ -352,6 +352,7 @@ static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm)
|
|||
unsigned long charge;
|
||||
struct mempolicy *pol;
|
||||
|
||||
uprobe_start_dup_mmap();
|
||||
down_write(&oldmm->mmap_sem);
|
||||
flush_cache_dup_mm(oldmm);
|
||||
uprobe_dup_mmap(oldmm, mm);
|
||||
|
|
@ -469,6 +470,7 @@ out:
|
|||
up_write(&mm->mmap_sem);
|
||||
flush_tlb_mm(oldmm);
|
||||
up_write(&oldmm->mmap_sem);
|
||||
uprobe_end_dup_mmap();
|
||||
return retval;
|
||||
fail_nomem_anon_vma_fork:
|
||||
mpol_put(pol);
|
||||
|
|
@ -820,6 +822,9 @@ struct mm_struct *dup_mm(struct task_struct *tsk)
|
|||
|
||||
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
|
||||
mm->pmd_huge_pte = NULL;
|
||||
#endif
|
||||
#ifdef CONFIG_NUMA_BALANCING
|
||||
mm->first_nid = NUMA_PTE_SCAN_INIT;
|
||||
#endif
|
||||
if (!mm_init(mm, tsk))
|
||||
goto fail_nomem;
|
||||
|
|
@ -1039,8 +1044,6 @@ static int copy_signal(unsigned long clone_flags, struct task_struct *tsk)
|
|||
atomic_set(&sig->live, 1);
|
||||
atomic_set(&sig->sigcnt, 1);
|
||||
init_waitqueue_head(&sig->wait_chldexit);
|
||||
if (clone_flags & CLONE_NEWPID)
|
||||
sig->flags |= SIGNAL_UNKILLABLE;
|
||||
sig->curr_target = tsk;
|
||||
init_sigpending(&sig->shared_pending);
|
||||
INIT_LIST_HEAD(&sig->posix_timers);
|
||||
|
|
@ -1127,7 +1130,6 @@ static void posix_cpu_timers_init(struct task_struct *tsk)
|
|||
*/
|
||||
static struct task_struct *copy_process(unsigned long clone_flags,
|
||||
unsigned long stack_start,
|
||||
struct pt_regs *regs,
|
||||
unsigned long stack_size,
|
||||
int __user *child_tidptr,
|
||||
struct pid *pid,
|
||||
|
|
@ -1135,7 +1137,6 @@ static struct task_struct *copy_process(unsigned long clone_flags,
|
|||
{
|
||||
int retval;
|
||||
struct task_struct *p;
|
||||
int cgroup_callbacks_done = 0;
|
||||
|
||||
if ((clone_flags & (CLONE_NEWNS|CLONE_FS)) == (CLONE_NEWNS|CLONE_FS))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
|
@ -1165,6 +1166,14 @@ static struct task_struct *copy_process(unsigned long clone_flags,
|
|||
current->signal->flags & SIGNAL_UNKILLABLE)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
/*
|
||||
* If the new process will be in a different pid namespace
|
||||
* don't allow the creation of threads.
|
||||
*/
|
||||
if ((clone_flags & (CLONE_VM|CLONE_NEWPID)) &&
|
||||
(task_active_pid_ns(current) != current->nsproxy->pid_ns))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
retval = security_task_create(clone_flags);
|
||||
if (retval)
|
||||
goto fork_out;
|
||||
|
|
@ -1222,7 +1231,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
|
|||
p->utime = p->stime = p->gtime = 0;
|
||||
p->utimescaled = p->stimescaled = 0;
|
||||
#ifndef CONFIG_VIRT_CPU_ACCOUNTING
|
||||
p->prev_utime = p->prev_stime = 0;
|
||||
p->prev_cputime.utime = p->prev_cputime.stime = 0;
|
||||
#endif
|
||||
#if defined(SPLIT_RSS_COUNTING)
|
||||
memset(&p->rss_stat, 0, sizeof(p->rss_stat));
|
||||
|
|
@ -1320,7 +1329,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
|
|||
retval = copy_io(clone_flags, p);
|
||||
if (retval)
|
||||
goto bad_fork_cleanup_namespaces;
|
||||
retval = copy_thread(clone_flags, stack_start, stack_size, p, regs);
|
||||
retval = copy_thread(clone_flags, stack_start, stack_size, p);
|
||||
if (retval)
|
||||
goto bad_fork_cleanup_io;
|
||||
|
||||
|
|
@ -1393,12 +1402,6 @@ static struct task_struct *copy_process(unsigned long clone_flags,
|
|||
INIT_LIST_HEAD(&p->thread_group);
|
||||
p->task_works = NULL;
|
||||
|
||||
/* Now that the task is set up, run cgroup callbacks if
|
||||
* necessary. We need to run them before the task is visible
|
||||
* on the tasklist. */
|
||||
cgroup_fork_callbacks(p);
|
||||
cgroup_callbacks_done = 1;
|
||||
|
||||
/* Need tasklist lock for parent etc handling! */
|
||||
write_lock_irq(&tasklist_lock);
|
||||
|
||||
|
|
@ -1441,8 +1444,10 @@ static struct task_struct *copy_process(unsigned long clone_flags,
|
|||
ptrace_init_task(p, (clone_flags & CLONE_PTRACE) || trace);
|
||||
|
||||
if (thread_group_leader(p)) {
|
||||
if (is_child_reaper(pid))
|
||||
p->nsproxy->pid_ns->child_reaper = p;
|
||||
if (is_child_reaper(pid)) {
|
||||
ns_of_pid(pid)->child_reaper = p;
|
||||
p->signal->flags |= SIGNAL_UNKILLABLE;
|
||||
}
|
||||
|
||||
p->signal->leader_pid = pid;
|
||||
p->signal->tty = tty_kref_get(current->signal->tty);
|
||||
|
|
@ -1476,8 +1481,6 @@ bad_fork_cleanup_io:
|
|||
if (p->io_context)
|
||||
exit_io_context(p);
|
||||
bad_fork_cleanup_namespaces:
|
||||
if (unlikely(clone_flags & CLONE_NEWPID))
|
||||
pid_ns_release_proc(p->nsproxy->pid_ns);
|
||||
exit_task_namespaces(p);
|
||||
bad_fork_cleanup_mm:
|
||||
if (p->mm)
|
||||
|
|
@ -1503,7 +1506,7 @@ bad_fork_cleanup_cgroup:
|
|||
#endif
|
||||
if (clone_flags & CLONE_THREAD)
|
||||
threadgroup_change_end(current);
|
||||
cgroup_exit(p, cgroup_callbacks_done);
|
||||
cgroup_exit(p, 0);
|
||||
delayacct_tsk_free(p);
|
||||
module_put(task_thread_info(p)->exec_domain->module);
|
||||
bad_fork_cleanup_count:
|
||||
|
|
@ -1515,12 +1518,6 @@ fork_out:
|
|||
return ERR_PTR(retval);
|
||||
}
|
||||
|
||||
noinline struct pt_regs * __cpuinit __attribute__((weak)) idle_regs(struct pt_regs *regs)
|
||||
{
|
||||
memset(regs, 0, sizeof(struct pt_regs));
|
||||
return regs;
|
||||
}
|
||||
|
||||
static inline void init_idle_pids(struct pid_link *links)
|
||||
{
|
||||
enum pid_type type;
|
||||
|
|
@ -1534,10 +1531,7 @@ static inline void init_idle_pids(struct pid_link *links)
|
|||
struct task_struct * __cpuinit fork_idle(int cpu)
|
||||
{
|
||||
struct task_struct *task;
|
||||
struct pt_regs regs;
|
||||
|
||||
task = copy_process(CLONE_VM, 0, idle_regs(®s), 0, NULL,
|
||||
&init_struct_pid, 0);
|
||||
task = copy_process(CLONE_VM, 0, 0, NULL, &init_struct_pid, 0);
|
||||
if (!IS_ERR(task)) {
|
||||
init_idle_pids(task->pids);
|
||||
init_idle(task, cpu);
|
||||
|
|
@ -1554,7 +1548,6 @@ struct task_struct * __cpuinit fork_idle(int cpu)
|
|||
*/
|
||||
long do_fork(unsigned long clone_flags,
|
||||
unsigned long stack_start,
|
||||
struct pt_regs *regs,
|
||||
unsigned long stack_size,
|
||||
int __user *parent_tidptr,
|
||||
int __user *child_tidptr)
|
||||
|
|
@ -1567,15 +1560,9 @@ long do_fork(unsigned long clone_flags,
|
|||
* Do some preliminary argument and permissions checking before we
|
||||
* actually start allocating stuff
|
||||
*/
|
||||
if (clone_flags & CLONE_NEWUSER) {
|
||||
if (clone_flags & CLONE_THREAD)
|
||||
if (clone_flags & (CLONE_NEWUSER | CLONE_NEWPID)) {
|
||||
if (clone_flags & (CLONE_THREAD|CLONE_PARENT))
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1584,7 +1571,7 @@ long do_fork(unsigned long clone_flags,
|
|||
* requested, no event is reported; otherwise, report if the event
|
||||
* for the type of forking is enabled.
|
||||
*/
|
||||
if (!(clone_flags & CLONE_UNTRACED) && likely(user_mode(regs))) {
|
||||
if (!(clone_flags & CLONE_UNTRACED)) {
|
||||
if (clone_flags & CLONE_VFORK)
|
||||
trace = PTRACE_EVENT_VFORK;
|
||||
else if ((clone_flags & CSIGNAL) != SIGCHLD)
|
||||
|
|
@ -1596,7 +1583,7 @@ long do_fork(unsigned long clone_flags,
|
|||
trace = 0;
|
||||
}
|
||||
|
||||
p = copy_process(clone_flags, stack_start, regs, stack_size,
|
||||
p = copy_process(clone_flags, stack_start, stack_size,
|
||||
child_tidptr, NULL, trace);
|
||||
/*
|
||||
* Do this prior waking up the new thread - the thread pointer
|
||||
|
|
@ -1634,15 +1621,56 @@ long do_fork(unsigned long clone_flags,
|
|||
return nr;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_GENERIC_KERNEL_THREAD
|
||||
/*
|
||||
* Create a kernel thread.
|
||||
*/
|
||||
pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
|
||||
{
|
||||
return do_fork(flags|CLONE_VM|CLONE_UNTRACED, (unsigned long)fn, NULL,
|
||||
return do_fork(flags|CLONE_VM|CLONE_UNTRACED, (unsigned long)fn,
|
||||
(unsigned long)arg, NULL, NULL);
|
||||
}
|
||||
|
||||
#ifdef __ARCH_WANT_SYS_FORK
|
||||
SYSCALL_DEFINE0(fork)
|
||||
{
|
||||
#ifdef CONFIG_MMU
|
||||
return do_fork(SIGCHLD, 0, 0, NULL, NULL);
|
||||
#else
|
||||
/* can not support in nommu mode */
|
||||
return(-EINVAL);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __ARCH_WANT_SYS_VFORK
|
||||
SYSCALL_DEFINE0(vfork)
|
||||
{
|
||||
return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, 0,
|
||||
0, NULL, NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __ARCH_WANT_SYS_CLONE
|
||||
#ifdef CONFIG_CLONE_BACKWARDS
|
||||
SYSCALL_DEFINE5(clone, unsigned long, clone_flags, unsigned long, newsp,
|
||||
int __user *, parent_tidptr,
|
||||
int, tls_val,
|
||||
int __user *, child_tidptr)
|
||||
#elif defined(CONFIG_CLONE_BACKWARDS2)
|
||||
SYSCALL_DEFINE5(clone, unsigned long, newsp, unsigned long, clone_flags,
|
||||
int __user *, parent_tidptr,
|
||||
int __user *, child_tidptr,
|
||||
int, tls_val)
|
||||
#else
|
||||
SYSCALL_DEFINE5(clone, unsigned long, clone_flags, unsigned long, newsp,
|
||||
int __user *, parent_tidptr,
|
||||
int __user *, child_tidptr,
|
||||
int, tls_val)
|
||||
#endif
|
||||
{
|
||||
return do_fork(clone_flags, newsp, 0,
|
||||
parent_tidptr, child_tidptr);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef ARCH_MIN_MMSTRUCT_ALIGN
|
||||
|
|
@ -1694,7 +1722,8 @@ static int check_unshare_flags(unsigned long unshare_flags)
|
|||
{
|
||||
if (unshare_flags & ~(CLONE_THREAD|CLONE_FS|CLONE_NEWNS|CLONE_SIGHAND|
|
||||
CLONE_VM|CLONE_FILES|CLONE_SYSVSEM|
|
||||
CLONE_NEWUTS|CLONE_NEWIPC|CLONE_NEWNET))
|
||||
CLONE_NEWUTS|CLONE_NEWIPC|CLONE_NEWNET|
|
||||
CLONE_NEWUSER|CLONE_NEWPID))
|
||||
return -EINVAL;
|
||||
/*
|
||||
* Not implemented, but pretend it works if there is nothing to
|
||||
|
|
@ -1761,19 +1790,40 @@ SYSCALL_DEFINE1(unshare, unsigned long, unshare_flags)
|
|||
{
|
||||
struct fs_struct *fs, *new_fs = NULL;
|
||||
struct files_struct *fd, *new_fd = NULL;
|
||||
struct cred *new_cred = NULL;
|
||||
struct nsproxy *new_nsproxy = NULL;
|
||||
int do_sysvsem = 0;
|
||||
int err;
|
||||
|
||||
err = check_unshare_flags(unshare_flags);
|
||||
if (err)
|
||||
goto bad_unshare_out;
|
||||
|
||||
/*
|
||||
* If unsharing a user namespace must also unshare the thread.
|
||||
*/
|
||||
if (unshare_flags & CLONE_NEWUSER)
|
||||
unshare_flags |= CLONE_THREAD;
|
||||
/*
|
||||
* If unsharing a pid namespace must also unshare the thread.
|
||||
*/
|
||||
if (unshare_flags & CLONE_NEWPID)
|
||||
unshare_flags |= CLONE_THREAD;
|
||||
/*
|
||||
* If unsharing a thread from a thread group, must also unshare vm.
|
||||
*/
|
||||
if (unshare_flags & CLONE_THREAD)
|
||||
unshare_flags |= CLONE_VM;
|
||||
/*
|
||||
* If unsharing vm, must also unshare signal handlers.
|
||||
*/
|
||||
if (unshare_flags & CLONE_VM)
|
||||
unshare_flags |= CLONE_SIGHAND;
|
||||
/*
|
||||
* If unsharing namespace, must also unshare filesystem information.
|
||||
*/
|
||||
if (unshare_flags & CLONE_NEWNS)
|
||||
unshare_flags |= CLONE_FS;
|
||||
|
||||
err = check_unshare_flags(unshare_flags);
|
||||
if (err)
|
||||
goto bad_unshare_out;
|
||||
/*
|
||||
* CLONE_NEWIPC must also detach from the undolist: after switching
|
||||
* to a new ipc namespace, the semaphore arrays from the old
|
||||
|
|
@ -1787,11 +1837,15 @@ SYSCALL_DEFINE1(unshare, unsigned long, unshare_flags)
|
|||
err = unshare_fd(unshare_flags, &new_fd);
|
||||
if (err)
|
||||
goto bad_unshare_cleanup_fs;
|
||||
err = unshare_nsproxy_namespaces(unshare_flags, &new_nsproxy, new_fs);
|
||||
err = unshare_userns(unshare_flags, &new_cred);
|
||||
if (err)
|
||||
goto bad_unshare_cleanup_fd;
|
||||
err = unshare_nsproxy_namespaces(unshare_flags, &new_nsproxy,
|
||||
new_cred, new_fs);
|
||||
if (err)
|
||||
goto bad_unshare_cleanup_cred;
|
||||
|
||||
if (new_fs || new_fd || do_sysvsem || new_nsproxy) {
|
||||
if (new_fs || new_fd || do_sysvsem || new_cred || new_nsproxy) {
|
||||
if (do_sysvsem) {
|
||||
/*
|
||||
* CLONE_SYSVSEM is equivalent to sys_exit().
|
||||
|
|
@ -1824,11 +1878,20 @@ SYSCALL_DEFINE1(unshare, unsigned long, unshare_flags)
|
|||
}
|
||||
|
||||
task_unlock(current);
|
||||
|
||||
if (new_cred) {
|
||||
/* Install the new user namespace */
|
||||
commit_creds(new_cred);
|
||||
new_cred = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (new_nsproxy)
|
||||
put_nsproxy(new_nsproxy);
|
||||
|
||||
bad_unshare_cleanup_cred:
|
||||
if (new_cred)
|
||||
put_cred(new_cred);
|
||||
bad_unshare_cleanup_fd:
|
||||
if (new_fd)
|
||||
put_files_struct(new_fd);
|
||||
|
|
|
|||
|
|
@ -116,17 +116,10 @@ bool freeze_task(struct task_struct *p)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!(p->flags & PF_KTHREAD)) {
|
||||
if (!(p->flags & PF_KTHREAD))
|
||||
fake_signal_wake_up(p);
|
||||
/*
|
||||
* fake_signal_wake_up() goes through p's scheduler
|
||||
* lock and guarantees that TASK_STOPPED/TRACED ->
|
||||
* TASK_RUNNING transition can't race with task state
|
||||
* testing in try_to_freeze_tasks().
|
||||
*/
|
||||
} else {
|
||||
else
|
||||
wake_up_state(p, TASK_INTERRUPTIBLE);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&freezer_lock, flags);
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -716,7 +716,7 @@ static int futex_lock_pi_atomic(u32 __user *uaddr, struct futex_hash_bucket *hb,
|
|||
struct futex_pi_state **ps,
|
||||
struct task_struct *task, int set_waiters)
|
||||
{
|
||||
int lock_taken, ret, ownerdied = 0;
|
||||
int lock_taken, ret, force_take = 0;
|
||||
u32 uval, newval, curval, vpid = task_pid_vnr(task);
|
||||
|
||||
retry:
|
||||
|
|
@ -755,17 +755,15 @@ retry:
|
|||
newval = curval | FUTEX_WAITERS;
|
||||
|
||||
/*
|
||||
* There are two cases, where a futex might have no owner (the
|
||||
* owner TID is 0): OWNER_DIED. We take over the futex in this
|
||||
* case. We also do an unconditional take over, when the owner
|
||||
* of the futex died.
|
||||
*
|
||||
* This is safe as we are protected by the hash bucket lock !
|
||||
* Should we force take the futex? See below.
|
||||
*/
|
||||
if (unlikely(ownerdied || !(curval & FUTEX_TID_MASK))) {
|
||||
/* Keep the OWNER_DIED bit */
|
||||
if (unlikely(force_take)) {
|
||||
/*
|
||||
* Keep the OWNER_DIED and the WAITERS bit and set the
|
||||
* new TID value.
|
||||
*/
|
||||
newval = (curval & ~FUTEX_TID_MASK) | vpid;
|
||||
ownerdied = 0;
|
||||
force_take = 0;
|
||||
lock_taken = 1;
|
||||
}
|
||||
|
||||
|
|
@ -775,7 +773,7 @@ retry:
|
|||
goto retry;
|
||||
|
||||
/*
|
||||
* We took the lock due to owner died take over.
|
||||
* We took the lock due to forced take over.
|
||||
*/
|
||||
if (unlikely(lock_taken))
|
||||
return 1;
|
||||
|
|
@ -790,20 +788,25 @@ retry:
|
|||
switch (ret) {
|
||||
case -ESRCH:
|
||||
/*
|
||||
* No owner found for this futex. Check if the
|
||||
* OWNER_DIED bit is set to figure out whether
|
||||
* this is a robust futex or not.
|
||||
* We failed to find an owner for this
|
||||
* futex. So we have no pi_state to block
|
||||
* on. This can happen in two cases:
|
||||
*
|
||||
* 1) The owner died
|
||||
* 2) A stale FUTEX_WAITERS bit
|
||||
*
|
||||
* Re-read the futex value.
|
||||
*/
|
||||
if (get_futex_value_locked(&curval, uaddr))
|
||||
return -EFAULT;
|
||||
|
||||
/*
|
||||
* We simply start over in case of a robust
|
||||
* futex. The code above will take the futex
|
||||
* and return happy.
|
||||
* If the owner died or we have a stale
|
||||
* WAITERS bit the owner TID in the user space
|
||||
* futex is 0.
|
||||
*/
|
||||
if (curval & FUTEX_OWNER_DIED) {
|
||||
ownerdied = 1;
|
||||
if (!(curval & FUTEX_TID_MASK)) {
|
||||
force_take = 1;
|
||||
goto retry;
|
||||
}
|
||||
default:
|
||||
|
|
@ -840,6 +843,9 @@ static void wake_futex(struct futex_q *q)
|
|||
{
|
||||
struct task_struct *p = q->task;
|
||||
|
||||
if (WARN(q->pi_state || q->rt_waiter, "refusing to wake PI futex\n"))
|
||||
return;
|
||||
|
||||
/*
|
||||
* We set q->lock_ptr = NULL _before_ we wake up the task. If
|
||||
* a non-futex wake up happens on another CPU then the task
|
||||
|
|
@ -1075,6 +1081,10 @@ retry_private:
|
|||
|
||||
plist_for_each_entry_safe(this, next, head, list) {
|
||||
if (match_futex (&this->key, &key1)) {
|
||||
if (this->pi_state || this->rt_waiter) {
|
||||
ret = -EINVAL;
|
||||
goto out_unlock;
|
||||
}
|
||||
wake_futex(this);
|
||||
if (++ret >= nr_wake)
|
||||
break;
|
||||
|
|
@ -1087,6 +1097,10 @@ retry_private:
|
|||
op_ret = 0;
|
||||
plist_for_each_entry_safe(this, next, head, list) {
|
||||
if (match_futex (&this->key, &key2)) {
|
||||
if (this->pi_state || this->rt_waiter) {
|
||||
ret = -EINVAL;
|
||||
goto out_unlock;
|
||||
}
|
||||
wake_futex(this);
|
||||
if (++op_ret >= nr_wake2)
|
||||
break;
|
||||
|
|
@ -1095,6 +1109,7 @@ retry_private:
|
|||
ret += op_ret;
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
double_unlock_hb(hb1, hb2);
|
||||
out_put_keys:
|
||||
put_futex_key(&key2);
|
||||
|
|
@ -1384,9 +1399,13 @@ retry_private:
|
|||
/*
|
||||
* FUTEX_WAIT_REQEUE_PI and FUTEX_CMP_REQUEUE_PI should always
|
||||
* be paired with each other and no other futex ops.
|
||||
*
|
||||
* We should never be requeueing a futex_q with a pi_state,
|
||||
* which is awaiting a futex_unlock_pi().
|
||||
*/
|
||||
if ((requeue_pi && !this->rt_waiter) ||
|
||||
(!requeue_pi && this->rt_waiter)) {
|
||||
(!requeue_pi && this->rt_waiter) ||
|
||||
this->pi_state) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -177,8 +177,8 @@ struct irq_domain *irq_domain_add_simple(struct device_node *of_node,
|
|||
irq_base = irq_alloc_descs(first_irq, first_irq, size,
|
||||
of_node_to_nid(of_node));
|
||||
if (irq_base < 0) {
|
||||
WARN(1, "Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n",
|
||||
first_irq);
|
||||
pr_info("Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n",
|
||||
first_irq);
|
||||
irq_base = first_irq;
|
||||
}
|
||||
} else
|
||||
|
|
|
|||
|
|
@ -818,7 +818,7 @@ static void irq_thread_dtor(struct callback_head *unused)
|
|||
action = kthread_data(tsk);
|
||||
|
||||
pr_err("exiting task \"%s\" (%d) is an active IRQ thread (irq %d)\n",
|
||||
tsk->comm ? tsk->comm : "", tsk->pid, action->irq);
|
||||
tsk->comm, tsk->pid, action->irq);
|
||||
|
||||
|
||||
desc = irq_to_desc(action->irq);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include <linux/string.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/cache.h>
|
||||
|
|
|
|||
|
|
@ -219,9 +219,9 @@ static int ____call_usermodehelper(void *data)
|
|||
|
||||
commit_creds(new);
|
||||
|
||||
retval = kernel_execve(sub_info->path,
|
||||
(const char *const *)sub_info->argv,
|
||||
(const char *const *)sub_info->envp);
|
||||
retval = do_execve(sub_info->path,
|
||||
(const char __user *const __user *)sub_info->argv,
|
||||
(const char __user *const __user *)sub_info->envp);
|
||||
if (!retval)
|
||||
return 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ static struct kobj_attribute _name##_attr = __ATTR_RO(_name)
|
|||
static struct kobj_attribute _name##_attr = \
|
||||
__ATTR(_name, 0644, _name##_show, _name##_store)
|
||||
|
||||
#if defined(CONFIG_HOTPLUG)
|
||||
/* current uevent sequence number */
|
||||
static ssize_t uevent_seqnum_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf)
|
||||
|
|
@ -54,7 +53,7 @@ static ssize_t uevent_helper_store(struct kobject *kobj,
|
|||
return count;
|
||||
}
|
||||
KERNEL_ATTR_RW(uevent_helper);
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef CONFIG_PROFILING
|
||||
static ssize_t profiling_show(struct kobject *kobj,
|
||||
|
|
@ -141,6 +140,23 @@ static ssize_t fscaps_show(struct kobject *kobj,
|
|||
}
|
||||
KERNEL_ATTR_RO(fscaps);
|
||||
|
||||
int rcu_expedited;
|
||||
static ssize_t rcu_expedited_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%d\n", rcu_expedited);
|
||||
}
|
||||
static ssize_t rcu_expedited_store(struct kobject *kobj,
|
||||
struct kobj_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
if (kstrtoint(buf, 0, &rcu_expedited))
|
||||
return -EINVAL;
|
||||
|
||||
return count;
|
||||
}
|
||||
KERNEL_ATTR_RW(rcu_expedited);
|
||||
|
||||
/*
|
||||
* Make /sys/kernel/notes give the raw contents of our kernel .notes section.
|
||||
*/
|
||||
|
|
@ -169,10 +185,8 @@ EXPORT_SYMBOL_GPL(kernel_kobj);
|
|||
|
||||
static struct attribute * kernel_attrs[] = {
|
||||
&fscaps_attr.attr,
|
||||
#if defined(CONFIG_HOTPLUG)
|
||||
&uevent_seqnum_attr.attr,
|
||||
&uevent_helper_attr.attr,
|
||||
#endif
|
||||
#ifdef CONFIG_PROFILING
|
||||
&profiling_attr.attr,
|
||||
#endif
|
||||
|
|
@ -182,6 +196,7 @@ static struct attribute * kernel_attrs[] = {
|
|||
&kexec_crash_size_attr.attr,
|
||||
&vmcoreinfo_attr.attr,
|
||||
#endif
|
||||
&rcu_expedited_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -428,7 +428,7 @@ int kthreadd(void *unused)
|
|||
set_task_comm(tsk, "kthreadd");
|
||||
ignore_signals(tsk);
|
||||
set_cpus_allowed_ptr(tsk, cpu_all_mask);
|
||||
set_mems_allowed(node_states[N_HIGH_MEMORY]);
|
||||
set_mems_allowed(node_states[N_MEMORY]);
|
||||
|
||||
current->flags |= PF_NOFREEZE;
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ static void l_stop(struct seq_file *m, void *v)
|
|||
|
||||
static void print_name(struct seq_file *m, struct lock_class *class)
|
||||
{
|
||||
char str[128];
|
||||
char str[KSYM_NAME_LEN];
|
||||
const char *name = class->name;
|
||||
|
||||
if (!name) {
|
||||
|
|
|
|||
19
kernel/modsign_certificate.S
Normal file
19
kernel/modsign_certificate.S
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/* SYMBOL_PREFIX defined on commandline from CONFIG_SYMBOL_PREFIX */
|
||||
#ifndef SYMBOL_PREFIX
|
||||
#define ASM_SYMBOL(sym) sym
|
||||
#else
|
||||
#define PASTE2(x,y) x##y
|
||||
#define PASTE(x,y) PASTE2(x,y)
|
||||
#define ASM_SYMBOL(sym) PASTE(SYMBOL_PREFIX, sym)
|
||||
#endif
|
||||
|
||||
#define GLOBAL(name) \
|
||||
.globl ASM_SYMBOL(name); \
|
||||
ASM_SYMBOL(name):
|
||||
|
||||
.section ".init.data","aw"
|
||||
|
||||
GLOBAL(modsign_certificate_list)
|
||||
.incbin "signing_key.x509"
|
||||
.incbin "extra_certificates"
|
||||
GLOBAL(modsign_certificate_list_end)
|
||||
|
|
@ -20,12 +20,6 @@ struct key *modsign_keyring;
|
|||
|
||||
extern __initdata const u8 modsign_certificate_list[];
|
||||
extern __initdata const u8 modsign_certificate_list_end[];
|
||||
asm(".section .init.data,\"aw\"\n"
|
||||
"modsign_certificate_list:\n"
|
||||
".incbin \"signing_key.x509\"\n"
|
||||
".incbin \"extra_certificates\"\n"
|
||||
"modsign_certificate_list_end:"
|
||||
);
|
||||
|
||||
/*
|
||||
* We need to make sure ccache doesn't cache the .o file as it doesn't notice
|
||||
|
|
@ -40,18 +34,15 @@ static __init int module_verify_init(void)
|
|||
{
|
||||
pr_notice("Initialise module verification\n");
|
||||
|
||||
modsign_keyring = key_alloc(&key_type_keyring, ".module_sign",
|
||||
KUIDT_INIT(0), KGIDT_INIT(0),
|
||||
current_cred(),
|
||||
(KEY_POS_ALL & ~KEY_POS_SETATTR) |
|
||||
KEY_USR_VIEW | KEY_USR_READ,
|
||||
KEY_ALLOC_NOT_IN_QUOTA);
|
||||
modsign_keyring = keyring_alloc(".module_sign",
|
||||
KUIDT_INIT(0), KGIDT_INIT(0),
|
||||
current_cred(),
|
||||
((KEY_POS_ALL & ~KEY_POS_SETATTR) |
|
||||
KEY_USR_VIEW | KEY_USR_READ),
|
||||
KEY_ALLOC_NOT_IN_QUOTA, NULL);
|
||||
if (IS_ERR(modsign_keyring))
|
||||
panic("Can't allocate module signing keyring\n");
|
||||
|
||||
if (key_instantiate_and_link(modsign_keyring, NULL, 0, NULL, NULL) < 0)
|
||||
panic("Can't instantiate module signing keyring\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
614
kernel/module.c
614
kernel/module.c
|
|
@ -21,6 +21,7 @@
|
|||
#include <linux/ftrace_event.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kallsyms.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/kernel.h>
|
||||
|
|
@ -28,6 +29,7 @@
|
|||
#include <linux/vmalloc.h>
|
||||
#include <linux/elf.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/fcntl.h>
|
||||
|
|
@ -59,6 +61,7 @@
|
|||
#include <linux/pfn.h>
|
||||
#include <linux/bsearch.h>
|
||||
#include <linux/fips.h>
|
||||
#include <uapi/linux/module.h>
|
||||
#include "module-internal.h"
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
|
|
@ -372,9 +375,6 @@ static bool check_symbol(const struct symsearch *syms,
|
|||
printk(KERN_WARNING "Symbol %s is being used "
|
||||
"by a non-GPL module, which will not "
|
||||
"be allowed in the future\n", fsa->name);
|
||||
printk(KERN_WARNING "Please see the file "
|
||||
"Documentation/feature-removal-schedule.txt "
|
||||
"in the kernel source tree for more details.\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2282,7 +2282,7 @@ static void layout_symtab(struct module *mod, struct load_info *info)
|
|||
Elf_Shdr *symsect = info->sechdrs + info->index.sym;
|
||||
Elf_Shdr *strsect = info->sechdrs + info->index.str;
|
||||
const Elf_Sym *src;
|
||||
unsigned int i, nsrc, ndst, strtab_size;
|
||||
unsigned int i, nsrc, ndst, strtab_size = 0;
|
||||
|
||||
/* Put symbol section at end of init part of module. */
|
||||
symsect->sh_flags |= SHF_ALLOC;
|
||||
|
|
@ -2294,11 +2294,13 @@ static void layout_symtab(struct module *mod, struct load_info *info)
|
|||
nsrc = symsect->sh_size / sizeof(*src);
|
||||
|
||||
/* Compute total space required for the core symbols' strtab. */
|
||||
for (ndst = i = strtab_size = 1; i < nsrc; ++i, ++src)
|
||||
if (is_core_symbol(src, info->sechdrs, info->hdr->e_shnum)) {
|
||||
strtab_size += strlen(&info->strtab[src->st_name]) + 1;
|
||||
for (ndst = i = 0; i < nsrc; i++) {
|
||||
if (i == 0 ||
|
||||
is_core_symbol(src+i, info->sechdrs, info->hdr->e_shnum)) {
|
||||
strtab_size += strlen(&info->strtab[src[i].st_name])+1;
|
||||
ndst++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Append room for core symbols at end of core part. */
|
||||
info->symoffs = ALIGN(mod->core_size, symsect->sh_addralign ?: 1);
|
||||
|
|
@ -2332,15 +2334,14 @@ static void add_kallsyms(struct module *mod, const struct load_info *info)
|
|||
mod->core_symtab = dst = mod->module_core + info->symoffs;
|
||||
mod->core_strtab = s = mod->module_core + info->stroffs;
|
||||
src = mod->symtab;
|
||||
*dst = *src;
|
||||
*s++ = 0;
|
||||
for (ndst = i = 1; i < mod->num_symtab; ++i, ++src) {
|
||||
if (!is_core_symbol(src, info->sechdrs, info->hdr->e_shnum))
|
||||
continue;
|
||||
|
||||
dst[ndst] = *src;
|
||||
dst[ndst++].st_name = s - mod->core_strtab;
|
||||
s += strlcpy(s, &mod->strtab[src->st_name], KSYM_NAME_LEN) + 1;
|
||||
for (ndst = i = 0; i < mod->num_symtab; i++) {
|
||||
if (i == 0 ||
|
||||
is_core_symbol(src+i, info->sechdrs, info->hdr->e_shnum)) {
|
||||
dst[ndst] = src[i];
|
||||
dst[ndst++].st_name = s - mod->core_strtab;
|
||||
s += strlcpy(s, &mod->strtab[src[i].st_name],
|
||||
KSYM_NAME_LEN) + 1;
|
||||
}
|
||||
}
|
||||
mod->core_num_syms = ndst;
|
||||
}
|
||||
|
|
@ -2373,7 +2374,7 @@ static void dynamic_debug_remove(struct _ddebug *debug)
|
|||
|
||||
void * __weak module_alloc(unsigned long size)
|
||||
{
|
||||
return size == 0 ? NULL : vmalloc_exec(size);
|
||||
return vmalloc_exec(size);
|
||||
}
|
||||
|
||||
static void *module_alloc_update_bounds(unsigned long size)
|
||||
|
|
@ -2420,18 +2421,17 @@ static inline void kmemleak_load_module(const struct module *mod,
|
|||
#endif
|
||||
|
||||
#ifdef CONFIG_MODULE_SIG
|
||||
static int module_sig_check(struct load_info *info,
|
||||
const void *mod, unsigned long *_len)
|
||||
static int module_sig_check(struct load_info *info)
|
||||
{
|
||||
int err = -ENOKEY;
|
||||
unsigned long markerlen = sizeof(MODULE_SIG_STRING) - 1;
|
||||
unsigned long len = *_len;
|
||||
const unsigned long markerlen = sizeof(MODULE_SIG_STRING) - 1;
|
||||
const void *mod = info->hdr;
|
||||
|
||||
if (len > markerlen &&
|
||||
memcmp(mod + len - markerlen, MODULE_SIG_STRING, markerlen) == 0) {
|
||||
if (info->len > markerlen &&
|
||||
memcmp(mod + info->len - markerlen, MODULE_SIG_STRING, markerlen) == 0) {
|
||||
/* We truncate the module to discard the signature */
|
||||
*_len -= markerlen;
|
||||
err = mod_verify_sig(mod, _len);
|
||||
info->len -= markerlen;
|
||||
err = mod_verify_sig(mod, &info->len);
|
||||
}
|
||||
|
||||
if (!err) {
|
||||
|
|
@ -2449,59 +2449,107 @@ static int module_sig_check(struct load_info *info,
|
|||
return err;
|
||||
}
|
||||
#else /* !CONFIG_MODULE_SIG */
|
||||
static int module_sig_check(struct load_info *info,
|
||||
void *mod, unsigned long *len)
|
||||
static int module_sig_check(struct load_info *info)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* !CONFIG_MODULE_SIG */
|
||||
|
||||
/* Sets info->hdr, info->len and info->sig_ok. */
|
||||
static int copy_and_check(struct load_info *info,
|
||||
const void __user *umod, unsigned long len,
|
||||
const char __user *uargs)
|
||||
/* Sanity checks against invalid binaries, wrong arch, weird elf version. */
|
||||
static int elf_header_check(struct load_info *info)
|
||||
{
|
||||
int err;
|
||||
Elf_Ehdr *hdr;
|
||||
|
||||
if (len < sizeof(*hdr))
|
||||
if (info->len < sizeof(*(info->hdr)))
|
||||
return -ENOEXEC;
|
||||
|
||||
if (memcmp(info->hdr->e_ident, ELFMAG, SELFMAG) != 0
|
||||
|| info->hdr->e_type != ET_REL
|
||||
|| !elf_check_arch(info->hdr)
|
||||
|| info->hdr->e_shentsize != sizeof(Elf_Shdr))
|
||||
return -ENOEXEC;
|
||||
|
||||
if (info->hdr->e_shoff >= info->len
|
||||
|| (info->hdr->e_shnum * sizeof(Elf_Shdr) >
|
||||
info->len - info->hdr->e_shoff))
|
||||
return -ENOEXEC;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Sets info->hdr and info->len. */
|
||||
static int copy_module_from_user(const void __user *umod, unsigned long len,
|
||||
struct load_info *info)
|
||||
{
|
||||
int err;
|
||||
|
||||
info->len = len;
|
||||
if (info->len < sizeof(*(info->hdr)))
|
||||
return -ENOEXEC;
|
||||
|
||||
err = security_kernel_module_from_file(NULL);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Suck in entire file: we'll want most of it. */
|
||||
if ((hdr = vmalloc(len)) == NULL)
|
||||
info->hdr = vmalloc(info->len);
|
||||
if (!info->hdr)
|
||||
return -ENOMEM;
|
||||
|
||||
if (copy_from_user(hdr, umod, len) != 0) {
|
||||
err = -EFAULT;
|
||||
goto free_hdr;
|
||||
if (copy_from_user(info->hdr, umod, info->len) != 0) {
|
||||
vfree(info->hdr);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
err = module_sig_check(info, hdr, &len);
|
||||
if (err)
|
||||
goto free_hdr;
|
||||
|
||||
/* Sanity checks against insmoding binaries or wrong arch,
|
||||
weird elf version */
|
||||
if (memcmp(hdr->e_ident, ELFMAG, SELFMAG) != 0
|
||||
|| hdr->e_type != ET_REL
|
||||
|| !elf_check_arch(hdr)
|
||||
|| hdr->e_shentsize != sizeof(Elf_Shdr)) {
|
||||
err = -ENOEXEC;
|
||||
goto free_hdr;
|
||||
}
|
||||
|
||||
if (hdr->e_shoff >= len ||
|
||||
hdr->e_shnum * sizeof(Elf_Shdr) > len - hdr->e_shoff) {
|
||||
err = -ENOEXEC;
|
||||
goto free_hdr;
|
||||
}
|
||||
|
||||
info->hdr = hdr;
|
||||
info->len = len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
free_hdr:
|
||||
vfree(hdr);
|
||||
/* Sets info->hdr and info->len. */
|
||||
static int copy_module_from_fd(int fd, struct load_info *info)
|
||||
{
|
||||
struct file *file;
|
||||
int err;
|
||||
struct kstat stat;
|
||||
loff_t pos;
|
||||
ssize_t bytes = 0;
|
||||
|
||||
file = fget(fd);
|
||||
if (!file)
|
||||
return -ENOEXEC;
|
||||
|
||||
err = security_kernel_module_from_file(file);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = vfs_getattr(file->f_vfsmnt, file->f_dentry, &stat);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
if (stat.size > INT_MAX) {
|
||||
err = -EFBIG;
|
||||
goto out;
|
||||
}
|
||||
info->hdr = vmalloc(stat.size);
|
||||
if (!info->hdr) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
pos = 0;
|
||||
while (pos < stat.size) {
|
||||
bytes = kernel_read(file, pos, (char *)(info->hdr) + pos,
|
||||
stat.size - pos);
|
||||
if (bytes < 0) {
|
||||
vfree(info->hdr);
|
||||
err = bytes;
|
||||
goto out;
|
||||
}
|
||||
if (bytes == 0)
|
||||
break;
|
||||
pos += bytes;
|
||||
}
|
||||
info->len = pos;
|
||||
|
||||
out:
|
||||
fput(file);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
@ -2510,7 +2558,7 @@ static void free_copy(struct load_info *info)
|
|||
vfree(info->hdr);
|
||||
}
|
||||
|
||||
static int rewrite_section_headers(struct load_info *info)
|
||||
static int rewrite_section_headers(struct load_info *info, int flags)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
|
|
@ -2538,7 +2586,10 @@ static int rewrite_section_headers(struct load_info *info)
|
|||
}
|
||||
|
||||
/* Track but don't keep modinfo and version sections. */
|
||||
info->index.vers = find_sec(info, "__versions");
|
||||
if (flags & MODULE_INIT_IGNORE_MODVERSIONS)
|
||||
info->index.vers = 0; /* Pretend no __versions section! */
|
||||
else
|
||||
info->index.vers = find_sec(info, "__versions");
|
||||
info->index.info = find_sec(info, ".modinfo");
|
||||
info->sechdrs[info->index.info].sh_flags &= ~(unsigned long)SHF_ALLOC;
|
||||
info->sechdrs[info->index.vers].sh_flags &= ~(unsigned long)SHF_ALLOC;
|
||||
|
|
@ -2553,7 +2604,7 @@ static int rewrite_section_headers(struct load_info *info)
|
|||
* Return the temporary module pointer (we'll replace it with the final
|
||||
* one when we move the module sections around).
|
||||
*/
|
||||
static struct module *setup_load_info(struct load_info *info)
|
||||
static struct module *setup_load_info(struct load_info *info, int flags)
|
||||
{
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
|
@ -2564,7 +2615,7 @@ static struct module *setup_load_info(struct load_info *info)
|
|||
info->secstrings = (void *)info->hdr
|
||||
+ info->sechdrs[info->hdr->e_shstrndx].sh_offset;
|
||||
|
||||
err = rewrite_section_headers(info);
|
||||
err = rewrite_section_headers(info, flags);
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
|
||||
|
|
@ -2602,11 +2653,14 @@ static struct module *setup_load_info(struct load_info *info)
|
|||
return mod;
|
||||
}
|
||||
|
||||
static int check_modinfo(struct module *mod, struct load_info *info)
|
||||
static int check_modinfo(struct module *mod, struct load_info *info, int flags)
|
||||
{
|
||||
const char *modmagic = get_modinfo(info, "vermagic");
|
||||
int err;
|
||||
|
||||
if (flags & MODULE_INIT_IGNORE_VERMAGIC)
|
||||
modmagic = NULL;
|
||||
|
||||
/* This is allowed: modprobe --force will invalidate it. */
|
||||
if (!modmagic) {
|
||||
err = try_to_force_load(mod, "bad vermagic");
|
||||
|
|
@ -2736,20 +2790,23 @@ static int move_module(struct module *mod, struct load_info *info)
|
|||
memset(ptr, 0, mod->core_size);
|
||||
mod->module_core = ptr;
|
||||
|
||||
ptr = module_alloc_update_bounds(mod->init_size);
|
||||
/*
|
||||
* The pointer to this block is stored in the module structure
|
||||
* which is inside the block. This block doesn't need to be
|
||||
* scanned as it contains data and code that will be freed
|
||||
* after the module is initialized.
|
||||
*/
|
||||
kmemleak_ignore(ptr);
|
||||
if (!ptr && mod->init_size) {
|
||||
module_free(mod, mod->module_core);
|
||||
return -ENOMEM;
|
||||
}
|
||||
memset(ptr, 0, mod->init_size);
|
||||
mod->module_init = ptr;
|
||||
if (mod->init_size) {
|
||||
ptr = module_alloc_update_bounds(mod->init_size);
|
||||
/*
|
||||
* The pointer to this block is stored in the module structure
|
||||
* which is inside the block. This block doesn't need to be
|
||||
* scanned as it contains data and code that will be freed
|
||||
* after the module is initialized.
|
||||
*/
|
||||
kmemleak_ignore(ptr);
|
||||
if (!ptr) {
|
||||
module_free(mod, mod->module_core);
|
||||
return -ENOMEM;
|
||||
}
|
||||
memset(ptr, 0, mod->init_size);
|
||||
mod->module_init = ptr;
|
||||
} else
|
||||
mod->module_init = NULL;
|
||||
|
||||
/* Transfer each section which specifies SHF_ALLOC */
|
||||
pr_debug("final section addresses:\n");
|
||||
|
|
@ -2842,18 +2899,18 @@ int __weak module_frob_arch_sections(Elf_Ehdr *hdr,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct module *layout_and_allocate(struct load_info *info)
|
||||
static struct module *layout_and_allocate(struct load_info *info, int flags)
|
||||
{
|
||||
/* Module within temporary copy. */
|
||||
struct module *mod;
|
||||
Elf_Shdr *pcpusec;
|
||||
int err;
|
||||
|
||||
mod = setup_load_info(info);
|
||||
mod = setup_load_info(info, flags);
|
||||
if (IS_ERR(mod))
|
||||
return mod;
|
||||
|
||||
err = check_modinfo(mod, info);
|
||||
err = check_modinfo(mod, info, flags);
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
|
||||
|
|
@ -2940,156 +2997,6 @@ static bool finished_loading(const char *name)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* Allocate and load the module: note that size of section 0 is always
|
||||
zero, and we rely on this for optional sections. */
|
||||
static struct module *load_module(void __user *umod,
|
||||
unsigned long len,
|
||||
const char __user *uargs)
|
||||
{
|
||||
struct load_info info = { NULL, };
|
||||
struct module *mod, *old;
|
||||
long err;
|
||||
|
||||
pr_debug("load_module: umod=%p, len=%lu, uargs=%p\n",
|
||||
umod, len, uargs);
|
||||
|
||||
/* Copy in the blobs from userspace, check they are vaguely sane. */
|
||||
err = copy_and_check(&info, umod, len, uargs);
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
|
||||
/* Figure out module layout, and allocate all the memory. */
|
||||
mod = layout_and_allocate(&info);
|
||||
if (IS_ERR(mod)) {
|
||||
err = PTR_ERR(mod);
|
||||
goto free_copy;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MODULE_SIG
|
||||
mod->sig_ok = info.sig_ok;
|
||||
if (!mod->sig_ok)
|
||||
add_taint_module(mod, TAINT_FORCED_MODULE);
|
||||
#endif
|
||||
|
||||
/* Now module is in final location, initialize linked lists, etc. */
|
||||
err = module_unload_init(mod);
|
||||
if (err)
|
||||
goto free_module;
|
||||
|
||||
/* Now we've got everything in the final locations, we can
|
||||
* find optional sections. */
|
||||
find_module_sections(mod, &info);
|
||||
|
||||
err = check_module_license_and_versions(mod);
|
||||
if (err)
|
||||
goto free_unload;
|
||||
|
||||
/* Set up MODINFO_ATTR fields */
|
||||
setup_modinfo(mod, &info);
|
||||
|
||||
/* Fix up syms, so that st_value is a pointer to location. */
|
||||
err = simplify_symbols(mod, &info);
|
||||
if (err < 0)
|
||||
goto free_modinfo;
|
||||
|
||||
err = apply_relocations(mod, &info);
|
||||
if (err < 0)
|
||||
goto free_modinfo;
|
||||
|
||||
err = post_relocation(mod, &info);
|
||||
if (err < 0)
|
||||
goto free_modinfo;
|
||||
|
||||
flush_module_icache(mod);
|
||||
|
||||
/* Now copy in args */
|
||||
mod->args = strndup_user(uargs, ~0UL >> 1);
|
||||
if (IS_ERR(mod->args)) {
|
||||
err = PTR_ERR(mod->args);
|
||||
goto free_arch_cleanup;
|
||||
}
|
||||
|
||||
/* Mark state as coming so strong_try_module_get() ignores us. */
|
||||
mod->state = MODULE_STATE_COMING;
|
||||
|
||||
/* Now sew it into the lists so we can get lockdep and oops
|
||||
* info during argument parsing. No one should access us, since
|
||||
* strong_try_module_get() will fail.
|
||||
* lockdep/oops can run asynchronous, so use the RCU list insertion
|
||||
* function to insert in a way safe to concurrent readers.
|
||||
* The mutex protects against concurrent writers.
|
||||
*/
|
||||
again:
|
||||
mutex_lock(&module_mutex);
|
||||
if ((old = find_module(mod->name)) != NULL) {
|
||||
if (old->state == MODULE_STATE_COMING) {
|
||||
/* Wait in case it fails to load. */
|
||||
mutex_unlock(&module_mutex);
|
||||
err = wait_event_interruptible(module_wq,
|
||||
finished_loading(mod->name));
|
||||
if (err)
|
||||
goto free_arch_cleanup;
|
||||
goto again;
|
||||
}
|
||||
err = -EEXIST;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/* This has to be done once we're sure module name is unique. */
|
||||
dynamic_debug_setup(info.debug, info.num_debug);
|
||||
|
||||
/* Find duplicate symbols */
|
||||
err = verify_export_symbols(mod);
|
||||
if (err < 0)
|
||||
goto ddebug;
|
||||
|
||||
module_bug_finalize(info.hdr, info.sechdrs, mod);
|
||||
list_add_rcu(&mod->list, &modules);
|
||||
mutex_unlock(&module_mutex);
|
||||
|
||||
/* Module is ready to execute: parsing args may do that. */
|
||||
err = parse_args(mod->name, mod->args, mod->kp, mod->num_kp,
|
||||
-32768, 32767, &ddebug_dyndbg_module_param_cb);
|
||||
if (err < 0)
|
||||
goto unlink;
|
||||
|
||||
/* Link in to syfs. */
|
||||
err = mod_sysfs_setup(mod, &info, mod->kp, mod->num_kp);
|
||||
if (err < 0)
|
||||
goto unlink;
|
||||
|
||||
/* Get rid of temporary copy. */
|
||||
free_copy(&info);
|
||||
|
||||
/* Done! */
|
||||
trace_module_load(mod);
|
||||
return mod;
|
||||
|
||||
unlink:
|
||||
mutex_lock(&module_mutex);
|
||||
/* Unlink carefully: kallsyms could be walking list. */
|
||||
list_del_rcu(&mod->list);
|
||||
module_bug_cleanup(mod);
|
||||
wake_up_all(&module_wq);
|
||||
ddebug:
|
||||
dynamic_debug_remove(info.debug);
|
||||
unlock:
|
||||
mutex_unlock(&module_mutex);
|
||||
synchronize_sched();
|
||||
kfree(mod->args);
|
||||
free_arch_cleanup:
|
||||
module_arch_cleanup(mod);
|
||||
free_modinfo:
|
||||
free_modinfo(mod);
|
||||
free_unload:
|
||||
module_unload_free(mod);
|
||||
free_module:
|
||||
module_deallocate(mod, &info);
|
||||
free_copy:
|
||||
free_copy(&info);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
/* Call module constructors. */
|
||||
static void do_mod_ctors(struct module *mod)
|
||||
{
|
||||
|
|
@ -3102,20 +3009,15 @@ static void do_mod_ctors(struct module *mod)
|
|||
}
|
||||
|
||||
/* This is where the real work happens */
|
||||
SYSCALL_DEFINE3(init_module, void __user *, umod,
|
||||
unsigned long, len, const char __user *, uargs)
|
||||
static int do_init_module(struct module *mod)
|
||||
{
|
||||
struct module *mod;
|
||||
int ret = 0;
|
||||
|
||||
/* Must have permission */
|
||||
if (!capable(CAP_SYS_MODULE) || modules_disabled)
|
||||
return -EPERM;
|
||||
|
||||
/* Do all the hard work */
|
||||
mod = load_module(umod, len, uargs);
|
||||
if (IS_ERR(mod))
|
||||
return PTR_ERR(mod);
|
||||
/*
|
||||
* We want to find out whether @mod uses async during init. Clear
|
||||
* PF_USED_ASYNC. async_schedule*() will set it.
|
||||
*/
|
||||
current->flags &= ~PF_USED_ASYNC;
|
||||
|
||||
blocking_notifier_call_chain(&module_notify_list,
|
||||
MODULE_STATE_COMING, mod);
|
||||
|
|
@ -3162,8 +3064,25 @@ SYSCALL_DEFINE3(init_module, void __user *, umod,
|
|||
blocking_notifier_call_chain(&module_notify_list,
|
||||
MODULE_STATE_LIVE, mod);
|
||||
|
||||
/* We need to finish all async code before the module init sequence is done */
|
||||
async_synchronize_full();
|
||||
/*
|
||||
* We need to finish all async code before the module init sequence
|
||||
* is done. This has potential to deadlock. For example, a newly
|
||||
* detected block device can trigger request_module() of the
|
||||
* default iosched from async probing task. Once userland helper
|
||||
* reaches here, async_synchronize_full() will wait on the async
|
||||
* task waiting on request_module() and deadlock.
|
||||
*
|
||||
* This deadlock is avoided by perfomring async_synchronize_full()
|
||||
* iff module init queued any async jobs. This isn't a full
|
||||
* solution as it will deadlock the same if module loading from
|
||||
* async jobs nests more than once; however, due to the various
|
||||
* constraints, this hack seems to be the best option for now.
|
||||
* Please refer to the following thread for details.
|
||||
*
|
||||
* http://thread.gmane.org/gmane.linux.kernel/1420814
|
||||
*/
|
||||
if (current->flags & PF_USED_ASYNC)
|
||||
async_synchronize_full();
|
||||
|
||||
mutex_lock(&module_mutex);
|
||||
/* Drop initial reference. */
|
||||
|
|
@ -3186,6 +3105,205 @@ SYSCALL_DEFINE3(init_module, void __user *, umod,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int may_init_module(void)
|
||||
{
|
||||
if (!capable(CAP_SYS_MODULE) || modules_disabled)
|
||||
return -EPERM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Allocate and load the module: note that size of section 0 is always
|
||||
zero, and we rely on this for optional sections. */
|
||||
static int load_module(struct load_info *info, const char __user *uargs,
|
||||
int flags)
|
||||
{
|
||||
struct module *mod, *old;
|
||||
long err;
|
||||
|
||||
err = module_sig_check(info);
|
||||
if (err)
|
||||
goto free_copy;
|
||||
|
||||
err = elf_header_check(info);
|
||||
if (err)
|
||||
goto free_copy;
|
||||
|
||||
/* Figure out module layout, and allocate all the memory. */
|
||||
mod = layout_and_allocate(info, flags);
|
||||
if (IS_ERR(mod)) {
|
||||
err = PTR_ERR(mod);
|
||||
goto free_copy;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MODULE_SIG
|
||||
mod->sig_ok = info->sig_ok;
|
||||
if (!mod->sig_ok)
|
||||
add_taint_module(mod, TAINT_FORCED_MODULE);
|
||||
#endif
|
||||
|
||||
/* Now module is in final location, initialize linked lists, etc. */
|
||||
err = module_unload_init(mod);
|
||||
if (err)
|
||||
goto free_module;
|
||||
|
||||
/* Now we've got everything in the final locations, we can
|
||||
* find optional sections. */
|
||||
find_module_sections(mod, info);
|
||||
|
||||
err = check_module_license_and_versions(mod);
|
||||
if (err)
|
||||
goto free_unload;
|
||||
|
||||
/* Set up MODINFO_ATTR fields */
|
||||
setup_modinfo(mod, info);
|
||||
|
||||
/* Fix up syms, so that st_value is a pointer to location. */
|
||||
err = simplify_symbols(mod, info);
|
||||
if (err < 0)
|
||||
goto free_modinfo;
|
||||
|
||||
err = apply_relocations(mod, info);
|
||||
if (err < 0)
|
||||
goto free_modinfo;
|
||||
|
||||
err = post_relocation(mod, info);
|
||||
if (err < 0)
|
||||
goto free_modinfo;
|
||||
|
||||
flush_module_icache(mod);
|
||||
|
||||
/* Now copy in args */
|
||||
mod->args = strndup_user(uargs, ~0UL >> 1);
|
||||
if (IS_ERR(mod->args)) {
|
||||
err = PTR_ERR(mod->args);
|
||||
goto free_arch_cleanup;
|
||||
}
|
||||
|
||||
/* Mark state as coming so strong_try_module_get() ignores us. */
|
||||
mod->state = MODULE_STATE_COMING;
|
||||
|
||||
/* Now sew it into the lists so we can get lockdep and oops
|
||||
* info during argument parsing. No one should access us, since
|
||||
* strong_try_module_get() will fail.
|
||||
* lockdep/oops can run asynchronous, so use the RCU list insertion
|
||||
* function to insert in a way safe to concurrent readers.
|
||||
* The mutex protects against concurrent writers.
|
||||
*/
|
||||
again:
|
||||
mutex_lock(&module_mutex);
|
||||
if ((old = find_module(mod->name)) != NULL) {
|
||||
if (old->state == MODULE_STATE_COMING) {
|
||||
/* Wait in case it fails to load. */
|
||||
mutex_unlock(&module_mutex);
|
||||
err = wait_event_interruptible(module_wq,
|
||||
finished_loading(mod->name));
|
||||
if (err)
|
||||
goto free_arch_cleanup;
|
||||
goto again;
|
||||
}
|
||||
err = -EEXIST;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/* This has to be done once we're sure module name is unique. */
|
||||
dynamic_debug_setup(info->debug, info->num_debug);
|
||||
|
||||
/* Find duplicate symbols */
|
||||
err = verify_export_symbols(mod);
|
||||
if (err < 0)
|
||||
goto ddebug;
|
||||
|
||||
module_bug_finalize(info->hdr, info->sechdrs, mod);
|
||||
list_add_rcu(&mod->list, &modules);
|
||||
mutex_unlock(&module_mutex);
|
||||
|
||||
/* Module is ready to execute: parsing args may do that. */
|
||||
err = parse_args(mod->name, mod->args, mod->kp, mod->num_kp,
|
||||
-32768, 32767, &ddebug_dyndbg_module_param_cb);
|
||||
if (err < 0)
|
||||
goto unlink;
|
||||
|
||||
/* Link in to syfs. */
|
||||
err = mod_sysfs_setup(mod, info, mod->kp, mod->num_kp);
|
||||
if (err < 0)
|
||||
goto unlink;
|
||||
|
||||
/* Get rid of temporary copy. */
|
||||
free_copy(info);
|
||||
|
||||
/* Done! */
|
||||
trace_module_load(mod);
|
||||
|
||||
return do_init_module(mod);
|
||||
|
||||
unlink:
|
||||
mutex_lock(&module_mutex);
|
||||
/* Unlink carefully: kallsyms could be walking list. */
|
||||
list_del_rcu(&mod->list);
|
||||
module_bug_cleanup(mod);
|
||||
wake_up_all(&module_wq);
|
||||
ddebug:
|
||||
dynamic_debug_remove(info->debug);
|
||||
unlock:
|
||||
mutex_unlock(&module_mutex);
|
||||
synchronize_sched();
|
||||
kfree(mod->args);
|
||||
free_arch_cleanup:
|
||||
module_arch_cleanup(mod);
|
||||
free_modinfo:
|
||||
free_modinfo(mod);
|
||||
free_unload:
|
||||
module_unload_free(mod);
|
||||
free_module:
|
||||
module_deallocate(mod, info);
|
||||
free_copy:
|
||||
free_copy(info);
|
||||
return err;
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE3(init_module, void __user *, umod,
|
||||
unsigned long, len, const char __user *, uargs)
|
||||
{
|
||||
int err;
|
||||
struct load_info info = { };
|
||||
|
||||
err = may_init_module();
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
pr_debug("init_module: umod=%p, len=%lu, uargs=%p\n",
|
||||
umod, len, uargs);
|
||||
|
||||
err = copy_module_from_user(umod, len, &info);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return load_module(&info, uargs, 0);
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE3(finit_module, int, fd, const char __user *, uargs, int, flags)
|
||||
{
|
||||
int err;
|
||||
struct load_info info = { };
|
||||
|
||||
err = may_init_module();
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
pr_debug("finit_module: fd=%d, uargs=%p, flags=%i\n", fd, uargs, flags);
|
||||
|
||||
if (flags & ~(MODULE_INIT_IGNORE_MODVERSIONS
|
||||
|MODULE_INIT_IGNORE_VERMAGIC))
|
||||
return -EINVAL;
|
||||
|
||||
err = copy_module_from_fd(fd, &info);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return load_module(&info, uargs, flags);
|
||||
}
|
||||
|
||||
static inline int within(unsigned long addr, void *start, unsigned long size)
|
||||
{
|
||||
return ((void *)addr >= start && (void *)addr < start + size);
|
||||
|
|
|
|||
|
|
@ -27,13 +27,13 @@
|
|||
* - Information block
|
||||
*/
|
||||
struct module_signature {
|
||||
enum pkey_algo algo : 8; /* Public-key crypto algorithm */
|
||||
enum pkey_hash_algo hash : 8; /* Digest algorithm */
|
||||
enum pkey_id_type id_type : 8; /* Key identifier type */
|
||||
u8 signer_len; /* Length of signer's name */
|
||||
u8 key_id_len; /* Length of key identifier */
|
||||
u8 __pad[3];
|
||||
__be32 sig_len; /* Length of signature data */
|
||||
u8 algo; /* Public-key crypto algorithm [enum pkey_algo] */
|
||||
u8 hash; /* Digest algorithm [enum pkey_hash_algo] */
|
||||
u8 id_type; /* Key identifier type [enum pkey_id_type] */
|
||||
u8 signer_len; /* Length of signer's name */
|
||||
u8 key_id_len; /* Length of key identifier */
|
||||
u8 __pad[3];
|
||||
__be32 sig_len; /* Length of signature data */
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -57,7 +57,8 @@ static inline struct nsproxy *create_nsproxy(void)
|
|||
* leave it to the caller to do proper locking and attach it to task.
|
||||
*/
|
||||
static struct nsproxy *create_new_namespaces(unsigned long flags,
|
||||
struct task_struct *tsk, struct fs_struct *new_fs)
|
||||
struct task_struct *tsk, struct user_namespace *user_ns,
|
||||
struct fs_struct *new_fs)
|
||||
{
|
||||
struct nsproxy *new_nsp;
|
||||
int err;
|
||||
|
|
@ -66,31 +67,31 @@ static struct nsproxy *create_new_namespaces(unsigned long flags,
|
|||
if (!new_nsp)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
new_nsp->mnt_ns = copy_mnt_ns(flags, tsk->nsproxy->mnt_ns, new_fs);
|
||||
new_nsp->mnt_ns = copy_mnt_ns(flags, tsk->nsproxy->mnt_ns, user_ns, new_fs);
|
||||
if (IS_ERR(new_nsp->mnt_ns)) {
|
||||
err = PTR_ERR(new_nsp->mnt_ns);
|
||||
goto out_ns;
|
||||
}
|
||||
|
||||
new_nsp->uts_ns = copy_utsname(flags, tsk);
|
||||
new_nsp->uts_ns = copy_utsname(flags, user_ns, tsk->nsproxy->uts_ns);
|
||||
if (IS_ERR(new_nsp->uts_ns)) {
|
||||
err = PTR_ERR(new_nsp->uts_ns);
|
||||
goto out_uts;
|
||||
}
|
||||
|
||||
new_nsp->ipc_ns = copy_ipcs(flags, tsk);
|
||||
new_nsp->ipc_ns = copy_ipcs(flags, user_ns, tsk->nsproxy->ipc_ns);
|
||||
if (IS_ERR(new_nsp->ipc_ns)) {
|
||||
err = PTR_ERR(new_nsp->ipc_ns);
|
||||
goto out_ipc;
|
||||
}
|
||||
|
||||
new_nsp->pid_ns = copy_pid_ns(flags, task_active_pid_ns(tsk));
|
||||
new_nsp->pid_ns = copy_pid_ns(flags, user_ns, tsk->nsproxy->pid_ns);
|
||||
if (IS_ERR(new_nsp->pid_ns)) {
|
||||
err = PTR_ERR(new_nsp->pid_ns);
|
||||
goto out_pid;
|
||||
}
|
||||
|
||||
new_nsp->net_ns = copy_net_ns(flags, tsk->nsproxy->net_ns);
|
||||
new_nsp->net_ns = copy_net_ns(flags, user_ns, tsk->nsproxy->net_ns);
|
||||
if (IS_ERR(new_nsp->net_ns)) {
|
||||
err = PTR_ERR(new_nsp->net_ns);
|
||||
goto out_net;
|
||||
|
|
@ -122,6 +123,7 @@ out_ns:
|
|||
int copy_namespaces(unsigned long flags, struct task_struct *tsk)
|
||||
{
|
||||
struct nsproxy *old_ns = tsk->nsproxy;
|
||||
struct user_namespace *user_ns = task_cred_xxx(tsk, user_ns);
|
||||
struct nsproxy *new_ns;
|
||||
int err = 0;
|
||||
|
||||
|
|
@ -134,7 +136,7 @@ int copy_namespaces(unsigned long flags, struct task_struct *tsk)
|
|||
CLONE_NEWPID | CLONE_NEWNET)))
|
||||
return 0;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN)) {
|
||||
if (!ns_capable(user_ns, CAP_SYS_ADMIN)) {
|
||||
err = -EPERM;
|
||||
goto out;
|
||||
}
|
||||
|
|
@ -151,7 +153,8 @@ int copy_namespaces(unsigned long flags, struct task_struct *tsk)
|
|||
goto out;
|
||||
}
|
||||
|
||||
new_ns = create_new_namespaces(flags, tsk, tsk->fs);
|
||||
new_ns = create_new_namespaces(flags, tsk,
|
||||
task_cred_xxx(tsk, user_ns), tsk->fs);
|
||||
if (IS_ERR(new_ns)) {
|
||||
err = PTR_ERR(new_ns);
|
||||
goto out;
|
||||
|
|
@ -183,19 +186,21 @@ void free_nsproxy(struct nsproxy *ns)
|
|||
* On success, returns the new nsproxy.
|
||||
*/
|
||||
int unshare_nsproxy_namespaces(unsigned long unshare_flags,
|
||||
struct nsproxy **new_nsp, struct fs_struct *new_fs)
|
||||
struct nsproxy **new_nsp, struct cred *new_cred, struct fs_struct *new_fs)
|
||||
{
|
||||
struct user_namespace *user_ns;
|
||||
int err = 0;
|
||||
|
||||
if (!(unshare_flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC |
|
||||
CLONE_NEWNET)))
|
||||
CLONE_NEWNET | CLONE_NEWPID)))
|
||||
return 0;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
user_ns = new_cred ? new_cred->user_ns : current_user_ns();
|
||||
if (!ns_capable(user_ns, CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
*new_nsp = create_new_namespaces(unshare_flags, current,
|
||||
new_fs ? new_fs : current->fs);
|
||||
*new_nsp = create_new_namespaces(unshare_flags, current, user_ns,
|
||||
new_fs ? new_fs : current->fs);
|
||||
if (IS_ERR(*new_nsp)) {
|
||||
err = PTR_ERR(*new_nsp);
|
||||
goto out;
|
||||
|
|
@ -241,9 +246,6 @@ SYSCALL_DEFINE2(setns, int, fd, int, nstype)
|
|||
struct file *file;
|
||||
int err;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
file = proc_ns_fget(fd);
|
||||
if (IS_ERR(file))
|
||||
return PTR_ERR(file);
|
||||
|
|
@ -254,7 +256,7 @@ SYSCALL_DEFINE2(setns, int, fd, int, nstype)
|
|||
if (nstype && (ops->type != nstype))
|
||||
goto out;
|
||||
|
||||
new_nsproxy = create_new_namespaces(0, tsk, tsk->fs);
|
||||
new_nsproxy = create_new_namespaces(0, tsk, current_user_ns(), tsk->fs);
|
||||
if (IS_ERR(new_nsproxy)) {
|
||||
err = PTR_ERR(new_nsproxy);
|
||||
goto out;
|
||||
|
|
|
|||
|
|
@ -171,7 +171,7 @@ static struct padata_priv *padata_get_next(struct parallel_data *pd)
|
|||
{
|
||||
int cpu, num_cpus;
|
||||
unsigned int next_nr, next_index;
|
||||
struct padata_parallel_queue *queue, *next_queue;
|
||||
struct padata_parallel_queue *next_queue;
|
||||
struct padata_priv *padata;
|
||||
struct padata_list *reorder;
|
||||
|
||||
|
|
@ -204,8 +204,7 @@ static struct padata_priv *padata_get_next(struct parallel_data *pd)
|
|||
goto out;
|
||||
}
|
||||
|
||||
queue = per_cpu_ptr(pd->pqueue, smp_processor_id());
|
||||
if (queue->cpu_index == next_queue->cpu_index) {
|
||||
if (__this_cpu_read(pd->pqueue->cpu_index) == next_queue->cpu_index) {
|
||||
padata = ERR_PTR(-ENODATA);
|
||||
goto out;
|
||||
}
|
||||
|
|
|
|||
75
kernel/pid.c
75
kernel/pid.c
|
|
@ -1,8 +1,8 @@
|
|||
/*
|
||||
* Generic pidhash and scalable, time-bounded PID allocator
|
||||
*
|
||||
* (C) 2002-2003 William Irwin, IBM
|
||||
* (C) 2004 William Irwin, Oracle
|
||||
* (C) 2002-2003 Nadia Yvette Chambers, IBM
|
||||
* (C) 2004 Nadia Yvette Chambers, Oracle
|
||||
* (C) 2002-2004 Ingo Molnar, Red Hat
|
||||
*
|
||||
* pid-structures are backing objects for tasks sharing a given ID to chain
|
||||
|
|
@ -36,6 +36,7 @@
|
|||
#include <linux/pid_namespace.h>
|
||||
#include <linux/init_task.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/proc_fs.h>
|
||||
|
||||
#define pid_hashfn(nr, ns) \
|
||||
hash_long((unsigned long)nr + (unsigned long)ns, pidhash_shift)
|
||||
|
|
@ -78,24 +79,11 @@ struct pid_namespace init_pid_ns = {
|
|||
.last_pid = 0,
|
||||
.level = 0,
|
||||
.child_reaper = &init_task,
|
||||
.user_ns = &init_user_ns,
|
||||
.proc_inum = PROC_PID_INIT_INO,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(init_pid_ns);
|
||||
|
||||
int is_container_init(struct task_struct *tsk)
|
||||
{
|
||||
int ret = 0;
|
||||
struct pid *pid;
|
||||
|
||||
rcu_read_lock();
|
||||
pid = task_pid(tsk);
|
||||
if (pid != NULL && pid->numbers[pid->level].nr == 1)
|
||||
ret = 1;
|
||||
rcu_read_unlock();
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(is_container_init);
|
||||
|
||||
/*
|
||||
* Note: disable interrupts while the pidmap_lock is held as an
|
||||
* interrupt might come in and do read_lock(&tasklist_lock).
|
||||
|
|
@ -269,8 +257,23 @@ void free_pid(struct pid *pid)
|
|||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&pidmap_lock, flags);
|
||||
for (i = 0; i <= pid->level; i++)
|
||||
hlist_del_rcu(&pid->numbers[i].pid_chain);
|
||||
for (i = 0; i <= pid->level; i++) {
|
||||
struct upid *upid = pid->numbers + i;
|
||||
struct pid_namespace *ns = upid->ns;
|
||||
hlist_del_rcu(&upid->pid_chain);
|
||||
switch(--ns->nr_hashed) {
|
||||
case 1:
|
||||
/* When all that is left in the pid namespace
|
||||
* is the reaper wake up the reaper. The reaper
|
||||
* may be sleeping in zap_pid_ns_processes().
|
||||
*/
|
||||
wake_up_process(ns->child_reaper);
|
||||
break;
|
||||
case 0:
|
||||
schedule_work(&ns->proc_work);
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&pidmap_lock, flags);
|
||||
|
||||
for (i = 0; i <= pid->level; i++)
|
||||
|
|
@ -292,6 +295,7 @@ struct pid *alloc_pid(struct pid_namespace *ns)
|
|||
goto out;
|
||||
|
||||
tmp = ns;
|
||||
pid->level = ns->level;
|
||||
for (i = ns->level; i >= 0; i--) {
|
||||
nr = alloc_pidmap(tmp);
|
||||
if (nr < 0)
|
||||
|
|
@ -302,22 +306,32 @@ struct pid *alloc_pid(struct pid_namespace *ns)
|
|||
tmp = tmp->parent;
|
||||
}
|
||||
|
||||
if (unlikely(is_child_reaper(pid))) {
|
||||
if (pid_ns_prepare_proc(ns))
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
get_pid_ns(ns);
|
||||
pid->level = ns->level;
|
||||
atomic_set(&pid->count, 1);
|
||||
for (type = 0; type < PIDTYPE_MAX; ++type)
|
||||
INIT_HLIST_HEAD(&pid->tasks[type]);
|
||||
|
||||
upid = pid->numbers + ns->level;
|
||||
spin_lock_irq(&pidmap_lock);
|
||||
for ( ; upid >= pid->numbers; --upid)
|
||||
if (!(ns->nr_hashed & PIDNS_HASH_ADDING))
|
||||
goto out_unlock;
|
||||
for ( ; upid >= pid->numbers; --upid) {
|
||||
hlist_add_head_rcu(&upid->pid_chain,
|
||||
&pid_hash[pid_hashfn(upid->nr, upid->ns)]);
|
||||
upid->ns->nr_hashed++;
|
||||
}
|
||||
spin_unlock_irq(&pidmap_lock);
|
||||
|
||||
out:
|
||||
return pid;
|
||||
|
||||
out_unlock:
|
||||
spin_unlock(&pidmap_lock);
|
||||
out_free:
|
||||
while (++i <= ns->level)
|
||||
free_pidmap(pid->numbers + i);
|
||||
|
|
@ -327,6 +341,13 @@ out_free:
|
|||
goto out;
|
||||
}
|
||||
|
||||
void disable_pid_allocation(struct pid_namespace *ns)
|
||||
{
|
||||
spin_lock_irq(&pidmap_lock);
|
||||
ns->nr_hashed &= ~PIDNS_HASH_ADDING;
|
||||
spin_unlock_irq(&pidmap_lock);
|
||||
}
|
||||
|
||||
struct pid *find_pid_ns(int nr, struct pid_namespace *ns)
|
||||
{
|
||||
struct hlist_node *elem;
|
||||
|
|
@ -344,7 +365,7 @@ EXPORT_SYMBOL_GPL(find_pid_ns);
|
|||
|
||||
struct pid *find_vpid(int nr)
|
||||
{
|
||||
return find_pid_ns(nr, current->nsproxy->pid_ns);
|
||||
return find_pid_ns(nr, task_active_pid_ns(current));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(find_vpid);
|
||||
|
||||
|
|
@ -428,7 +449,7 @@ struct task_struct *find_task_by_pid_ns(pid_t nr, struct pid_namespace *ns)
|
|||
|
||||
struct task_struct *find_task_by_vpid(pid_t vnr)
|
||||
{
|
||||
return find_task_by_pid_ns(vnr, current->nsproxy->pid_ns);
|
||||
return find_task_by_pid_ns(vnr, task_active_pid_ns(current));
|
||||
}
|
||||
|
||||
struct pid *get_task_pid(struct task_struct *task, enum pid_type type)
|
||||
|
|
@ -483,7 +504,7 @@ EXPORT_SYMBOL_GPL(pid_nr_ns);
|
|||
|
||||
pid_t pid_vnr(struct pid *pid)
|
||||
{
|
||||
return pid_nr_ns(pid, current->nsproxy->pid_ns);
|
||||
return pid_nr_ns(pid, task_active_pid_ns(current));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pid_vnr);
|
||||
|
||||
|
|
@ -494,7 +515,7 @@ pid_t __task_pid_nr_ns(struct task_struct *task, enum pid_type type,
|
|||
|
||||
rcu_read_lock();
|
||||
if (!ns)
|
||||
ns = current->nsproxy->pid_ns;
|
||||
ns = task_active_pid_ns(current);
|
||||
if (likely(pid_alive(task))) {
|
||||
if (type != PIDTYPE_PID)
|
||||
task = task->group_leader;
|
||||
|
|
@ -558,6 +579,9 @@ void __init pidhash_init(void)
|
|||
|
||||
void __init pidmap_init(void)
|
||||
{
|
||||
/* Veryify no one has done anything silly */
|
||||
BUILD_BUG_ON(PID_MAX_LIMIT >= PIDNS_HASH_ADDING);
|
||||
|
||||
/* bump default and minimum pid_max based on number of cpus */
|
||||
pid_max = min(pid_max_max, max_t(int, pid_max,
|
||||
PIDS_PER_CPU_DEFAULT * num_possible_cpus()));
|
||||
|
|
@ -569,6 +593,7 @@ void __init pidmap_init(void)
|
|||
/* Reserve PID 0. We never call free_pidmap(0) */
|
||||
set_bit(0, init_pid_ns.pidmap[0].page);
|
||||
atomic_dec(&init_pid_ns.pidmap[0].nr_free);
|
||||
init_pid_ns.nr_hashed = PIDNS_HASH_ADDING;
|
||||
|
||||
init_pid_ns.pid_cachep = KMEM_CACHE(pid,
|
||||
SLAB_HWCACHE_ALIGN | SLAB_PANIC);
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
#include <linux/pid.h>
|
||||
#include <linux/pid_namespace.h>
|
||||
#include <linux/user_namespace.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/acct.h>
|
||||
|
|
@ -71,10 +72,17 @@ err_alloc:
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static void proc_cleanup_work(struct work_struct *work)
|
||||
{
|
||||
struct pid_namespace *ns = container_of(work, struct pid_namespace, proc_work);
|
||||
pid_ns_release_proc(ns);
|
||||
}
|
||||
|
||||
/* MAX_PID_NS_LEVEL is needed for limiting size of 'struct pid' */
|
||||
#define MAX_PID_NS_LEVEL 32
|
||||
|
||||
static struct pid_namespace *create_pid_namespace(struct pid_namespace *parent_pid_ns)
|
||||
static struct pid_namespace *create_pid_namespace(struct user_namespace *user_ns,
|
||||
struct pid_namespace *parent_pid_ns)
|
||||
{
|
||||
struct pid_namespace *ns;
|
||||
unsigned int level = parent_pid_ns->level + 1;
|
||||
|
|
@ -99,9 +107,16 @@ static struct pid_namespace *create_pid_namespace(struct pid_namespace *parent_p
|
|||
if (ns->pid_cachep == NULL)
|
||||
goto out_free_map;
|
||||
|
||||
err = proc_alloc_inum(&ns->proc_inum);
|
||||
if (err)
|
||||
goto out_free_map;
|
||||
|
||||
kref_init(&ns->kref);
|
||||
ns->level = level;
|
||||
ns->parent = get_pid_ns(parent_pid_ns);
|
||||
ns->user_ns = get_user_ns(user_ns);
|
||||
ns->nr_hashed = PIDNS_HASH_ADDING;
|
||||
INIT_WORK(&ns->proc_work, proc_cleanup_work);
|
||||
|
||||
set_bit(0, ns->pidmap[0].page);
|
||||
atomic_set(&ns->pidmap[0].nr_free, BITS_PER_PAGE - 1);
|
||||
|
|
@ -109,14 +124,8 @@ static struct pid_namespace *create_pid_namespace(struct pid_namespace *parent_p
|
|||
for (i = 1; i < PIDMAP_ENTRIES; i++)
|
||||
atomic_set(&ns->pidmap[i].nr_free, BITS_PER_PAGE);
|
||||
|
||||
err = pid_ns_prepare_proc(ns);
|
||||
if (err)
|
||||
goto out_put_parent_pid_ns;
|
||||
|
||||
return ns;
|
||||
|
||||
out_put_parent_pid_ns:
|
||||
put_pid_ns(parent_pid_ns);
|
||||
out_free_map:
|
||||
kfree(ns->pidmap[0].page);
|
||||
out_free:
|
||||
|
|
@ -129,18 +138,21 @@ static void destroy_pid_namespace(struct pid_namespace *ns)
|
|||
{
|
||||
int i;
|
||||
|
||||
proc_free_inum(ns->proc_inum);
|
||||
for (i = 0; i < PIDMAP_ENTRIES; i++)
|
||||
kfree(ns->pidmap[i].page);
|
||||
put_user_ns(ns->user_ns);
|
||||
kmem_cache_free(pid_ns_cachep, ns);
|
||||
}
|
||||
|
||||
struct pid_namespace *copy_pid_ns(unsigned long flags, struct pid_namespace *old_ns)
|
||||
struct pid_namespace *copy_pid_ns(unsigned long flags,
|
||||
struct user_namespace *user_ns, struct pid_namespace *old_ns)
|
||||
{
|
||||
if (!(flags & CLONE_NEWPID))
|
||||
return get_pid_ns(old_ns);
|
||||
if (flags & (CLONE_THREAD|CLONE_PARENT))
|
||||
if (task_active_pid_ns(current) != old_ns)
|
||||
return ERR_PTR(-EINVAL);
|
||||
return create_pid_namespace(old_ns);
|
||||
return create_pid_namespace(user_ns, old_ns);
|
||||
}
|
||||
|
||||
static void free_pid_ns(struct kref *kref)
|
||||
|
|
@ -170,6 +182,9 @@ void zap_pid_ns_processes(struct pid_namespace *pid_ns)
|
|||
int rc;
|
||||
struct task_struct *task, *me = current;
|
||||
|
||||
/* Don't allow any more processes into the pid namespace */
|
||||
disable_pid_allocation(pid_ns);
|
||||
|
||||
/* Ignore SIGCHLD causing any terminated children to autoreap */
|
||||
spin_lock_irq(&me->sighand->siglock);
|
||||
me->sighand->action[SIGCHLD - 1].sa.sa_handler = SIG_IGN;
|
||||
|
|
@ -211,22 +226,15 @@ void zap_pid_ns_processes(struct pid_namespace *pid_ns)
|
|||
|
||||
/*
|
||||
* sys_wait4() above can't reap the TASK_DEAD children.
|
||||
* Make sure they all go away, see __unhash_process().
|
||||
* Make sure they all go away, see free_pid().
|
||||
*/
|
||||
for (;;) {
|
||||
bool need_wait = false;
|
||||
|
||||
read_lock(&tasklist_lock);
|
||||
if (!list_empty(¤t->children)) {
|
||||
__set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
need_wait = true;
|
||||
}
|
||||
read_unlock(&tasklist_lock);
|
||||
|
||||
if (!need_wait)
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
if (pid_ns->nr_hashed == 1)
|
||||
break;
|
||||
schedule();
|
||||
}
|
||||
__set_current_state(TASK_RUNNING);
|
||||
|
||||
if (pid_ns->reboot)
|
||||
current->signal->group_exit_code = pid_ns->reboot;
|
||||
|
|
@ -239,9 +247,10 @@ void zap_pid_ns_processes(struct pid_namespace *pid_ns)
|
|||
static int pid_ns_ctl_handler(struct ctl_table *table, int write,
|
||||
void __user *buffer, size_t *lenp, loff_t *ppos)
|
||||
{
|
||||
struct pid_namespace *pid_ns = task_active_pid_ns(current);
|
||||
struct ctl_table tmp = *table;
|
||||
|
||||
if (write && !capable(CAP_SYS_ADMIN))
|
||||
if (write && !ns_capable(pid_ns->user_ns, CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
/*
|
||||
|
|
@ -250,7 +259,7 @@ static int pid_ns_ctl_handler(struct ctl_table *table, int write,
|
|||
* it should synchronize its usage with external means.
|
||||
*/
|
||||
|
||||
tmp.data = ¤t->nsproxy->pid_ns->last_pid;
|
||||
tmp.data = &pid_ns->last_pid;
|
||||
return proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
|
||||
}
|
||||
|
||||
|
|
@ -299,6 +308,68 @@ int reboot_pid_ns(struct pid_namespace *pid_ns, int cmd)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void *pidns_get(struct task_struct *task)
|
||||
{
|
||||
struct pid_namespace *ns;
|
||||
|
||||
rcu_read_lock();
|
||||
ns = get_pid_ns(task_active_pid_ns(task));
|
||||
rcu_read_unlock();
|
||||
|
||||
return ns;
|
||||
}
|
||||
|
||||
static void pidns_put(void *ns)
|
||||
{
|
||||
put_pid_ns(ns);
|
||||
}
|
||||
|
||||
static int pidns_install(struct nsproxy *nsproxy, void *ns)
|
||||
{
|
||||
struct pid_namespace *active = task_active_pid_ns(current);
|
||||
struct pid_namespace *ancestor, *new = ns;
|
||||
|
||||
if (!ns_capable(new->user_ns, CAP_SYS_ADMIN) ||
|
||||
!nsown_capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
/*
|
||||
* Only allow entering the current active pid namespace
|
||||
* or a child of the current active pid namespace.
|
||||
*
|
||||
* This is required for fork to return a usable pid value and
|
||||
* this maintains the property that processes and their
|
||||
* children can not escape their current pid namespace.
|
||||
*/
|
||||
if (new->level < active->level)
|
||||
return -EINVAL;
|
||||
|
||||
ancestor = new;
|
||||
while (ancestor->level > active->level)
|
||||
ancestor = ancestor->parent;
|
||||
if (ancestor != active)
|
||||
return -EINVAL;
|
||||
|
||||
put_pid_ns(nsproxy->pid_ns);
|
||||
nsproxy->pid_ns = get_pid_ns(new);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int pidns_inum(void *ns)
|
||||
{
|
||||
struct pid_namespace *pid_ns = ns;
|
||||
return pid_ns->proc_inum;
|
||||
}
|
||||
|
||||
const struct proc_ns_operations pidns_operations = {
|
||||
.name = "pid",
|
||||
.type = CLONE_NEWPID,
|
||||
.get = pidns_get,
|
||||
.put = pidns_put,
|
||||
.install = pidns_install,
|
||||
.inum = pidns_inum,
|
||||
};
|
||||
|
||||
static __init int pid_namespaces_init(void)
|
||||
{
|
||||
pid_ns_cachep = KMEM_CACHE(pid_namespace, SLAB_PANIC);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include <asm/uaccess.h>
|
||||
#include <linux/kernel_stat.h>
|
||||
#include <trace/events/timer.h>
|
||||
#include <linux/random.h>
|
||||
|
||||
/*
|
||||
* Called after updating RLIMIT_CPU to run cpu timer and update
|
||||
|
|
@ -217,30 +218,6 @@ static int cpu_clock_sample(const clockid_t which_clock, struct task_struct *p,
|
|||
return 0;
|
||||
}
|
||||
|
||||
void thread_group_cputime(struct task_struct *tsk, struct task_cputime *times)
|
||||
{
|
||||
struct signal_struct *sig = tsk->signal;
|
||||
struct task_struct *t;
|
||||
|
||||
times->utime = sig->utime;
|
||||
times->stime = sig->stime;
|
||||
times->sum_exec_runtime = sig->sum_sched_runtime;
|
||||
|
||||
rcu_read_lock();
|
||||
/* make sure we can trust tsk->thread_group list */
|
||||
if (!likely(pid_alive(tsk)))
|
||||
goto out;
|
||||
|
||||
t = tsk;
|
||||
do {
|
||||
times->utime += t->utime;
|
||||
times->stime += t->stime;
|
||||
times->sum_exec_runtime += task_sched_runtime(t);
|
||||
} while_each_thread(tsk, t);
|
||||
out:
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
static void update_gt_cputime(struct task_cputime *a, struct task_cputime *b)
|
||||
{
|
||||
if (b->utime > a->utime)
|
||||
|
|
@ -494,6 +471,8 @@ static void cleanup_timers(struct list_head *head,
|
|||
*/
|
||||
void posix_cpu_timers_exit(struct task_struct *tsk)
|
||||
{
|
||||
add_device_randomness((const void*) &tsk->se.sum_exec_runtime,
|
||||
sizeof(unsigned long long));
|
||||
cleanup_timers(tsk->cpu_timers,
|
||||
tsk->utime, tsk->stime, tsk->se.sum_exec_runtime);
|
||||
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ static ssize_t pm_async_store(struct kobject *kobj, struct kobj_attribute *attr,
|
|||
{
|
||||
unsigned long val;
|
||||
|
||||
if (strict_strtoul(buf, 10, &val))
|
||||
if (kstrtoul(buf, 10, &val))
|
||||
return -EINVAL;
|
||||
|
||||
if (val > 1)
|
||||
|
|
|
|||
|
|
@ -48,18 +48,7 @@ static int try_to_freeze_tasks(bool user_only)
|
|||
if (p == current || !freeze_task(p))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Now that we've done set_freeze_flag, don't
|
||||
* perturb a task in TASK_STOPPED or TASK_TRACED.
|
||||
* It is "frozen enough". If the task does wake
|
||||
* up, it will immediately call try_to_freeze.
|
||||
*
|
||||
* Because freeze_task() goes through p's scheduler lock, it's
|
||||
* guaranteed that TASK_STOPPED/TRACED -> TASK_RUNNING
|
||||
* transition can't race with task state testing here.
|
||||
*/
|
||||
if (!task_is_stopped_or_traced(p) &&
|
||||
!freezer_should_skip(p))
|
||||
if (!freezer_should_skip(p))
|
||||
todo++;
|
||||
} while_each_thread(g, p);
|
||||
read_unlock(&tasklist_lock);
|
||||
|
|
|
|||
|
|
@ -212,6 +212,69 @@ int pm_qos_update_target(struct pm_qos_constraints *c, struct plist_node *node,
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_qos_flags_remove_req - Remove device PM QoS flags request.
|
||||
* @pqf: Device PM QoS flags set to remove the request from.
|
||||
* @req: Request to remove from the set.
|
||||
*/
|
||||
static void pm_qos_flags_remove_req(struct pm_qos_flags *pqf,
|
||||
struct pm_qos_flags_request *req)
|
||||
{
|
||||
s32 val = 0;
|
||||
|
||||
list_del(&req->node);
|
||||
list_for_each_entry(req, &pqf->list, node)
|
||||
val |= req->flags;
|
||||
|
||||
pqf->effective_flags = val;
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_qos_update_flags - Update a set of PM QoS flags.
|
||||
* @pqf: Set of flags to update.
|
||||
* @req: Request to add to the set, to modify, or to remove from the set.
|
||||
* @action: Action to take on the set.
|
||||
* @val: Value of the request to add or modify.
|
||||
*
|
||||
* Update the given set of PM QoS flags and call notifiers if the aggregate
|
||||
* value has changed. Returns 1 if the aggregate constraint value has changed,
|
||||
* 0 otherwise.
|
||||
*/
|
||||
bool pm_qos_update_flags(struct pm_qos_flags *pqf,
|
||||
struct pm_qos_flags_request *req,
|
||||
enum pm_qos_req_action action, s32 val)
|
||||
{
|
||||
unsigned long irqflags;
|
||||
s32 prev_value, curr_value;
|
||||
|
||||
spin_lock_irqsave(&pm_qos_lock, irqflags);
|
||||
|
||||
prev_value = list_empty(&pqf->list) ? 0 : pqf->effective_flags;
|
||||
|
||||
switch (action) {
|
||||
case PM_QOS_REMOVE_REQ:
|
||||
pm_qos_flags_remove_req(pqf, req);
|
||||
break;
|
||||
case PM_QOS_UPDATE_REQ:
|
||||
pm_qos_flags_remove_req(pqf, req);
|
||||
case PM_QOS_ADD_REQ:
|
||||
req->flags = val;
|
||||
INIT_LIST_HEAD(&req->node);
|
||||
list_add_tail(&req->node, &pqf->list);
|
||||
pqf->effective_flags |= val;
|
||||
break;
|
||||
default:
|
||||
/* no action */
|
||||
;
|
||||
}
|
||||
|
||||
curr_value = list_empty(&pqf->list) ? 0 : pqf->effective_flags;
|
||||
|
||||
spin_unlock_irqrestore(&pm_qos_lock, irqflags);
|
||||
|
||||
return prev_value != curr_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_qos_request - returns current system wide qos expectation
|
||||
* @pm_qos_class: identification of which qos value is requested
|
||||
|
|
@ -500,7 +563,7 @@ static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf,
|
|||
} else {
|
||||
ascii_value[count] = '\0';
|
||||
}
|
||||
ret = strict_strtoul(ascii_value, 16, &ulval);
|
||||
ret = kstrtoul(ascii_value, 16, &ulval);
|
||||
if (ret) {
|
||||
pr_debug("%s, 0x%lx, 0x%x\n", ascii_value, ulval, ret);
|
||||
return -EINVAL;
|
||||
|
|
|
|||
|
|
@ -126,7 +126,7 @@ static int swsusp_extents_insert(unsigned long swap_offset)
|
|||
|
||||
/* Figure out where to put the new node */
|
||||
while (*new) {
|
||||
ext = container_of(*new, struct swsusp_extent, node);
|
||||
ext = rb_entry(*new, struct swsusp_extent, node);
|
||||
parent = *new;
|
||||
if (swap_offset < ext->start) {
|
||||
/* Try to merge */
|
||||
|
|
|
|||
|
|
@ -87,6 +87,12 @@ static DEFINE_SEMAPHORE(console_sem);
|
|||
struct console *console_drivers;
|
||||
EXPORT_SYMBOL_GPL(console_drivers);
|
||||
|
||||
#ifdef CONFIG_LOCKDEP
|
||||
static struct lockdep_map console_lock_dep_map = {
|
||||
.name = "console_lock"
|
||||
};
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This is used for debugging the mess that is the VT code by
|
||||
* keeping track if we have the console semaphore held. It's
|
||||
|
|
@ -741,6 +747,21 @@ void __init setup_log_buf(int early)
|
|||
free, (free * 100) / __LOG_BUF_LEN);
|
||||
}
|
||||
|
||||
static bool __read_mostly ignore_loglevel;
|
||||
|
||||
static int __init ignore_loglevel_setup(char *str)
|
||||
{
|
||||
ignore_loglevel = 1;
|
||||
printk(KERN_INFO "debug: ignoring loglevel setting.\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
early_param("ignore_loglevel", ignore_loglevel_setup);
|
||||
module_param(ignore_loglevel, bool, S_IRUGO | S_IWUSR);
|
||||
MODULE_PARM_DESC(ignore_loglevel, "ignore loglevel setting, to"
|
||||
"print all kernel messages to the console.");
|
||||
|
||||
#ifdef CONFIG_BOOT_PRINTK_DELAY
|
||||
|
||||
static int boot_delay; /* msecs delay after each printk during bootup */
|
||||
|
|
@ -764,13 +785,15 @@ static int __init boot_delay_setup(char *str)
|
|||
}
|
||||
__setup("boot_delay=", boot_delay_setup);
|
||||
|
||||
static void boot_delay_msec(void)
|
||||
static void boot_delay_msec(int level)
|
||||
{
|
||||
unsigned long long k;
|
||||
unsigned long timeout;
|
||||
|
||||
if (boot_delay == 0 || system_state != SYSTEM_BOOTING)
|
||||
if ((boot_delay == 0 || system_state != SYSTEM_BOOTING)
|
||||
|| (level >= console_loglevel && !ignore_loglevel)) {
|
||||
return;
|
||||
}
|
||||
|
||||
k = (unsigned long long)loops_per_msec * boot_delay;
|
||||
|
||||
|
|
@ -789,7 +812,7 @@ static void boot_delay_msec(void)
|
|||
}
|
||||
}
|
||||
#else
|
||||
static inline void boot_delay_msec(void)
|
||||
static inline void boot_delay_msec(int level)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
|
@ -847,10 +870,11 @@ static size_t print_time(u64 ts, char *buf)
|
|||
if (!printk_time)
|
||||
return 0;
|
||||
|
||||
if (!buf)
|
||||
return 15;
|
||||
|
||||
rem_nsec = do_div(ts, 1000000000);
|
||||
|
||||
if (!buf)
|
||||
return snprintf(NULL, 0, "[%5lu.000000] ", (unsigned long)ts);
|
||||
|
||||
return sprintf(buf, "[%5lu.%06lu] ",
|
||||
(unsigned long)ts, rem_nsec / 1000);
|
||||
}
|
||||
|
|
@ -1232,21 +1256,6 @@ SYSCALL_DEFINE3(syslog, int, type, char __user *, buf, int, len)
|
|||
return do_syslog(type, buf, len, SYSLOG_FROM_CALL);
|
||||
}
|
||||
|
||||
static bool __read_mostly ignore_loglevel;
|
||||
|
||||
static int __init ignore_loglevel_setup(char *str)
|
||||
{
|
||||
ignore_loglevel = 1;
|
||||
printk(KERN_INFO "debug: ignoring loglevel setting.\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
early_param("ignore_loglevel", ignore_loglevel_setup);
|
||||
module_param(ignore_loglevel, bool, S_IRUGO | S_IWUSR);
|
||||
MODULE_PARM_DESC(ignore_loglevel, "ignore loglevel setting, to"
|
||||
"print all kernel messages to the console.");
|
||||
|
||||
/*
|
||||
* Call the console drivers, asking them to write out
|
||||
* log_buf[start] to log_buf[end - 1].
|
||||
|
|
@ -1492,7 +1501,7 @@ asmlinkage int vprintk_emit(int facility, int level,
|
|||
int this_cpu;
|
||||
int printed_len = 0;
|
||||
|
||||
boot_delay_msec();
|
||||
boot_delay_msec(level);
|
||||
printk_delay();
|
||||
|
||||
/* This stops the holder of console_sem just where we want him */
|
||||
|
|
@ -1908,12 +1917,14 @@ static int __cpuinit console_cpu_notify(struct notifier_block *self,
|
|||
*/
|
||||
void console_lock(void)
|
||||
{
|
||||
BUG_ON(in_interrupt());
|
||||
might_sleep();
|
||||
|
||||
down(&console_sem);
|
||||
if (console_suspended)
|
||||
return;
|
||||
console_locked = 1;
|
||||
console_may_schedule = 1;
|
||||
mutex_acquire(&console_lock_dep_map, 0, 0, _RET_IP_);
|
||||
}
|
||||
EXPORT_SYMBOL(console_lock);
|
||||
|
||||
|
|
@ -1935,6 +1946,7 @@ int console_trylock(void)
|
|||
}
|
||||
console_locked = 1;
|
||||
console_may_schedule = 0;
|
||||
mutex_acquire(&console_lock_dep_map, 0, 1, _RET_IP_);
|
||||
return 1;
|
||||
}
|
||||
EXPORT_SYMBOL(console_trylock);
|
||||
|
|
@ -2095,6 +2107,7 @@ skip:
|
|||
local_irq_restore(flags);
|
||||
}
|
||||
console_locked = 0;
|
||||
mutex_release(&console_lock_dep_map, 1, _RET_IP_);
|
||||
|
||||
/* Release the exclusive_console once it is used */
|
||||
if (unlikely(exclusive_console))
|
||||
|
|
|
|||
|
|
@ -8,9 +8,10 @@
|
|||
* Scheduler profiling support, Arjan van de Ven and Ingo Molnar,
|
||||
* Red Hat, July 2004
|
||||
* Consolidation of architecture support code for profiling,
|
||||
* William Irwin, Oracle, July 2004
|
||||
* Nadia Yvette Chambers, Oracle, July 2004
|
||||
* Amortized hit count accounting via per-cpu open-addressed hashtables
|
||||
* to resolve timer interrupt livelocks, William Irwin, Oracle, 2004
|
||||
* to resolve timer interrupt livelocks, Nadia Yvette Chambers,
|
||||
* Oracle, 2004
|
||||
*/
|
||||
|
||||
#include <linux/export.h>
|
||||
|
|
@ -256,7 +257,7 @@ EXPORT_SYMBOL_GPL(unregister_timer_hook);
|
|||
* pagetable hash functions, but uses a full hashtable full of finite
|
||||
* collision chains, not just pairs of them.
|
||||
*
|
||||
* -- wli
|
||||
* -- nyc
|
||||
*/
|
||||
static void __profile_flip_buffers(void *unused)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -215,8 +215,12 @@ ok:
|
|||
smp_rmb();
|
||||
if (task->mm)
|
||||
dumpable = get_dumpable(task->mm);
|
||||
if (!dumpable && !ptrace_has_cap(task_user_ns(task), mode))
|
||||
rcu_read_lock();
|
||||
if (!dumpable && !ptrace_has_cap(__task_cred(task)->user_ns, mode)) {
|
||||
rcu_read_unlock();
|
||||
return -EPERM;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
return security_ptrace_access_check(task, mode);
|
||||
}
|
||||
|
|
@ -280,8 +284,10 @@ static int ptrace_attach(struct task_struct *task, long request,
|
|||
|
||||
if (seize)
|
||||
flags |= PT_SEIZED;
|
||||
if (ns_capable(task_user_ns(task), CAP_SYS_PTRACE))
|
||||
rcu_read_lock();
|
||||
if (ns_capable(__task_cred(task)->user_ns, CAP_SYS_PTRACE))
|
||||
flags |= PT_PTRACE_CAP;
|
||||
rcu_read_unlock();
|
||||
task->ptrace = flags;
|
||||
|
||||
__ptrace_link(task, current);
|
||||
|
|
@ -457,6 +463,9 @@ void exit_ptrace(struct task_struct *tracer)
|
|||
return;
|
||||
|
||||
list_for_each_entry_safe(p, n, &tracer->ptraced, ptrace_entry) {
|
||||
if (unlikely(p->ptrace & PT_EXITKILL))
|
||||
send_sig_info(SIGKILL, SEND_SIG_FORCED, p);
|
||||
|
||||
if (__ptrace_detach(tracer, p))
|
||||
list_add(&p->ptrace_entry, &ptrace_dead);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -109,4 +109,6 @@ static inline bool __rcu_reclaim(char *rn, struct rcu_head *head)
|
|||
}
|
||||
}
|
||||
|
||||
extern int rcu_expedited;
|
||||
|
||||
#endif /* __LINUX_RCU_H */
|
||||
|
|
|
|||
|
|
@ -46,12 +46,15 @@
|
|||
#include <linux/export.h>
|
||||
#include <linux/hardirq.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include <trace/events/rcu.h>
|
||||
|
||||
#include "rcu.h"
|
||||
|
||||
module_param(rcu_expedited, int, 0);
|
||||
|
||||
#ifdef CONFIG_PREEMPT_RCU
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -195,7 +195,7 @@ EXPORT_SYMBOL(rcu_is_cpu_idle);
|
|||
*/
|
||||
int rcu_is_cpu_rrupt_from_idle(void)
|
||||
{
|
||||
return rcu_dynticks_nesting <= 0;
|
||||
return rcu_dynticks_nesting <= 1;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -706,7 +706,10 @@ void synchronize_rcu(void)
|
|||
return;
|
||||
|
||||
/* Once we get past the fastpath checks, same code as rcu_barrier(). */
|
||||
rcu_barrier();
|
||||
if (rcu_expedited)
|
||||
synchronize_rcu_expedited();
|
||||
else
|
||||
rcu_barrier();
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(synchronize_rcu);
|
||||
|
||||
|
|
|
|||
|
|
@ -339,7 +339,6 @@ rcu_stutter_wait(char *title)
|
|||
|
||||
struct rcu_torture_ops {
|
||||
void (*init)(void);
|
||||
void (*cleanup)(void);
|
||||
int (*readlock)(void);
|
||||
void (*read_delay)(struct rcu_random_state *rrsp);
|
||||
void (*readunlock)(int idx);
|
||||
|
|
@ -431,7 +430,6 @@ static void rcu_torture_deferred_free(struct rcu_torture *p)
|
|||
|
||||
static struct rcu_torture_ops rcu_ops = {
|
||||
.init = NULL,
|
||||
.cleanup = NULL,
|
||||
.readlock = rcu_torture_read_lock,
|
||||
.read_delay = rcu_read_delay,
|
||||
.readunlock = rcu_torture_read_unlock,
|
||||
|
|
@ -475,7 +473,6 @@ static void rcu_sync_torture_init(void)
|
|||
|
||||
static struct rcu_torture_ops rcu_sync_ops = {
|
||||
.init = rcu_sync_torture_init,
|
||||
.cleanup = NULL,
|
||||
.readlock = rcu_torture_read_lock,
|
||||
.read_delay = rcu_read_delay,
|
||||
.readunlock = rcu_torture_read_unlock,
|
||||
|
|
@ -493,7 +490,6 @@ static struct rcu_torture_ops rcu_sync_ops = {
|
|||
|
||||
static struct rcu_torture_ops rcu_expedited_ops = {
|
||||
.init = rcu_sync_torture_init,
|
||||
.cleanup = NULL,
|
||||
.readlock = rcu_torture_read_lock,
|
||||
.read_delay = rcu_read_delay, /* just reuse rcu's version. */
|
||||
.readunlock = rcu_torture_read_unlock,
|
||||
|
|
@ -536,7 +532,6 @@ static void rcu_bh_torture_deferred_free(struct rcu_torture *p)
|
|||
|
||||
static struct rcu_torture_ops rcu_bh_ops = {
|
||||
.init = NULL,
|
||||
.cleanup = NULL,
|
||||
.readlock = rcu_bh_torture_read_lock,
|
||||
.read_delay = rcu_read_delay, /* just reuse rcu's version. */
|
||||
.readunlock = rcu_bh_torture_read_unlock,
|
||||
|
|
@ -553,7 +548,6 @@ static struct rcu_torture_ops rcu_bh_ops = {
|
|||
|
||||
static struct rcu_torture_ops rcu_bh_sync_ops = {
|
||||
.init = rcu_sync_torture_init,
|
||||
.cleanup = NULL,
|
||||
.readlock = rcu_bh_torture_read_lock,
|
||||
.read_delay = rcu_read_delay, /* just reuse rcu's version. */
|
||||
.readunlock = rcu_bh_torture_read_unlock,
|
||||
|
|
@ -570,7 +564,6 @@ static struct rcu_torture_ops rcu_bh_sync_ops = {
|
|||
|
||||
static struct rcu_torture_ops rcu_bh_expedited_ops = {
|
||||
.init = rcu_sync_torture_init,
|
||||
.cleanup = NULL,
|
||||
.readlock = rcu_bh_torture_read_lock,
|
||||
.read_delay = rcu_read_delay, /* just reuse rcu's version. */
|
||||
.readunlock = rcu_bh_torture_read_unlock,
|
||||
|
|
@ -589,19 +582,7 @@ static struct rcu_torture_ops rcu_bh_expedited_ops = {
|
|||
* Definitions for srcu torture testing.
|
||||
*/
|
||||
|
||||
static struct srcu_struct srcu_ctl;
|
||||
|
||||
static void srcu_torture_init(void)
|
||||
{
|
||||
init_srcu_struct(&srcu_ctl);
|
||||
rcu_sync_torture_init();
|
||||
}
|
||||
|
||||
static void srcu_torture_cleanup(void)
|
||||
{
|
||||
synchronize_srcu(&srcu_ctl);
|
||||
cleanup_srcu_struct(&srcu_ctl);
|
||||
}
|
||||
DEFINE_STATIC_SRCU(srcu_ctl);
|
||||
|
||||
static int srcu_torture_read_lock(void) __acquires(&srcu_ctl)
|
||||
{
|
||||
|
|
@ -672,8 +653,7 @@ static int srcu_torture_stats(char *page)
|
|||
}
|
||||
|
||||
static struct rcu_torture_ops srcu_ops = {
|
||||
.init = srcu_torture_init,
|
||||
.cleanup = srcu_torture_cleanup,
|
||||
.init = rcu_sync_torture_init,
|
||||
.readlock = srcu_torture_read_lock,
|
||||
.read_delay = srcu_read_delay,
|
||||
.readunlock = srcu_torture_read_unlock,
|
||||
|
|
@ -687,8 +667,7 @@ static struct rcu_torture_ops srcu_ops = {
|
|||
};
|
||||
|
||||
static struct rcu_torture_ops srcu_sync_ops = {
|
||||
.init = srcu_torture_init,
|
||||
.cleanup = srcu_torture_cleanup,
|
||||
.init = rcu_sync_torture_init,
|
||||
.readlock = srcu_torture_read_lock,
|
||||
.read_delay = srcu_read_delay,
|
||||
.readunlock = srcu_torture_read_unlock,
|
||||
|
|
@ -712,8 +691,7 @@ static void srcu_torture_read_unlock_raw(int idx) __releases(&srcu_ctl)
|
|||
}
|
||||
|
||||
static struct rcu_torture_ops srcu_raw_ops = {
|
||||
.init = srcu_torture_init,
|
||||
.cleanup = srcu_torture_cleanup,
|
||||
.init = rcu_sync_torture_init,
|
||||
.readlock = srcu_torture_read_lock_raw,
|
||||
.read_delay = srcu_read_delay,
|
||||
.readunlock = srcu_torture_read_unlock_raw,
|
||||
|
|
@ -727,8 +705,7 @@ static struct rcu_torture_ops srcu_raw_ops = {
|
|||
};
|
||||
|
||||
static struct rcu_torture_ops srcu_raw_sync_ops = {
|
||||
.init = srcu_torture_init,
|
||||
.cleanup = srcu_torture_cleanup,
|
||||
.init = rcu_sync_torture_init,
|
||||
.readlock = srcu_torture_read_lock_raw,
|
||||
.read_delay = srcu_read_delay,
|
||||
.readunlock = srcu_torture_read_unlock_raw,
|
||||
|
|
@ -747,8 +724,7 @@ static void srcu_torture_synchronize_expedited(void)
|
|||
}
|
||||
|
||||
static struct rcu_torture_ops srcu_expedited_ops = {
|
||||
.init = srcu_torture_init,
|
||||
.cleanup = srcu_torture_cleanup,
|
||||
.init = rcu_sync_torture_init,
|
||||
.readlock = srcu_torture_read_lock,
|
||||
.read_delay = srcu_read_delay,
|
||||
.readunlock = srcu_torture_read_unlock,
|
||||
|
|
@ -783,7 +759,6 @@ static void rcu_sched_torture_deferred_free(struct rcu_torture *p)
|
|||
|
||||
static struct rcu_torture_ops sched_ops = {
|
||||
.init = rcu_sync_torture_init,
|
||||
.cleanup = NULL,
|
||||
.readlock = sched_torture_read_lock,
|
||||
.read_delay = rcu_read_delay, /* just reuse rcu's version. */
|
||||
.readunlock = sched_torture_read_unlock,
|
||||
|
|
@ -799,7 +774,6 @@ static struct rcu_torture_ops sched_ops = {
|
|||
|
||||
static struct rcu_torture_ops sched_sync_ops = {
|
||||
.init = rcu_sync_torture_init,
|
||||
.cleanup = NULL,
|
||||
.readlock = sched_torture_read_lock,
|
||||
.read_delay = rcu_read_delay, /* just reuse rcu's version. */
|
||||
.readunlock = sched_torture_read_unlock,
|
||||
|
|
@ -814,7 +788,6 @@ static struct rcu_torture_ops sched_sync_ops = {
|
|||
|
||||
static struct rcu_torture_ops sched_expedited_ops = {
|
||||
.init = rcu_sync_torture_init,
|
||||
.cleanup = NULL,
|
||||
.readlock = sched_torture_read_lock,
|
||||
.read_delay = rcu_read_delay, /* just reuse rcu's version. */
|
||||
.readunlock = sched_torture_read_unlock,
|
||||
|
|
@ -1396,12 +1369,16 @@ rcu_torture_print_module_parms(struct rcu_torture_ops *cur_ops, char *tag)
|
|||
"fqs_duration=%d fqs_holdoff=%d fqs_stutter=%d "
|
||||
"test_boost=%d/%d test_boost_interval=%d "
|
||||
"test_boost_duration=%d shutdown_secs=%d "
|
||||
"stall_cpu=%d stall_cpu_holdoff=%d "
|
||||
"n_barrier_cbs=%d "
|
||||
"onoff_interval=%d onoff_holdoff=%d\n",
|
||||
torture_type, tag, nrealreaders, nfakewriters,
|
||||
stat_interval, verbose, test_no_idle_hz, shuffle_interval,
|
||||
stutter, irqreader, fqs_duration, fqs_holdoff, fqs_stutter,
|
||||
test_boost, cur_ops->can_boost,
|
||||
test_boost_interval, test_boost_duration, shutdown_secs,
|
||||
stall_cpu, stall_cpu_holdoff,
|
||||
n_barrier_cbs,
|
||||
onoff_interval, onoff_holdoff);
|
||||
}
|
||||
|
||||
|
|
@ -1502,6 +1479,7 @@ rcu_torture_onoff(void *arg)
|
|||
unsigned long delta;
|
||||
int maxcpu = -1;
|
||||
DEFINE_RCU_RANDOM(rand);
|
||||
int ret;
|
||||
unsigned long starttime;
|
||||
|
||||
VERBOSE_PRINTK_STRING("rcu_torture_onoff task started");
|
||||
|
|
@ -1522,7 +1500,13 @@ rcu_torture_onoff(void *arg)
|
|||
torture_type, cpu);
|
||||
starttime = jiffies;
|
||||
n_offline_attempts++;
|
||||
if (cpu_down(cpu) == 0) {
|
||||
ret = cpu_down(cpu);
|
||||
if (ret) {
|
||||
if (verbose)
|
||||
pr_alert("%s" TORTURE_FLAG
|
||||
"rcu_torture_onoff task: offline %d failed: errno %d\n",
|
||||
torture_type, cpu, ret);
|
||||
} else {
|
||||
if (verbose)
|
||||
pr_alert("%s" TORTURE_FLAG
|
||||
"rcu_torture_onoff task: offlined %d\n",
|
||||
|
|
@ -1936,8 +1920,6 @@ rcu_torture_cleanup(void)
|
|||
|
||||
rcu_torture_stats_print(); /* -After- the stats thread is stopped! */
|
||||
|
||||
if (cur_ops->cleanup)
|
||||
cur_ops->cleanup();
|
||||
if (atomic_read(&n_rcu_torture_error) || n_rcu_torture_barrier_error)
|
||||
rcu_torture_print_module_parms(cur_ops, "End of test: FAILURE");
|
||||
else if (n_online_successes != n_online_attempts ||
|
||||
|
|
|
|||
347
kernel/rcutree.c
347
kernel/rcutree.c
|
|
@ -68,9 +68,9 @@ static struct lock_class_key rcu_fqs_class[RCU_NUM_LVLS];
|
|||
.level = { &sname##_state.node[0] }, \
|
||||
.call = cr, \
|
||||
.fqs_state = RCU_GP_IDLE, \
|
||||
.gpnum = -300, \
|
||||
.completed = -300, \
|
||||
.onofflock = __RAW_SPIN_LOCK_UNLOCKED(&sname##_state.onofflock), \
|
||||
.gpnum = 0UL - 300UL, \
|
||||
.completed = 0UL - 300UL, \
|
||||
.orphan_lock = __RAW_SPIN_LOCK_UNLOCKED(&sname##_state.orphan_lock), \
|
||||
.orphan_nxttail = &sname##_state.orphan_nxtlist, \
|
||||
.orphan_donetail = &sname##_state.orphan_donelist, \
|
||||
.barrier_mutex = __MUTEX_INITIALIZER(sname##_state.barrier_mutex), \
|
||||
|
|
@ -207,18 +207,15 @@ EXPORT_SYMBOL_GPL(rcu_note_context_switch);
|
|||
DEFINE_PER_CPU(struct rcu_dynticks, rcu_dynticks) = {
|
||||
.dynticks_nesting = DYNTICK_TASK_EXIT_IDLE,
|
||||
.dynticks = ATOMIC_INIT(1),
|
||||
#if defined(CONFIG_RCU_USER_QS) && !defined(CONFIG_RCU_USER_QS_FORCE)
|
||||
.ignore_user_qs = true,
|
||||
#endif
|
||||
};
|
||||
|
||||
static int blimit = 10; /* Maximum callbacks per rcu_do_batch. */
|
||||
static int qhimark = 10000; /* If this many pending, ignore blimit. */
|
||||
static int qlowmark = 100; /* Once only this many pending, use blimit. */
|
||||
static long blimit = 10; /* Maximum callbacks per rcu_do_batch. */
|
||||
static long qhimark = 10000; /* If this many pending, ignore blimit. */
|
||||
static long qlowmark = 100; /* Once only this many pending, use blimit. */
|
||||
|
||||
module_param(blimit, int, 0444);
|
||||
module_param(qhimark, int, 0444);
|
||||
module_param(qlowmark, int, 0444);
|
||||
module_param(blimit, long, 0444);
|
||||
module_param(qhimark, long, 0444);
|
||||
module_param(qlowmark, long, 0444);
|
||||
|
||||
int rcu_cpu_stall_suppress __read_mostly; /* 1 = suppress stall warnings. */
|
||||
int rcu_cpu_stall_timeout __read_mostly = CONFIG_RCU_CPU_STALL_TIMEOUT;
|
||||
|
|
@ -303,7 +300,8 @@ EXPORT_SYMBOL_GPL(rcu_sched_force_quiescent_state);
|
|||
static int
|
||||
cpu_has_callbacks_ready_to_invoke(struct rcu_data *rdp)
|
||||
{
|
||||
return &rdp->nxtlist != rdp->nxttail[RCU_DONE_TAIL];
|
||||
return &rdp->nxtlist != rdp->nxttail[RCU_DONE_TAIL] &&
|
||||
rdp->nxttail[RCU_DONE_TAIL] != NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -312,8 +310,11 @@ cpu_has_callbacks_ready_to_invoke(struct rcu_data *rdp)
|
|||
static int
|
||||
cpu_needs_another_gp(struct rcu_state *rsp, struct rcu_data *rdp)
|
||||
{
|
||||
return *rdp->nxttail[RCU_DONE_TAIL +
|
||||
ACCESS_ONCE(rsp->completed) != rdp->completed] &&
|
||||
struct rcu_head **ntp;
|
||||
|
||||
ntp = rdp->nxttail[RCU_DONE_TAIL +
|
||||
(ACCESS_ONCE(rsp->completed) != rdp->completed)];
|
||||
return rdp->nxttail[RCU_DONE_TAIL] && ntp && *ntp &&
|
||||
!rcu_gp_in_progress(rsp);
|
||||
}
|
||||
|
||||
|
|
@ -416,29 +417,7 @@ EXPORT_SYMBOL_GPL(rcu_idle_enter);
|
|||
*/
|
||||
void rcu_user_enter(void)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct rcu_dynticks *rdtp;
|
||||
|
||||
/*
|
||||
* Some contexts may involve an exception occuring in an irq,
|
||||
* leading to that nesting:
|
||||
* rcu_irq_enter() rcu_user_exit() rcu_user_exit() rcu_irq_exit()
|
||||
* This would mess up the dyntick_nesting count though. And rcu_irq_*()
|
||||
* helpers are enough to protect RCU uses inside the exception. So
|
||||
* just return immediately if we detect we are in an IRQ.
|
||||
*/
|
||||
if (in_interrupt())
|
||||
return;
|
||||
|
||||
WARN_ON_ONCE(!current->mm);
|
||||
|
||||
local_irq_save(flags);
|
||||
rdtp = &__get_cpu_var(rcu_dynticks);
|
||||
if (!rdtp->ignore_user_qs && !rdtp->in_user) {
|
||||
rdtp->in_user = true;
|
||||
rcu_eqs_enter(true);
|
||||
}
|
||||
local_irq_restore(flags);
|
||||
rcu_eqs_enter(1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -575,27 +554,7 @@ EXPORT_SYMBOL_GPL(rcu_idle_exit);
|
|||
*/
|
||||
void rcu_user_exit(void)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct rcu_dynticks *rdtp;
|
||||
|
||||
/*
|
||||
* Some contexts may involve an exception occuring in an irq,
|
||||
* leading to that nesting:
|
||||
* rcu_irq_enter() rcu_user_exit() rcu_user_exit() rcu_irq_exit()
|
||||
* This would mess up the dyntick_nesting count though. And rcu_irq_*()
|
||||
* helpers are enough to protect RCU uses inside the exception. So
|
||||
* just return immediately if we detect we are in an IRQ.
|
||||
*/
|
||||
if (in_interrupt())
|
||||
return;
|
||||
|
||||
local_irq_save(flags);
|
||||
rdtp = &__get_cpu_var(rcu_dynticks);
|
||||
if (rdtp->in_user) {
|
||||
rdtp->in_user = false;
|
||||
rcu_eqs_exit(true);
|
||||
}
|
||||
local_irq_restore(flags);
|
||||
rcu_eqs_exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -718,21 +677,6 @@ int rcu_is_cpu_idle(void)
|
|||
}
|
||||
EXPORT_SYMBOL(rcu_is_cpu_idle);
|
||||
|
||||
#ifdef CONFIG_RCU_USER_QS
|
||||
void rcu_user_hooks_switch(struct task_struct *prev,
|
||||
struct task_struct *next)
|
||||
{
|
||||
struct rcu_dynticks *rdtp;
|
||||
|
||||
/* Interrupts are disabled in context switch */
|
||||
rdtp = &__get_cpu_var(rcu_dynticks);
|
||||
if (!rdtp->ignore_user_qs) {
|
||||
clear_tsk_thread_flag(prev, TIF_NOHZ);
|
||||
set_tsk_thread_flag(next, TIF_NOHZ);
|
||||
}
|
||||
}
|
||||
#endif /* #ifdef CONFIG_RCU_USER_QS */
|
||||
|
||||
#if defined(CONFIG_PROVE_RCU) && defined(CONFIG_HOTPLUG_CPU)
|
||||
|
||||
/*
|
||||
|
|
@ -873,6 +817,29 @@ static void record_gp_stall_check_time(struct rcu_state *rsp)
|
|||
rsp->jiffies_stall = jiffies + jiffies_till_stall_check();
|
||||
}
|
||||
|
||||
/*
|
||||
* Dump stacks of all tasks running on stalled CPUs. This is a fallback
|
||||
* for architectures that do not implement trigger_all_cpu_backtrace().
|
||||
* The NMI-triggered stack traces are more accurate because they are
|
||||
* printed by the target CPU.
|
||||
*/
|
||||
static void rcu_dump_cpu_stacks(struct rcu_state *rsp)
|
||||
{
|
||||
int cpu;
|
||||
unsigned long flags;
|
||||
struct rcu_node *rnp;
|
||||
|
||||
rcu_for_each_leaf_node(rsp, rnp) {
|
||||
raw_spin_lock_irqsave(&rnp->lock, flags);
|
||||
if (rnp->qsmask != 0) {
|
||||
for (cpu = 0; cpu <= rnp->grphi - rnp->grplo; cpu++)
|
||||
if (rnp->qsmask & (1UL << cpu))
|
||||
dump_cpu_task(rnp->grplo + cpu);
|
||||
}
|
||||
raw_spin_unlock_irqrestore(&rnp->lock, flags);
|
||||
}
|
||||
}
|
||||
|
||||
static void print_other_cpu_stall(struct rcu_state *rsp)
|
||||
{
|
||||
int cpu;
|
||||
|
|
@ -880,6 +847,7 @@ static void print_other_cpu_stall(struct rcu_state *rsp)
|
|||
unsigned long flags;
|
||||
int ndetected = 0;
|
||||
struct rcu_node *rnp = rcu_get_root(rsp);
|
||||
long totqlen = 0;
|
||||
|
||||
/* Only let one CPU complain about others per time interval. */
|
||||
|
||||
|
|
@ -924,12 +892,15 @@ static void print_other_cpu_stall(struct rcu_state *rsp)
|
|||
raw_spin_unlock_irqrestore(&rnp->lock, flags);
|
||||
|
||||
print_cpu_stall_info_end();
|
||||
printk(KERN_CONT "(detected by %d, t=%ld jiffies)\n",
|
||||
smp_processor_id(), (long)(jiffies - rsp->gp_start));
|
||||
for_each_possible_cpu(cpu)
|
||||
totqlen += per_cpu_ptr(rsp->rda, cpu)->qlen;
|
||||
pr_cont("(detected by %d, t=%ld jiffies, g=%lu, c=%lu, q=%lu)\n",
|
||||
smp_processor_id(), (long)(jiffies - rsp->gp_start),
|
||||
rsp->gpnum, rsp->completed, totqlen);
|
||||
if (ndetected == 0)
|
||||
printk(KERN_ERR "INFO: Stall ended before state dump start\n");
|
||||
else if (!trigger_all_cpu_backtrace())
|
||||
dump_stack();
|
||||
rcu_dump_cpu_stacks(rsp);
|
||||
|
||||
/* Complain about tasks blocking the grace period. */
|
||||
|
||||
|
|
@ -940,8 +911,10 @@ static void print_other_cpu_stall(struct rcu_state *rsp)
|
|||
|
||||
static void print_cpu_stall(struct rcu_state *rsp)
|
||||
{
|
||||
int cpu;
|
||||
unsigned long flags;
|
||||
struct rcu_node *rnp = rcu_get_root(rsp);
|
||||
long totqlen = 0;
|
||||
|
||||
/*
|
||||
* OK, time to rat on ourselves...
|
||||
|
|
@ -952,7 +925,10 @@ static void print_cpu_stall(struct rcu_state *rsp)
|
|||
print_cpu_stall_info_begin();
|
||||
print_cpu_stall_info(rsp, smp_processor_id());
|
||||
print_cpu_stall_info_end();
|
||||
printk(KERN_CONT " (t=%lu jiffies)\n", jiffies - rsp->gp_start);
|
||||
for_each_possible_cpu(cpu)
|
||||
totqlen += per_cpu_ptr(rsp->rda, cpu)->qlen;
|
||||
pr_cont(" (t=%lu jiffies g=%lu c=%lu q=%lu)\n",
|
||||
jiffies - rsp->gp_start, rsp->gpnum, rsp->completed, totqlen);
|
||||
if (!trigger_all_cpu_backtrace())
|
||||
dump_stack();
|
||||
|
||||
|
|
@ -1091,6 +1067,7 @@ static void init_callback_list(struct rcu_data *rdp)
|
|||
rdp->nxtlist = NULL;
|
||||
for (i = 0; i < RCU_NEXT_SIZE; i++)
|
||||
rdp->nxttail[i] = &rdp->nxtlist;
|
||||
init_nocb_callback_list(rdp);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1404,15 +1381,37 @@ rcu_start_gp(struct rcu_state *rsp, unsigned long flags)
|
|||
!cpu_needs_another_gp(rsp, rdp)) {
|
||||
/*
|
||||
* Either we have not yet spawned the grace-period
|
||||
* task or this CPU does not need another grace period.
|
||||
* task, this CPU does not need another grace period,
|
||||
* or a grace period is already in progress.
|
||||
* Either way, don't start a new grace period.
|
||||
*/
|
||||
raw_spin_unlock_irqrestore(&rnp->lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Because there is no grace period in progress right now,
|
||||
* any callbacks we have up to this point will be satisfied
|
||||
* by the next grace period. So promote all callbacks to be
|
||||
* handled after the end of the next grace period. If the
|
||||
* CPU is not yet aware of the end of the previous grace period,
|
||||
* we need to allow for the callback advancement that will
|
||||
* occur when it does become aware. Deadlock prevents us from
|
||||
* making it aware at this point: We cannot acquire a leaf
|
||||
* rcu_node ->lock while holding the root rcu_node ->lock.
|
||||
*/
|
||||
rdp->nxttail[RCU_NEXT_READY_TAIL] = rdp->nxttail[RCU_NEXT_TAIL];
|
||||
if (rdp->completed == rsp->completed)
|
||||
rdp->nxttail[RCU_WAIT_TAIL] = rdp->nxttail[RCU_NEXT_TAIL];
|
||||
|
||||
rsp->gp_flags = RCU_GP_FLAG_INIT;
|
||||
raw_spin_unlock_irqrestore(&rnp->lock, flags);
|
||||
raw_spin_unlock(&rnp->lock); /* Interrupts remain disabled. */
|
||||
|
||||
/* Ensure that CPU is aware of completion of last grace period. */
|
||||
rcu_process_gp_end(rsp, rdp);
|
||||
local_irq_restore(flags);
|
||||
|
||||
/* Wake up rcu_gp_kthread() to start the grace period. */
|
||||
wake_up(&rsp->gp_wq);
|
||||
}
|
||||
|
||||
|
|
@ -1573,16 +1572,20 @@ rcu_check_quiescent_state(struct rcu_state *rsp, struct rcu_data *rdp)
|
|||
/*
|
||||
* Send the specified CPU's RCU callbacks to the orphanage. The
|
||||
* specified CPU must be offline, and the caller must hold the
|
||||
* ->onofflock.
|
||||
* ->orphan_lock.
|
||||
*/
|
||||
static void
|
||||
rcu_send_cbs_to_orphanage(int cpu, struct rcu_state *rsp,
|
||||
struct rcu_node *rnp, struct rcu_data *rdp)
|
||||
{
|
||||
/* No-CBs CPUs do not have orphanable callbacks. */
|
||||
if (is_nocb_cpu(rdp->cpu))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Orphan the callbacks. First adjust the counts. This is safe
|
||||
* because ->onofflock excludes _rcu_barrier()'s adoption of
|
||||
* the callbacks, thus no memory barrier is required.
|
||||
* because _rcu_barrier() excludes CPU-hotplug operations, so it
|
||||
* cannot be running now. Thus no memory barrier is required.
|
||||
*/
|
||||
if (rdp->nxtlist != NULL) {
|
||||
rsp->qlen_lazy += rdp->qlen_lazy;
|
||||
|
|
@ -1623,13 +1626,17 @@ rcu_send_cbs_to_orphanage(int cpu, struct rcu_state *rsp,
|
|||
|
||||
/*
|
||||
* Adopt the RCU callbacks from the specified rcu_state structure's
|
||||
* orphanage. The caller must hold the ->onofflock.
|
||||
* orphanage. The caller must hold the ->orphan_lock.
|
||||
*/
|
||||
static void rcu_adopt_orphan_cbs(struct rcu_state *rsp)
|
||||
{
|
||||
int i;
|
||||
struct rcu_data *rdp = __this_cpu_ptr(rsp->rda);
|
||||
|
||||
/* No-CBs CPUs are handled specially. */
|
||||
if (rcu_nocb_adopt_orphan_cbs(rsp, rdp))
|
||||
return;
|
||||
|
||||
/* Do the accounting first. */
|
||||
rdp->qlen_lazy += rsp->qlen_lazy;
|
||||
rdp->qlen += rsp->qlen;
|
||||
|
|
@ -1702,7 +1709,7 @@ static void rcu_cleanup_dead_cpu(int cpu, struct rcu_state *rsp)
|
|||
|
||||
/* Exclude any attempts to start a new grace period. */
|
||||
mutex_lock(&rsp->onoff_mutex);
|
||||
raw_spin_lock_irqsave(&rsp->onofflock, flags);
|
||||
raw_spin_lock_irqsave(&rsp->orphan_lock, flags);
|
||||
|
||||
/* Orphan the dead CPU's callbacks, and adopt them if appropriate. */
|
||||
rcu_send_cbs_to_orphanage(cpu, rsp, rnp, rdp);
|
||||
|
|
@ -1729,10 +1736,10 @@ static void rcu_cleanup_dead_cpu(int cpu, struct rcu_state *rsp)
|
|||
/*
|
||||
* We still hold the leaf rcu_node structure lock here, and
|
||||
* irqs are still disabled. The reason for this subterfuge is
|
||||
* because invoking rcu_report_unblock_qs_rnp() with ->onofflock
|
||||
* because invoking rcu_report_unblock_qs_rnp() with ->orphan_lock
|
||||
* held leads to deadlock.
|
||||
*/
|
||||
raw_spin_unlock(&rsp->onofflock); /* irqs remain disabled. */
|
||||
raw_spin_unlock(&rsp->orphan_lock); /* irqs remain disabled. */
|
||||
rnp = rdp->mynode;
|
||||
if (need_report & RCU_OFL_TASKS_NORM_GP)
|
||||
rcu_report_unblock_qs_rnp(rnp, flags);
|
||||
|
|
@ -1769,7 +1776,8 @@ static void rcu_do_batch(struct rcu_state *rsp, struct rcu_data *rdp)
|
|||
{
|
||||
unsigned long flags;
|
||||
struct rcu_head *next, *list, **tail;
|
||||
int bl, count, count_lazy, i;
|
||||
long bl, count, count_lazy;
|
||||
int i;
|
||||
|
||||
/* If no callbacks are ready, just return.*/
|
||||
if (!cpu_has_callbacks_ready_to_invoke(rdp)) {
|
||||
|
|
@ -2107,9 +2115,15 @@ static void __call_rcu_core(struct rcu_state *rsp, struct rcu_data *rdp,
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function for call_rcu() and friends. The cpu argument will
|
||||
* normally be -1, indicating "currently running CPU". It may specify
|
||||
* a CPU only if that CPU is a no-CBs CPU. Currently, only _rcu_barrier()
|
||||
* is expected to specify a CPU.
|
||||
*/
|
||||
static void
|
||||
__call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu),
|
||||
struct rcu_state *rsp, bool lazy)
|
||||
struct rcu_state *rsp, int cpu, bool lazy)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct rcu_data *rdp;
|
||||
|
|
@ -2129,9 +2143,14 @@ __call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu),
|
|||
rdp = this_cpu_ptr(rsp->rda);
|
||||
|
||||
/* Add the callback to our list. */
|
||||
if (unlikely(rdp->nxttail[RCU_NEXT_TAIL] == NULL)) {
|
||||
if (unlikely(rdp->nxttail[RCU_NEXT_TAIL] == NULL) || cpu != -1) {
|
||||
int offline;
|
||||
|
||||
if (cpu != -1)
|
||||
rdp = per_cpu_ptr(rsp->rda, cpu);
|
||||
offline = !__call_rcu_nocb(rdp, head, lazy);
|
||||
WARN_ON_ONCE(offline);
|
||||
/* _call_rcu() is illegal on offline CPU; leak the callback. */
|
||||
WARN_ON_ONCE(1);
|
||||
local_irq_restore(flags);
|
||||
return;
|
||||
}
|
||||
|
|
@ -2160,7 +2179,7 @@ __call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu),
|
|||
*/
|
||||
void call_rcu_sched(struct rcu_head *head, void (*func)(struct rcu_head *rcu))
|
||||
{
|
||||
__call_rcu(head, func, &rcu_sched_state, 0);
|
||||
__call_rcu(head, func, &rcu_sched_state, -1, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(call_rcu_sched);
|
||||
|
||||
|
|
@ -2169,7 +2188,7 @@ EXPORT_SYMBOL_GPL(call_rcu_sched);
|
|||
*/
|
||||
void call_rcu_bh(struct rcu_head *head, void (*func)(struct rcu_head *rcu))
|
||||
{
|
||||
__call_rcu(head, func, &rcu_bh_state, 0);
|
||||
__call_rcu(head, func, &rcu_bh_state, -1, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(call_rcu_bh);
|
||||
|
||||
|
|
@ -2205,10 +2224,28 @@ static inline int rcu_blocking_is_gp(void)
|
|||
* rcu_read_lock_sched().
|
||||
*
|
||||
* This means that all preempt_disable code sequences, including NMI and
|
||||
* hardware-interrupt handlers, in progress on entry will have completed
|
||||
* before this primitive returns. However, this does not guarantee that
|
||||
* softirq handlers will have completed, since in some kernels, these
|
||||
* handlers can run in process context, and can block.
|
||||
* non-threaded hardware-interrupt handlers, in progress on entry will
|
||||
* have completed before this primitive returns. However, this does not
|
||||
* guarantee that softirq handlers will have completed, since in some
|
||||
* kernels, these handlers can run in process context, and can block.
|
||||
*
|
||||
* Note that this guarantee implies further memory-ordering guarantees.
|
||||
* On systems with more than one CPU, when synchronize_sched() returns,
|
||||
* each CPU is guaranteed to have executed a full memory barrier since the
|
||||
* end of its last RCU-sched read-side critical section whose beginning
|
||||
* preceded the call to synchronize_sched(). In addition, each CPU having
|
||||
* an RCU read-side critical section that extends beyond the return from
|
||||
* synchronize_sched() is guaranteed to have executed a full memory barrier
|
||||
* after the beginning of synchronize_sched() and before the beginning of
|
||||
* that RCU read-side critical section. Note that these guarantees include
|
||||
* CPUs that are offline, idle, or executing in user mode, as well as CPUs
|
||||
* that are executing in the kernel.
|
||||
*
|
||||
* Furthermore, if CPU A invoked synchronize_sched(), which returned
|
||||
* to its caller on CPU B, then both CPU A and CPU B are guaranteed
|
||||
* to have executed a full memory barrier during the execution of
|
||||
* synchronize_sched() -- even if CPU A and CPU B are the same CPU (but
|
||||
* again only if the system has more than one CPU).
|
||||
*
|
||||
* This primitive provides the guarantees made by the (now removed)
|
||||
* synchronize_kernel() API. In contrast, synchronize_rcu() only
|
||||
|
|
@ -2224,7 +2261,10 @@ void synchronize_sched(void)
|
|||
"Illegal synchronize_sched() in RCU-sched read-side critical section");
|
||||
if (rcu_blocking_is_gp())
|
||||
return;
|
||||
wait_rcu_gp(call_rcu_sched);
|
||||
if (rcu_expedited)
|
||||
synchronize_sched_expedited();
|
||||
else
|
||||
wait_rcu_gp(call_rcu_sched);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(synchronize_sched);
|
||||
|
||||
|
|
@ -2236,6 +2276,9 @@ EXPORT_SYMBOL_GPL(synchronize_sched);
|
|||
* read-side critical sections have completed. RCU read-side critical
|
||||
* sections are delimited by rcu_read_lock_bh() and rcu_read_unlock_bh(),
|
||||
* and may be nested.
|
||||
*
|
||||
* See the description of synchronize_sched() for more detailed information
|
||||
* on memory ordering guarantees.
|
||||
*/
|
||||
void synchronize_rcu_bh(void)
|
||||
{
|
||||
|
|
@ -2245,13 +2288,13 @@ void synchronize_rcu_bh(void)
|
|||
"Illegal synchronize_rcu_bh() in RCU-bh read-side critical section");
|
||||
if (rcu_blocking_is_gp())
|
||||
return;
|
||||
wait_rcu_gp(call_rcu_bh);
|
||||
if (rcu_expedited)
|
||||
synchronize_rcu_bh_expedited();
|
||||
else
|
||||
wait_rcu_gp(call_rcu_bh);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(synchronize_rcu_bh);
|
||||
|
||||
static atomic_t sync_sched_expedited_started = ATOMIC_INIT(0);
|
||||
static atomic_t sync_sched_expedited_done = ATOMIC_INIT(0);
|
||||
|
||||
static int synchronize_sched_expedited_cpu_stop(void *data)
|
||||
{
|
||||
/*
|
||||
|
|
@ -2308,10 +2351,32 @@ static int synchronize_sched_expedited_cpu_stop(void *data)
|
|||
*/
|
||||
void synchronize_sched_expedited(void)
|
||||
{
|
||||
int firstsnap, s, snap, trycount = 0;
|
||||
long firstsnap, s, snap;
|
||||
int trycount = 0;
|
||||
struct rcu_state *rsp = &rcu_sched_state;
|
||||
|
||||
/* Note that atomic_inc_return() implies full memory barrier. */
|
||||
firstsnap = snap = atomic_inc_return(&sync_sched_expedited_started);
|
||||
/*
|
||||
* If we are in danger of counter wrap, just do synchronize_sched().
|
||||
* By allowing sync_sched_expedited_started to advance no more than
|
||||
* ULONG_MAX/8 ahead of sync_sched_expedited_done, we are ensuring
|
||||
* that more than 3.5 billion CPUs would be required to force a
|
||||
* counter wrap on a 32-bit system. Quite a few more CPUs would of
|
||||
* course be required on a 64-bit system.
|
||||
*/
|
||||
if (ULONG_CMP_GE((ulong)atomic_long_read(&rsp->expedited_start),
|
||||
(ulong)atomic_long_read(&rsp->expedited_done) +
|
||||
ULONG_MAX / 8)) {
|
||||
synchronize_sched();
|
||||
atomic_long_inc(&rsp->expedited_wrap);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Take a ticket. Note that atomic_inc_return() implies a
|
||||
* full memory barrier.
|
||||
*/
|
||||
snap = atomic_long_inc_return(&rsp->expedited_start);
|
||||
firstsnap = snap;
|
||||
get_online_cpus();
|
||||
WARN_ON_ONCE(cpu_is_offline(raw_smp_processor_id()));
|
||||
|
||||
|
|
@ -2323,48 +2388,65 @@ void synchronize_sched_expedited(void)
|
|||
synchronize_sched_expedited_cpu_stop,
|
||||
NULL) == -EAGAIN) {
|
||||
put_online_cpus();
|
||||
atomic_long_inc(&rsp->expedited_tryfail);
|
||||
|
||||
/* Check to see if someone else did our work for us. */
|
||||
s = atomic_long_read(&rsp->expedited_done);
|
||||
if (ULONG_CMP_GE((ulong)s, (ulong)firstsnap)) {
|
||||
/* ensure test happens before caller kfree */
|
||||
smp_mb__before_atomic_inc(); /* ^^^ */
|
||||
atomic_long_inc(&rsp->expedited_workdone1);
|
||||
return;
|
||||
}
|
||||
|
||||
/* No joy, try again later. Or just synchronize_sched(). */
|
||||
if (trycount++ < 10) {
|
||||
udelay(trycount * num_online_cpus());
|
||||
} else {
|
||||
synchronize_sched();
|
||||
wait_rcu_gp(call_rcu_sched);
|
||||
atomic_long_inc(&rsp->expedited_normal);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check to see if someone else did our work for us. */
|
||||
s = atomic_read(&sync_sched_expedited_done);
|
||||
if (UINT_CMP_GE((unsigned)s, (unsigned)firstsnap)) {
|
||||
smp_mb(); /* ensure test happens before caller kfree */
|
||||
/* Recheck to see if someone else did our work for us. */
|
||||
s = atomic_long_read(&rsp->expedited_done);
|
||||
if (ULONG_CMP_GE((ulong)s, (ulong)firstsnap)) {
|
||||
/* ensure test happens before caller kfree */
|
||||
smp_mb__before_atomic_inc(); /* ^^^ */
|
||||
atomic_long_inc(&rsp->expedited_workdone2);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Refetching sync_sched_expedited_started allows later
|
||||
* callers to piggyback on our grace period. We subtract
|
||||
* 1 to get the same token that the last incrementer got.
|
||||
* We retry after they started, so our grace period works
|
||||
* for them, and they started after our first try, so their
|
||||
* grace period works for us.
|
||||
* callers to piggyback on our grace period. We retry
|
||||
* after they started, so our grace period works for them,
|
||||
* and they started after our first try, so their grace
|
||||
* period works for us.
|
||||
*/
|
||||
get_online_cpus();
|
||||
snap = atomic_read(&sync_sched_expedited_started);
|
||||
snap = atomic_long_read(&rsp->expedited_start);
|
||||
smp_mb(); /* ensure read is before try_stop_cpus(). */
|
||||
}
|
||||
atomic_long_inc(&rsp->expedited_stoppedcpus);
|
||||
|
||||
/*
|
||||
* Everyone up to our most recent fetch is covered by our grace
|
||||
* period. Update the counter, but only if our work is still
|
||||
* relevant -- which it won't be if someone who started later
|
||||
* than we did beat us to the punch.
|
||||
* than we did already did their update.
|
||||
*/
|
||||
do {
|
||||
s = atomic_read(&sync_sched_expedited_done);
|
||||
if (UINT_CMP_GE((unsigned)s, (unsigned)snap)) {
|
||||
smp_mb(); /* ensure test happens before caller kfree */
|
||||
atomic_long_inc(&rsp->expedited_done_tries);
|
||||
s = atomic_long_read(&rsp->expedited_done);
|
||||
if (ULONG_CMP_GE((ulong)s, (ulong)snap)) {
|
||||
/* ensure test happens before caller kfree */
|
||||
smp_mb__before_atomic_inc(); /* ^^^ */
|
||||
atomic_long_inc(&rsp->expedited_done_lost);
|
||||
break;
|
||||
}
|
||||
} while (atomic_cmpxchg(&sync_sched_expedited_done, s, snap) != s);
|
||||
} while (atomic_long_cmpxchg(&rsp->expedited_done, s, snap) != s);
|
||||
atomic_long_inc(&rsp->expedited_done_exit);
|
||||
|
||||
put_online_cpus();
|
||||
}
|
||||
|
|
@ -2558,9 +2640,17 @@ static void _rcu_barrier(struct rcu_state *rsp)
|
|||
* When that callback is invoked, we will know that all of the
|
||||
* corresponding CPU's preceding callbacks have been invoked.
|
||||
*/
|
||||
for_each_online_cpu(cpu) {
|
||||
for_each_possible_cpu(cpu) {
|
||||
if (!cpu_online(cpu) && !is_nocb_cpu(cpu))
|
||||
continue;
|
||||
rdp = per_cpu_ptr(rsp->rda, cpu);
|
||||
if (ACCESS_ONCE(rdp->qlen)) {
|
||||
if (is_nocb_cpu(cpu)) {
|
||||
_rcu_barrier_trace(rsp, "OnlineNoCB", cpu,
|
||||
rsp->n_barrier_done);
|
||||
atomic_inc(&rsp->barrier_cpu_count);
|
||||
__call_rcu(&rdp->barrier_head, rcu_barrier_callback,
|
||||
rsp, cpu, 0);
|
||||
} else if (ACCESS_ONCE(rdp->qlen)) {
|
||||
_rcu_barrier_trace(rsp, "OnlineQ", cpu,
|
||||
rsp->n_barrier_done);
|
||||
smp_call_function_single(cpu, rcu_barrier_func, rsp, 1);
|
||||
|
|
@ -2634,6 +2724,7 @@ rcu_boot_init_percpu_data(int cpu, struct rcu_state *rsp)
|
|||
#endif
|
||||
rdp->cpu = cpu;
|
||||
rdp->rsp = rsp;
|
||||
rcu_boot_init_nocb_percpu_data(rdp);
|
||||
raw_spin_unlock_irqrestore(&rnp->lock, flags);
|
||||
}
|
||||
|
||||
|
|
@ -2715,6 +2806,7 @@ static int __cpuinit rcu_cpu_notify(struct notifier_block *self,
|
|||
struct rcu_data *rdp = per_cpu_ptr(rcu_state->rda, cpu);
|
||||
struct rcu_node *rnp = rdp->mynode;
|
||||
struct rcu_state *rsp;
|
||||
int ret = NOTIFY_OK;
|
||||
|
||||
trace_rcu_utilization("Start CPU hotplug");
|
||||
switch (action) {
|
||||
|
|
@ -2728,7 +2820,10 @@ static int __cpuinit rcu_cpu_notify(struct notifier_block *self,
|
|||
rcu_boost_kthread_setaffinity(rnp, -1);
|
||||
break;
|
||||
case CPU_DOWN_PREPARE:
|
||||
rcu_boost_kthread_setaffinity(rnp, cpu);
|
||||
if (nocb_cpu_expendable(cpu))
|
||||
rcu_boost_kthread_setaffinity(rnp, cpu);
|
||||
else
|
||||
ret = NOTIFY_BAD;
|
||||
break;
|
||||
case CPU_DYING:
|
||||
case CPU_DYING_FROZEN:
|
||||
|
|
@ -2752,7 +2847,7 @@ static int __cpuinit rcu_cpu_notify(struct notifier_block *self,
|
|||
break;
|
||||
}
|
||||
trace_rcu_utilization("End CPU hotplug");
|
||||
return NOTIFY_OK;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -2772,6 +2867,7 @@ static int __init rcu_spawn_gp_kthread(void)
|
|||
raw_spin_lock_irqsave(&rnp->lock, flags);
|
||||
rsp->gp_kthread = t;
|
||||
raw_spin_unlock_irqrestore(&rnp->lock, flags);
|
||||
rcu_spawn_nocb_kthreads(rsp);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -2967,6 +3063,7 @@ void __init rcu_init(void)
|
|||
rcu_init_one(&rcu_sched_state, &rcu_sched_data);
|
||||
rcu_init_one(&rcu_bh_state, &rcu_bh_data);
|
||||
__rcu_init_preempt();
|
||||
rcu_init_nocb();
|
||||
open_softirq(RCU_SOFTIRQ, rcu_process_callbacks);
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -287,6 +287,7 @@ struct rcu_data {
|
|||
long qlen_last_fqs_check;
|
||||
/* qlen at last check for QS forcing */
|
||||
unsigned long n_cbs_invoked; /* count of RCU cbs invoked. */
|
||||
unsigned long n_nocbs_invoked; /* count of no-CBs RCU cbs invoked. */
|
||||
unsigned long n_cbs_orphaned; /* RCU cbs orphaned by dying CPU */
|
||||
unsigned long n_cbs_adopted; /* RCU cbs adopted from dying CPU */
|
||||
unsigned long n_force_qs_snap;
|
||||
|
|
@ -317,6 +318,18 @@ struct rcu_data {
|
|||
struct rcu_head oom_head;
|
||||
#endif /* #ifdef CONFIG_RCU_FAST_NO_HZ */
|
||||
|
||||
/* 7) Callback offloading. */
|
||||
#ifdef CONFIG_RCU_NOCB_CPU
|
||||
struct rcu_head *nocb_head; /* CBs waiting for kthread. */
|
||||
struct rcu_head **nocb_tail;
|
||||
atomic_long_t nocb_q_count; /* # CBs waiting for kthread */
|
||||
atomic_long_t nocb_q_count_lazy; /* (approximate). */
|
||||
int nocb_p_count; /* # CBs being invoked by kthread */
|
||||
int nocb_p_count_lazy; /* (approximate). */
|
||||
wait_queue_head_t nocb_wq; /* For nocb kthreads to sleep on. */
|
||||
struct task_struct *nocb_kthread;
|
||||
#endif /* #ifdef CONFIG_RCU_NOCB_CPU */
|
||||
|
||||
int cpu;
|
||||
struct rcu_state *rsp;
|
||||
};
|
||||
|
|
@ -369,6 +382,12 @@ struct rcu_state {
|
|||
struct rcu_data __percpu *rda; /* pointer of percu rcu_data. */
|
||||
void (*call)(struct rcu_head *head, /* call_rcu() flavor. */
|
||||
void (*func)(struct rcu_head *head));
|
||||
#ifdef CONFIG_RCU_NOCB_CPU
|
||||
void (*call_remote)(struct rcu_head *head,
|
||||
void (*func)(struct rcu_head *head));
|
||||
/* call_rcu() flavor, but for */
|
||||
/* placing on remote CPU. */
|
||||
#endif /* #ifdef CONFIG_RCU_NOCB_CPU */
|
||||
|
||||
/* The following fields are guarded by the root rcu_node's lock. */
|
||||
|
||||
|
|
@ -383,9 +402,8 @@ struct rcu_state {
|
|||
|
||||
/* End of fields guarded by root rcu_node's lock. */
|
||||
|
||||
raw_spinlock_t onofflock ____cacheline_internodealigned_in_smp;
|
||||
/* exclude on/offline and */
|
||||
/* starting new GP. */
|
||||
raw_spinlock_t orphan_lock ____cacheline_internodealigned_in_smp;
|
||||
/* Protect following fields. */
|
||||
struct rcu_head *orphan_nxtlist; /* Orphaned callbacks that */
|
||||
/* need a grace period. */
|
||||
struct rcu_head **orphan_nxttail; /* Tail of above. */
|
||||
|
|
@ -394,7 +412,7 @@ struct rcu_state {
|
|||
struct rcu_head **orphan_donetail; /* Tail of above. */
|
||||
long qlen_lazy; /* Number of lazy callbacks. */
|
||||
long qlen; /* Total number of callbacks. */
|
||||
/* End of fields guarded by onofflock. */
|
||||
/* End of fields guarded by orphan_lock. */
|
||||
|
||||
struct mutex onoff_mutex; /* Coordinate hotplug & GPs. */
|
||||
|
||||
|
|
@ -405,6 +423,18 @@ struct rcu_state {
|
|||
/* _rcu_barrier(). */
|
||||
/* End of fields guarded by barrier_mutex. */
|
||||
|
||||
atomic_long_t expedited_start; /* Starting ticket. */
|
||||
atomic_long_t expedited_done; /* Done ticket. */
|
||||
atomic_long_t expedited_wrap; /* # near-wrap incidents. */
|
||||
atomic_long_t expedited_tryfail; /* # acquisition failures. */
|
||||
atomic_long_t expedited_workdone1; /* # done by others #1. */
|
||||
atomic_long_t expedited_workdone2; /* # done by others #2. */
|
||||
atomic_long_t expedited_normal; /* # fallbacks to normal. */
|
||||
atomic_long_t expedited_stoppedcpus; /* # successful stop_cpus. */
|
||||
atomic_long_t expedited_done_tries; /* # tries to update _done. */
|
||||
atomic_long_t expedited_done_lost; /* # times beaten to _done. */
|
||||
atomic_long_t expedited_done_exit; /* # times exited _done loop. */
|
||||
|
||||
unsigned long jiffies_force_qs; /* Time at which to invoke */
|
||||
/* force_quiescent_state(). */
|
||||
unsigned long n_force_qs; /* Number of calls to */
|
||||
|
|
@ -428,6 +458,8 @@ struct rcu_state {
|
|||
#define RCU_GP_FLAG_FQS 0x2 /* Need grace-period quiescent-state forcing. */
|
||||
|
||||
extern struct list_head rcu_struct_flavors;
|
||||
|
||||
/* Sequence through rcu_state structures for each RCU flavor. */
|
||||
#define for_each_rcu_flavor(rsp) \
|
||||
list_for_each_entry((rsp), &rcu_struct_flavors, flavors)
|
||||
|
||||
|
|
@ -504,5 +536,32 @@ static void print_cpu_stall_info(struct rcu_state *rsp, int cpu);
|
|||
static void print_cpu_stall_info_end(void);
|
||||
static void zero_cpu_stall_ticks(struct rcu_data *rdp);
|
||||
static void increment_cpu_stall_ticks(void);
|
||||
static bool is_nocb_cpu(int cpu);
|
||||
static bool __call_rcu_nocb(struct rcu_data *rdp, struct rcu_head *rhp,
|
||||
bool lazy);
|
||||
static bool rcu_nocb_adopt_orphan_cbs(struct rcu_state *rsp,
|
||||
struct rcu_data *rdp);
|
||||
static bool nocb_cpu_expendable(int cpu);
|
||||
static void rcu_boot_init_nocb_percpu_data(struct rcu_data *rdp);
|
||||
static void rcu_spawn_nocb_kthreads(struct rcu_state *rsp);
|
||||
static void init_nocb_callback_list(struct rcu_data *rdp);
|
||||
static void __init rcu_init_nocb(void);
|
||||
|
||||
#endif /* #ifndef RCU_TREE_NONCORE */
|
||||
|
||||
#ifdef CONFIG_RCU_TRACE
|
||||
#ifdef CONFIG_RCU_NOCB_CPU
|
||||
/* Sum up queue lengths for tracing. */
|
||||
static inline void rcu_nocb_q_lengths(struct rcu_data *rdp, long *ql, long *qll)
|
||||
{
|
||||
*ql = atomic_long_read(&rdp->nocb_q_count) + rdp->nocb_p_count;
|
||||
*qll = atomic_long_read(&rdp->nocb_q_count_lazy) + rdp->nocb_p_count_lazy;
|
||||
}
|
||||
#else /* #ifdef CONFIG_RCU_NOCB_CPU */
|
||||
static inline void rcu_nocb_q_lengths(struct rcu_data *rdp, long *ql, long *qll)
|
||||
{
|
||||
*ql = 0;
|
||||
*qll = 0;
|
||||
}
|
||||
#endif /* #else #ifdef CONFIG_RCU_NOCB_CPU */
|
||||
#endif /* #ifdef CONFIG_RCU_TRACE */
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/oom.h>
|
||||
#include <linux/smpboot.h>
|
||||
|
||||
|
|
@ -36,6 +37,14 @@
|
|||
#define RCU_BOOST_PRIO RCU_KTHREAD_PRIO
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_RCU_NOCB_CPU
|
||||
static cpumask_var_t rcu_nocb_mask; /* CPUs to have callbacks offloaded. */
|
||||
static bool have_rcu_nocb_mask; /* Was rcu_nocb_mask allocated? */
|
||||
static bool rcu_nocb_poll; /* Offload kthread are to poll. */
|
||||
module_param(rcu_nocb_poll, bool, 0444);
|
||||
static char __initdata nocb_buf[NR_CPUS * 5];
|
||||
#endif /* #ifdef CONFIG_RCU_NOCB_CPU */
|
||||
|
||||
/*
|
||||
* Check the RCU kernel configuration parameters and print informative
|
||||
* messages about anything out of the ordinary. If you like #ifdef, you
|
||||
|
|
@ -76,6 +85,18 @@ static void __init rcu_bootup_announce_oddness(void)
|
|||
printk(KERN_INFO "\tExperimental boot-time adjustment of leaf fanout to %d.\n", rcu_fanout_leaf);
|
||||
if (nr_cpu_ids != NR_CPUS)
|
||||
printk(KERN_INFO "\tRCU restricting CPUs from NR_CPUS=%d to nr_cpu_ids=%d.\n", NR_CPUS, nr_cpu_ids);
|
||||
#ifdef CONFIG_RCU_NOCB_CPU
|
||||
if (have_rcu_nocb_mask) {
|
||||
if (cpumask_test_cpu(0, rcu_nocb_mask)) {
|
||||
cpumask_clear_cpu(0, rcu_nocb_mask);
|
||||
pr_info("\tCPU 0: illegal no-CBs CPU (cleared).\n");
|
||||
}
|
||||
cpulist_scnprintf(nocb_buf, sizeof(nocb_buf), rcu_nocb_mask);
|
||||
pr_info("\tExperimental no-CBs CPUs: %s.\n", nocb_buf);
|
||||
if (rcu_nocb_poll)
|
||||
pr_info("\tExperimental polled no-CBs CPUs.\n");
|
||||
}
|
||||
#endif /* #ifdef CONFIG_RCU_NOCB_CPU */
|
||||
}
|
||||
|
||||
#ifdef CONFIG_TREE_PREEMPT_RCU
|
||||
|
|
@ -642,7 +663,7 @@ static void rcu_preempt_do_callbacks(void)
|
|||
*/
|
||||
void call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu))
|
||||
{
|
||||
__call_rcu(head, func, &rcu_preempt_state, 0);
|
||||
__call_rcu(head, func, &rcu_preempt_state, -1, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(call_rcu);
|
||||
|
||||
|
|
@ -656,7 +677,7 @@ EXPORT_SYMBOL_GPL(call_rcu);
|
|||
void kfree_call_rcu(struct rcu_head *head,
|
||||
void (*func)(struct rcu_head *rcu))
|
||||
{
|
||||
__call_rcu(head, func, &rcu_preempt_state, 1);
|
||||
__call_rcu(head, func, &rcu_preempt_state, -1, 1);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kfree_call_rcu);
|
||||
|
||||
|
|
@ -670,6 +691,9 @@ EXPORT_SYMBOL_GPL(kfree_call_rcu);
|
|||
* concurrently with new RCU read-side critical sections that began while
|
||||
* synchronize_rcu() was waiting. RCU read-side critical sections are
|
||||
* delimited by rcu_read_lock() and rcu_read_unlock(), and may be nested.
|
||||
*
|
||||
* See the description of synchronize_sched() for more detailed information
|
||||
* on memory ordering guarantees.
|
||||
*/
|
||||
void synchronize_rcu(void)
|
||||
{
|
||||
|
|
@ -679,7 +703,10 @@ void synchronize_rcu(void)
|
|||
"Illegal synchronize_rcu() in RCU read-side critical section");
|
||||
if (!rcu_scheduler_active)
|
||||
return;
|
||||
wait_rcu_gp(call_rcu);
|
||||
if (rcu_expedited)
|
||||
synchronize_rcu_expedited();
|
||||
else
|
||||
wait_rcu_gp(call_rcu);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(synchronize_rcu);
|
||||
|
||||
|
|
@ -757,7 +784,8 @@ static void rcu_report_exp_rnp(struct rcu_state *rsp, struct rcu_node *rnp,
|
|||
* grace period for the specified rcu_node structure. If there are no such
|
||||
* tasks, report it up the rcu_node hierarchy.
|
||||
*
|
||||
* Caller must hold sync_rcu_preempt_exp_mutex and rsp->onofflock.
|
||||
* Caller must hold sync_rcu_preempt_exp_mutex and must exclude
|
||||
* CPU hotplug operations.
|
||||
*/
|
||||
static void
|
||||
sync_rcu_preempt_exp_init(struct rcu_state *rsp, struct rcu_node *rnp)
|
||||
|
|
@ -831,7 +859,7 @@ void synchronize_rcu_expedited(void)
|
|||
udelay(trycount * num_online_cpus());
|
||||
} else {
|
||||
put_online_cpus();
|
||||
synchronize_rcu();
|
||||
wait_rcu_gp(call_rcu);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -875,6 +903,11 @@ EXPORT_SYMBOL_GPL(synchronize_rcu_expedited);
|
|||
|
||||
/**
|
||||
* rcu_barrier - Wait until all in-flight call_rcu() callbacks complete.
|
||||
*
|
||||
* Note that this primitive does not necessarily wait for an RCU grace period
|
||||
* to complete. For example, if there are no RCU callbacks queued anywhere
|
||||
* in the system, then rcu_barrier() is within its rights to return
|
||||
* immediately, without waiting for anything, much less an RCU grace period.
|
||||
*/
|
||||
void rcu_barrier(void)
|
||||
{
|
||||
|
|
@ -1013,7 +1046,7 @@ static void rcu_preempt_check_callbacks(int cpu)
|
|||
void kfree_call_rcu(struct rcu_head *head,
|
||||
void (*func)(struct rcu_head *rcu))
|
||||
{
|
||||
__call_rcu(head, func, &rcu_sched_state, 1);
|
||||
__call_rcu(head, func, &rcu_sched_state, -1, 1);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kfree_call_rcu);
|
||||
|
||||
|
|
@ -2092,3 +2125,373 @@ static void increment_cpu_stall_ticks(void)
|
|||
}
|
||||
|
||||
#endif /* #else #ifdef CONFIG_RCU_CPU_STALL_INFO */
|
||||
|
||||
#ifdef CONFIG_RCU_NOCB_CPU
|
||||
|
||||
/*
|
||||
* Offload callback processing from the boot-time-specified set of CPUs
|
||||
* specified by rcu_nocb_mask. For each CPU in the set, there is a
|
||||
* kthread created that pulls the callbacks from the corresponding CPU,
|
||||
* waits for a grace period to elapse, and invokes the callbacks.
|
||||
* The no-CBs CPUs do a wake_up() on their kthread when they insert
|
||||
* a callback into any empty list, unless the rcu_nocb_poll boot parameter
|
||||
* has been specified, in which case each kthread actively polls its
|
||||
* CPU. (Which isn't so great for energy efficiency, but which does
|
||||
* reduce RCU's overhead on that CPU.)
|
||||
*
|
||||
* This is intended to be used in conjunction with Frederic Weisbecker's
|
||||
* adaptive-idle work, which would seriously reduce OS jitter on CPUs
|
||||
* running CPU-bound user-mode computations.
|
||||
*
|
||||
* Offloading of callback processing could also in theory be used as
|
||||
* an energy-efficiency measure because CPUs with no RCU callbacks
|
||||
* queued are more aggressive about entering dyntick-idle mode.
|
||||
*/
|
||||
|
||||
|
||||
/* Parse the boot-time rcu_nocb_mask CPU list from the kernel parameters. */
|
||||
static int __init rcu_nocb_setup(char *str)
|
||||
{
|
||||
alloc_bootmem_cpumask_var(&rcu_nocb_mask);
|
||||
have_rcu_nocb_mask = true;
|
||||
cpulist_parse(str, rcu_nocb_mask);
|
||||
return 1;
|
||||
}
|
||||
__setup("rcu_nocbs=", rcu_nocb_setup);
|
||||
|
||||
/* Is the specified CPU a no-CPUs CPU? */
|
||||
static bool is_nocb_cpu(int cpu)
|
||||
{
|
||||
if (have_rcu_nocb_mask)
|
||||
return cpumask_test_cpu(cpu, rcu_nocb_mask);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enqueue the specified string of rcu_head structures onto the specified
|
||||
* CPU's no-CBs lists. The CPU is specified by rdp, the head of the
|
||||
* string by rhp, and the tail of the string by rhtp. The non-lazy/lazy
|
||||
* counts are supplied by rhcount and rhcount_lazy.
|
||||
*
|
||||
* If warranted, also wake up the kthread servicing this CPUs queues.
|
||||
*/
|
||||
static void __call_rcu_nocb_enqueue(struct rcu_data *rdp,
|
||||
struct rcu_head *rhp,
|
||||
struct rcu_head **rhtp,
|
||||
int rhcount, int rhcount_lazy)
|
||||
{
|
||||
int len;
|
||||
struct rcu_head **old_rhpp;
|
||||
struct task_struct *t;
|
||||
|
||||
/* Enqueue the callback on the nocb list and update counts. */
|
||||
old_rhpp = xchg(&rdp->nocb_tail, rhtp);
|
||||
ACCESS_ONCE(*old_rhpp) = rhp;
|
||||
atomic_long_add(rhcount, &rdp->nocb_q_count);
|
||||
atomic_long_add(rhcount_lazy, &rdp->nocb_q_count_lazy);
|
||||
|
||||
/* If we are not being polled and there is a kthread, awaken it ... */
|
||||
t = ACCESS_ONCE(rdp->nocb_kthread);
|
||||
if (rcu_nocb_poll | !t)
|
||||
return;
|
||||
len = atomic_long_read(&rdp->nocb_q_count);
|
||||
if (old_rhpp == &rdp->nocb_head) {
|
||||
wake_up(&rdp->nocb_wq); /* ... only if queue was empty ... */
|
||||
rdp->qlen_last_fqs_check = 0;
|
||||
} else if (len > rdp->qlen_last_fqs_check + qhimark) {
|
||||
wake_up_process(t); /* ... or if many callbacks queued. */
|
||||
rdp->qlen_last_fqs_check = LONG_MAX / 2;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is a helper for __call_rcu(), which invokes this when the normal
|
||||
* callback queue is inoperable. If this is not a no-CBs CPU, this
|
||||
* function returns failure back to __call_rcu(), which can complain
|
||||
* appropriately.
|
||||
*
|
||||
* Otherwise, this function queues the callback where the corresponding
|
||||
* "rcuo" kthread can find it.
|
||||
*/
|
||||
static bool __call_rcu_nocb(struct rcu_data *rdp, struct rcu_head *rhp,
|
||||
bool lazy)
|
||||
{
|
||||
|
||||
if (!is_nocb_cpu(rdp->cpu))
|
||||
return 0;
|
||||
__call_rcu_nocb_enqueue(rdp, rhp, &rhp->next, 1, lazy);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Adopt orphaned callbacks on a no-CBs CPU, or return 0 if this is
|
||||
* not a no-CBs CPU.
|
||||
*/
|
||||
static bool __maybe_unused rcu_nocb_adopt_orphan_cbs(struct rcu_state *rsp,
|
||||
struct rcu_data *rdp)
|
||||
{
|
||||
long ql = rsp->qlen;
|
||||
long qll = rsp->qlen_lazy;
|
||||
|
||||
/* If this is not a no-CBs CPU, tell the caller to do it the old way. */
|
||||
if (!is_nocb_cpu(smp_processor_id()))
|
||||
return 0;
|
||||
rsp->qlen = 0;
|
||||
rsp->qlen_lazy = 0;
|
||||
|
||||
/* First, enqueue the donelist, if any. This preserves CB ordering. */
|
||||
if (rsp->orphan_donelist != NULL) {
|
||||
__call_rcu_nocb_enqueue(rdp, rsp->orphan_donelist,
|
||||
rsp->orphan_donetail, ql, qll);
|
||||
ql = qll = 0;
|
||||
rsp->orphan_donelist = NULL;
|
||||
rsp->orphan_donetail = &rsp->orphan_donelist;
|
||||
}
|
||||
if (rsp->orphan_nxtlist != NULL) {
|
||||
__call_rcu_nocb_enqueue(rdp, rsp->orphan_nxtlist,
|
||||
rsp->orphan_nxttail, ql, qll);
|
||||
ql = qll = 0;
|
||||
rsp->orphan_nxtlist = NULL;
|
||||
rsp->orphan_nxttail = &rsp->orphan_nxtlist;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* There must be at least one non-no-CBs CPU in operation at any given
|
||||
* time, because no-CBs CPUs are not capable of initiating grace periods
|
||||
* independently. This function therefore complains if the specified
|
||||
* CPU is the last non-no-CBs CPU, allowing the CPU-hotplug system to
|
||||
* avoid offlining the last such CPU. (Recursion is a wonderful thing,
|
||||
* but you have to have a base case!)
|
||||
*/
|
||||
static bool nocb_cpu_expendable(int cpu)
|
||||
{
|
||||
cpumask_var_t non_nocb_cpus;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* If there are no no-CB CPUs or if this CPU is not a no-CB CPU,
|
||||
* then offlining this CPU is harmless. Let it happen.
|
||||
*/
|
||||
if (!have_rcu_nocb_mask || is_nocb_cpu(cpu))
|
||||
return 1;
|
||||
|
||||
/* If no memory, play it safe and keep the CPU around. */
|
||||
if (!alloc_cpumask_var(&non_nocb_cpus, GFP_NOIO))
|
||||
return 0;
|
||||
cpumask_andnot(non_nocb_cpus, cpu_online_mask, rcu_nocb_mask);
|
||||
cpumask_clear_cpu(cpu, non_nocb_cpus);
|
||||
ret = !cpumask_empty(non_nocb_cpus);
|
||||
free_cpumask_var(non_nocb_cpus);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper structure for remote registry of RCU callbacks.
|
||||
* This is needed for when a no-CBs CPU needs to start a grace period.
|
||||
* If it just invokes call_rcu(), the resulting callback will be queued,
|
||||
* which can result in deadlock.
|
||||
*/
|
||||
struct rcu_head_remote {
|
||||
struct rcu_head *rhp;
|
||||
call_rcu_func_t *crf;
|
||||
void (*func)(struct rcu_head *rhp);
|
||||
};
|
||||
|
||||
/*
|
||||
* Register a callback as specified by the rcu_head_remote struct.
|
||||
* This function is intended to be invoked via smp_call_function_single().
|
||||
*/
|
||||
static void call_rcu_local(void *arg)
|
||||
{
|
||||
struct rcu_head_remote *rhrp =
|
||||
container_of(arg, struct rcu_head_remote, rhp);
|
||||
|
||||
rhrp->crf(rhrp->rhp, rhrp->func);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up an rcu_head_remote structure and the invoke call_rcu_local()
|
||||
* on CPU 0 (which is guaranteed to be a non-no-CBs CPU) via
|
||||
* smp_call_function_single().
|
||||
*/
|
||||
static void invoke_crf_remote(struct rcu_head *rhp,
|
||||
void (*func)(struct rcu_head *rhp),
|
||||
call_rcu_func_t crf)
|
||||
{
|
||||
struct rcu_head_remote rhr;
|
||||
|
||||
rhr.rhp = rhp;
|
||||
rhr.crf = crf;
|
||||
rhr.func = func;
|
||||
smp_call_function_single(0, call_rcu_local, &rhr, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper functions to be passed to wait_rcu_gp(), each of which
|
||||
* invokes invoke_crf_remote() to register a callback appropriately.
|
||||
*/
|
||||
static void __maybe_unused
|
||||
call_rcu_preempt_remote(struct rcu_head *rhp,
|
||||
void (*func)(struct rcu_head *rhp))
|
||||
{
|
||||
invoke_crf_remote(rhp, func, call_rcu);
|
||||
}
|
||||
static void call_rcu_bh_remote(struct rcu_head *rhp,
|
||||
void (*func)(struct rcu_head *rhp))
|
||||
{
|
||||
invoke_crf_remote(rhp, func, call_rcu_bh);
|
||||
}
|
||||
static void call_rcu_sched_remote(struct rcu_head *rhp,
|
||||
void (*func)(struct rcu_head *rhp))
|
||||
{
|
||||
invoke_crf_remote(rhp, func, call_rcu_sched);
|
||||
}
|
||||
|
||||
/*
|
||||
* Per-rcu_data kthread, but only for no-CBs CPUs. Each kthread invokes
|
||||
* callbacks queued by the corresponding no-CBs CPU.
|
||||
*/
|
||||
static int rcu_nocb_kthread(void *arg)
|
||||
{
|
||||
int c, cl;
|
||||
struct rcu_head *list;
|
||||
struct rcu_head *next;
|
||||
struct rcu_head **tail;
|
||||
struct rcu_data *rdp = arg;
|
||||
|
||||
/* Each pass through this loop invokes one batch of callbacks */
|
||||
for (;;) {
|
||||
/* If not polling, wait for next batch of callbacks. */
|
||||
if (!rcu_nocb_poll)
|
||||
wait_event(rdp->nocb_wq, rdp->nocb_head);
|
||||
list = ACCESS_ONCE(rdp->nocb_head);
|
||||
if (!list) {
|
||||
schedule_timeout_interruptible(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract queued callbacks, update counts, and wait
|
||||
* for a grace period to elapse.
|
||||
*/
|
||||
ACCESS_ONCE(rdp->nocb_head) = NULL;
|
||||
tail = xchg(&rdp->nocb_tail, &rdp->nocb_head);
|
||||
c = atomic_long_xchg(&rdp->nocb_q_count, 0);
|
||||
cl = atomic_long_xchg(&rdp->nocb_q_count_lazy, 0);
|
||||
ACCESS_ONCE(rdp->nocb_p_count) += c;
|
||||
ACCESS_ONCE(rdp->nocb_p_count_lazy) += cl;
|
||||
wait_rcu_gp(rdp->rsp->call_remote);
|
||||
|
||||
/* Each pass through the following loop invokes a callback. */
|
||||
trace_rcu_batch_start(rdp->rsp->name, cl, c, -1);
|
||||
c = cl = 0;
|
||||
while (list) {
|
||||
next = list->next;
|
||||
/* Wait for enqueuing to complete, if needed. */
|
||||
while (next == NULL && &list->next != tail) {
|
||||
schedule_timeout_interruptible(1);
|
||||
next = list->next;
|
||||
}
|
||||
debug_rcu_head_unqueue(list);
|
||||
local_bh_disable();
|
||||
if (__rcu_reclaim(rdp->rsp->name, list))
|
||||
cl++;
|
||||
c++;
|
||||
local_bh_enable();
|
||||
list = next;
|
||||
}
|
||||
trace_rcu_batch_end(rdp->rsp->name, c, !!list, 0, 0, 1);
|
||||
ACCESS_ONCE(rdp->nocb_p_count) -= c;
|
||||
ACCESS_ONCE(rdp->nocb_p_count_lazy) -= cl;
|
||||
rdp->n_nocbs_invoked += c;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Initialize per-rcu_data variables for no-CBs CPUs. */
|
||||
static void __init rcu_boot_init_nocb_percpu_data(struct rcu_data *rdp)
|
||||
{
|
||||
rdp->nocb_tail = &rdp->nocb_head;
|
||||
init_waitqueue_head(&rdp->nocb_wq);
|
||||
}
|
||||
|
||||
/* Create a kthread for each RCU flavor for each no-CBs CPU. */
|
||||
static void __init rcu_spawn_nocb_kthreads(struct rcu_state *rsp)
|
||||
{
|
||||
int cpu;
|
||||
struct rcu_data *rdp;
|
||||
struct task_struct *t;
|
||||
|
||||
if (rcu_nocb_mask == NULL)
|
||||
return;
|
||||
for_each_cpu(cpu, rcu_nocb_mask) {
|
||||
rdp = per_cpu_ptr(rsp->rda, cpu);
|
||||
t = kthread_run(rcu_nocb_kthread, rdp, "rcuo%d", cpu);
|
||||
BUG_ON(IS_ERR(t));
|
||||
ACCESS_ONCE(rdp->nocb_kthread) = t;
|
||||
}
|
||||
}
|
||||
|
||||
/* Prevent __call_rcu() from enqueuing callbacks on no-CBs CPUs */
|
||||
static void init_nocb_callback_list(struct rcu_data *rdp)
|
||||
{
|
||||
if (rcu_nocb_mask == NULL ||
|
||||
!cpumask_test_cpu(rdp->cpu, rcu_nocb_mask))
|
||||
return;
|
||||
rdp->nxttail[RCU_NEXT_TAIL] = NULL;
|
||||
}
|
||||
|
||||
/* Initialize the ->call_remote fields in the rcu_state structures. */
|
||||
static void __init rcu_init_nocb(void)
|
||||
{
|
||||
#ifdef CONFIG_PREEMPT_RCU
|
||||
rcu_preempt_state.call_remote = call_rcu_preempt_remote;
|
||||
#endif /* #ifdef CONFIG_PREEMPT_RCU */
|
||||
rcu_bh_state.call_remote = call_rcu_bh_remote;
|
||||
rcu_sched_state.call_remote = call_rcu_sched_remote;
|
||||
}
|
||||
|
||||
#else /* #ifdef CONFIG_RCU_NOCB_CPU */
|
||||
|
||||
static bool is_nocb_cpu(int cpu)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool __call_rcu_nocb(struct rcu_data *rdp, struct rcu_head *rhp,
|
||||
bool lazy)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool __maybe_unused rcu_nocb_adopt_orphan_cbs(struct rcu_state *rsp,
|
||||
struct rcu_data *rdp)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool nocb_cpu_expendable(int cpu)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void __init rcu_boot_init_nocb_percpu_data(struct rcu_data *rdp)
|
||||
{
|
||||
}
|
||||
|
||||
static void __init rcu_spawn_nocb_kthreads(struct rcu_state *rsp)
|
||||
{
|
||||
}
|
||||
|
||||
static void init_nocb_callback_list(struct rcu_data *rdp)
|
||||
{
|
||||
}
|
||||
|
||||
static void __init rcu_init_nocb(void)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* #else #ifdef CONFIG_RCU_NOCB_CPU */
|
||||
|
|
|
|||
|
|
@ -46,29 +46,58 @@
|
|||
#define RCU_TREE_NONCORE
|
||||
#include "rcutree.h"
|
||||
|
||||
static int show_rcubarrier(struct seq_file *m, void *unused)
|
||||
{
|
||||
struct rcu_state *rsp;
|
||||
#define ulong2long(a) (*(long *)(&(a)))
|
||||
|
||||
for_each_rcu_flavor(rsp)
|
||||
seq_printf(m, "%s: bcc: %d nbd: %lu\n",
|
||||
rsp->name,
|
||||
atomic_read(&rsp->barrier_cpu_count),
|
||||
rsp->n_barrier_done);
|
||||
static int r_open(struct inode *inode, struct file *file,
|
||||
const struct seq_operations *op)
|
||||
{
|
||||
int ret = seq_open(file, op);
|
||||
if (!ret) {
|
||||
struct seq_file *m = (struct seq_file *)file->private_data;
|
||||
m->private = inode->i_private;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void *r_start(struct seq_file *m, loff_t *pos)
|
||||
{
|
||||
struct rcu_state *rsp = (struct rcu_state *)m->private;
|
||||
*pos = cpumask_next(*pos - 1, cpu_possible_mask);
|
||||
if ((*pos) < nr_cpu_ids)
|
||||
return per_cpu_ptr(rsp->rda, *pos);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *r_next(struct seq_file *m, void *v, loff_t *pos)
|
||||
{
|
||||
(*pos)++;
|
||||
return r_start(m, pos);
|
||||
}
|
||||
|
||||
static void r_stop(struct seq_file *m, void *v)
|
||||
{
|
||||
}
|
||||
|
||||
static int show_rcubarrier(struct seq_file *m, void *v)
|
||||
{
|
||||
struct rcu_state *rsp = (struct rcu_state *)m->private;
|
||||
seq_printf(m, "bcc: %d nbd: %lu\n",
|
||||
atomic_read(&rsp->barrier_cpu_count),
|
||||
rsp->n_barrier_done);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rcubarrier_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, show_rcubarrier, NULL);
|
||||
return single_open(file, show_rcubarrier, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations rcubarrier_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = rcubarrier_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
.llseek = no_llseek,
|
||||
.release = seq_release,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_RCU_BOOST
|
||||
|
|
@ -84,12 +113,14 @@ static char convert_kthread_status(unsigned int kthread_status)
|
|||
|
||||
static void print_one_rcu_data(struct seq_file *m, struct rcu_data *rdp)
|
||||
{
|
||||
long ql, qll;
|
||||
|
||||
if (!rdp->beenonline)
|
||||
return;
|
||||
seq_printf(m, "%3d%cc=%lu g=%lu pq=%d qp=%d",
|
||||
seq_printf(m, "%3d%cc=%ld g=%ld pq=%d qp=%d",
|
||||
rdp->cpu,
|
||||
cpu_is_offline(rdp->cpu) ? '!' : ' ',
|
||||
rdp->completed, rdp->gpnum,
|
||||
ulong2long(rdp->completed), ulong2long(rdp->gpnum),
|
||||
rdp->passed_quiesce, rdp->qs_pending);
|
||||
seq_printf(m, " dt=%d/%llx/%d df=%lu",
|
||||
atomic_read(&rdp->dynticks->dynticks),
|
||||
|
|
@ -97,8 +128,11 @@ static void print_one_rcu_data(struct seq_file *m, struct rcu_data *rdp)
|
|||
rdp->dynticks->dynticks_nmi_nesting,
|
||||
rdp->dynticks_fqs);
|
||||
seq_printf(m, " of=%lu", rdp->offline_fqs);
|
||||
rcu_nocb_q_lengths(rdp, &ql, &qll);
|
||||
qll += rdp->qlen_lazy;
|
||||
ql += rdp->qlen;
|
||||
seq_printf(m, " ql=%ld/%ld qs=%c%c%c%c",
|
||||
rdp->qlen_lazy, rdp->qlen,
|
||||
qll, ql,
|
||||
".N"[rdp->nxttail[RCU_NEXT_READY_TAIL] !=
|
||||
rdp->nxttail[RCU_NEXT_TAIL]],
|
||||
".R"[rdp->nxttail[RCU_WAIT_TAIL] !=
|
||||
|
|
@ -114,101 +148,67 @@ static void print_one_rcu_data(struct seq_file *m, struct rcu_data *rdp)
|
|||
per_cpu(rcu_cpu_kthread_loops, rdp->cpu) & 0xffff);
|
||||
#endif /* #ifdef CONFIG_RCU_BOOST */
|
||||
seq_printf(m, " b=%ld", rdp->blimit);
|
||||
seq_printf(m, " ci=%lu co=%lu ca=%lu\n",
|
||||
rdp->n_cbs_invoked, rdp->n_cbs_orphaned, rdp->n_cbs_adopted);
|
||||
seq_printf(m, " ci=%lu nci=%lu co=%lu ca=%lu\n",
|
||||
rdp->n_cbs_invoked, rdp->n_nocbs_invoked,
|
||||
rdp->n_cbs_orphaned, rdp->n_cbs_adopted);
|
||||
}
|
||||
|
||||
static int show_rcudata(struct seq_file *m, void *unused)
|
||||
static int show_rcudata(struct seq_file *m, void *v)
|
||||
{
|
||||
int cpu;
|
||||
struct rcu_state *rsp;
|
||||
|
||||
for_each_rcu_flavor(rsp) {
|
||||
seq_printf(m, "%s:\n", rsp->name);
|
||||
for_each_possible_cpu(cpu)
|
||||
print_one_rcu_data(m, per_cpu_ptr(rsp->rda, cpu));
|
||||
}
|
||||
print_one_rcu_data(m, (struct rcu_data *)v);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct seq_operations rcudate_op = {
|
||||
.start = r_start,
|
||||
.next = r_next,
|
||||
.stop = r_stop,
|
||||
.show = show_rcudata,
|
||||
};
|
||||
|
||||
static int rcudata_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, show_rcudata, NULL);
|
||||
return r_open(inode, file, &rcudate_op);
|
||||
}
|
||||
|
||||
static const struct file_operations rcudata_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = rcudata_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
.llseek = no_llseek,
|
||||
.release = seq_release,
|
||||
};
|
||||
|
||||
static void print_one_rcu_data_csv(struct seq_file *m, struct rcu_data *rdp)
|
||||
static int show_rcuexp(struct seq_file *m, void *v)
|
||||
{
|
||||
if (!rdp->beenonline)
|
||||
return;
|
||||
seq_printf(m, "%d,%s,%lu,%lu,%d,%d",
|
||||
rdp->cpu,
|
||||
cpu_is_offline(rdp->cpu) ? "\"N\"" : "\"Y\"",
|
||||
rdp->completed, rdp->gpnum,
|
||||
rdp->passed_quiesce, rdp->qs_pending);
|
||||
seq_printf(m, ",%d,%llx,%d,%lu",
|
||||
atomic_read(&rdp->dynticks->dynticks),
|
||||
rdp->dynticks->dynticks_nesting,
|
||||
rdp->dynticks->dynticks_nmi_nesting,
|
||||
rdp->dynticks_fqs);
|
||||
seq_printf(m, ",%lu", rdp->offline_fqs);
|
||||
seq_printf(m, ",%ld,%ld,\"%c%c%c%c\"", rdp->qlen_lazy, rdp->qlen,
|
||||
".N"[rdp->nxttail[RCU_NEXT_READY_TAIL] !=
|
||||
rdp->nxttail[RCU_NEXT_TAIL]],
|
||||
".R"[rdp->nxttail[RCU_WAIT_TAIL] !=
|
||||
rdp->nxttail[RCU_NEXT_READY_TAIL]],
|
||||
".W"[rdp->nxttail[RCU_DONE_TAIL] !=
|
||||
rdp->nxttail[RCU_WAIT_TAIL]],
|
||||
".D"[&rdp->nxtlist != rdp->nxttail[RCU_DONE_TAIL]]);
|
||||
#ifdef CONFIG_RCU_BOOST
|
||||
seq_printf(m, ",%d,\"%c\"",
|
||||
per_cpu(rcu_cpu_has_work, rdp->cpu),
|
||||
convert_kthread_status(per_cpu(rcu_cpu_kthread_status,
|
||||
rdp->cpu)));
|
||||
#endif /* #ifdef CONFIG_RCU_BOOST */
|
||||
seq_printf(m, ",%ld", rdp->blimit);
|
||||
seq_printf(m, ",%lu,%lu,%lu\n",
|
||||
rdp->n_cbs_invoked, rdp->n_cbs_orphaned, rdp->n_cbs_adopted);
|
||||
}
|
||||
struct rcu_state *rsp = (struct rcu_state *)m->private;
|
||||
|
||||
static int show_rcudata_csv(struct seq_file *m, void *unused)
|
||||
{
|
||||
int cpu;
|
||||
struct rcu_state *rsp;
|
||||
|
||||
seq_puts(m, "\"CPU\",\"Online?\",\"c\",\"g\",\"pq\",\"pq\",");
|
||||
seq_puts(m, "\"dt\",\"dt nesting\",\"dt NMI nesting\",\"df\",");
|
||||
seq_puts(m, "\"of\",\"qll\",\"ql\",\"qs\"");
|
||||
#ifdef CONFIG_RCU_BOOST
|
||||
seq_puts(m, "\"kt\",\"ktl\"");
|
||||
#endif /* #ifdef CONFIG_RCU_BOOST */
|
||||
seq_puts(m, ",\"b\",\"ci\",\"co\",\"ca\"\n");
|
||||
for_each_rcu_flavor(rsp) {
|
||||
seq_printf(m, "\"%s:\"\n", rsp->name);
|
||||
for_each_possible_cpu(cpu)
|
||||
print_one_rcu_data_csv(m, per_cpu_ptr(rsp->rda, cpu));
|
||||
}
|
||||
seq_printf(m, "s=%lu d=%lu w=%lu tf=%lu wd1=%lu wd2=%lu n=%lu sc=%lu dt=%lu dl=%lu dx=%lu\n",
|
||||
atomic_long_read(&rsp->expedited_start),
|
||||
atomic_long_read(&rsp->expedited_done),
|
||||
atomic_long_read(&rsp->expedited_wrap),
|
||||
atomic_long_read(&rsp->expedited_tryfail),
|
||||
atomic_long_read(&rsp->expedited_workdone1),
|
||||
atomic_long_read(&rsp->expedited_workdone2),
|
||||
atomic_long_read(&rsp->expedited_normal),
|
||||
atomic_long_read(&rsp->expedited_stoppedcpus),
|
||||
atomic_long_read(&rsp->expedited_done_tries),
|
||||
atomic_long_read(&rsp->expedited_done_lost),
|
||||
atomic_long_read(&rsp->expedited_done_exit));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rcudata_csv_open(struct inode *inode, struct file *file)
|
||||
static int rcuexp_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, show_rcudata_csv, NULL);
|
||||
return single_open(file, show_rcuexp, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations rcudata_csv_fops = {
|
||||
static const struct file_operations rcuexp_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = rcudata_csv_open,
|
||||
.open = rcuexp_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
.llseek = no_llseek,
|
||||
.release = seq_release,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_RCU_BOOST
|
||||
|
|
@ -254,27 +254,11 @@ static const struct file_operations rcu_node_boost_fops = {
|
|||
.owner = THIS_MODULE,
|
||||
.open = rcu_node_boost_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.llseek = no_llseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
/*
|
||||
* Create the rcuboost debugfs entry. Standard error return.
|
||||
*/
|
||||
static int rcu_boost_trace_create_file(struct dentry *rcudir)
|
||||
{
|
||||
return !debugfs_create_file("rcuboost", 0444, rcudir, NULL,
|
||||
&rcu_node_boost_fops);
|
||||
}
|
||||
|
||||
#else /* #ifdef CONFIG_RCU_BOOST */
|
||||
|
||||
static int rcu_boost_trace_create_file(struct dentry *rcudir)
|
||||
{
|
||||
return 0; /* There cannot be an error if we didn't create it! */
|
||||
}
|
||||
|
||||
#endif /* #else #ifdef CONFIG_RCU_BOOST */
|
||||
#endif /* #ifdef CONFIG_RCU_BOOST */
|
||||
|
||||
static void print_one_rcu_state(struct seq_file *m, struct rcu_state *rsp)
|
||||
{
|
||||
|
|
@ -283,8 +267,9 @@ static void print_one_rcu_state(struct seq_file *m, struct rcu_state *rsp)
|
|||
struct rcu_node *rnp;
|
||||
|
||||
gpnum = rsp->gpnum;
|
||||
seq_printf(m, "%s: c=%lu g=%lu s=%d jfq=%ld j=%x ",
|
||||
rsp->name, rsp->completed, gpnum, rsp->fqs_state,
|
||||
seq_printf(m, "c=%ld g=%ld s=%d jfq=%ld j=%x ",
|
||||
ulong2long(rsp->completed), ulong2long(gpnum),
|
||||
rsp->fqs_state,
|
||||
(long)(rsp->jiffies_force_qs - jiffies),
|
||||
(int)(jiffies & 0xffff));
|
||||
seq_printf(m, "nfqs=%lu/nfqsng=%lu(%lu) fqlh=%lu oqlen=%ld/%ld\n",
|
||||
|
|
@ -306,26 +291,24 @@ static void print_one_rcu_state(struct seq_file *m, struct rcu_state *rsp)
|
|||
seq_puts(m, "\n");
|
||||
}
|
||||
|
||||
static int show_rcuhier(struct seq_file *m, void *unused)
|
||||
static int show_rcuhier(struct seq_file *m, void *v)
|
||||
{
|
||||
struct rcu_state *rsp;
|
||||
|
||||
for_each_rcu_flavor(rsp)
|
||||
print_one_rcu_state(m, rsp);
|
||||
struct rcu_state *rsp = (struct rcu_state *)m->private;
|
||||
print_one_rcu_state(m, rsp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rcuhier_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, show_rcuhier, NULL);
|
||||
return single_open(file, show_rcuhier, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations rcuhier_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = rcuhier_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
.llseek = no_llseek,
|
||||
.release = seq_release,
|
||||
};
|
||||
|
||||
static void show_one_rcugp(struct seq_file *m, struct rcu_state *rsp)
|
||||
|
|
@ -338,42 +321,42 @@ static void show_one_rcugp(struct seq_file *m, struct rcu_state *rsp)
|
|||
struct rcu_node *rnp = &rsp->node[0];
|
||||
|
||||
raw_spin_lock_irqsave(&rnp->lock, flags);
|
||||
completed = rsp->completed;
|
||||
gpnum = rsp->gpnum;
|
||||
if (rsp->completed == rsp->gpnum)
|
||||
completed = ACCESS_ONCE(rsp->completed);
|
||||
gpnum = ACCESS_ONCE(rsp->gpnum);
|
||||
if (completed == gpnum)
|
||||
gpage = 0;
|
||||
else
|
||||
gpage = jiffies - rsp->gp_start;
|
||||
gpmax = rsp->gp_max;
|
||||
raw_spin_unlock_irqrestore(&rnp->lock, flags);
|
||||
seq_printf(m, "%s: completed=%ld gpnum=%lu age=%ld max=%ld\n",
|
||||
rsp->name, completed, gpnum, gpage, gpmax);
|
||||
seq_printf(m, "completed=%ld gpnum=%ld age=%ld max=%ld\n",
|
||||
ulong2long(completed), ulong2long(gpnum), gpage, gpmax);
|
||||
}
|
||||
|
||||
static int show_rcugp(struct seq_file *m, void *unused)
|
||||
static int show_rcugp(struct seq_file *m, void *v)
|
||||
{
|
||||
struct rcu_state *rsp;
|
||||
|
||||
for_each_rcu_flavor(rsp)
|
||||
show_one_rcugp(m, rsp);
|
||||
struct rcu_state *rsp = (struct rcu_state *)m->private;
|
||||
show_one_rcugp(m, rsp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rcugp_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, show_rcugp, NULL);
|
||||
return single_open(file, show_rcugp, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations rcugp_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = rcugp_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
.llseek = no_llseek,
|
||||
.release = seq_release,
|
||||
};
|
||||
|
||||
static void print_one_rcu_pending(struct seq_file *m, struct rcu_data *rdp)
|
||||
{
|
||||
if (!rdp->beenonline)
|
||||
return;
|
||||
seq_printf(m, "%3d%cnp=%ld ",
|
||||
rdp->cpu,
|
||||
cpu_is_offline(rdp->cpu) ? '!' : ' ',
|
||||
|
|
@ -389,34 +372,30 @@ static void print_one_rcu_pending(struct seq_file *m, struct rcu_data *rdp)
|
|||
rdp->n_rp_need_nothing);
|
||||
}
|
||||
|
||||
static int show_rcu_pending(struct seq_file *m, void *unused)
|
||||
static int show_rcu_pending(struct seq_file *m, void *v)
|
||||
{
|
||||
int cpu;
|
||||
struct rcu_data *rdp;
|
||||
struct rcu_state *rsp;
|
||||
|
||||
for_each_rcu_flavor(rsp) {
|
||||
seq_printf(m, "%s:\n", rsp->name);
|
||||
for_each_possible_cpu(cpu) {
|
||||
rdp = per_cpu_ptr(rsp->rda, cpu);
|
||||
if (rdp->beenonline)
|
||||
print_one_rcu_pending(m, rdp);
|
||||
}
|
||||
}
|
||||
print_one_rcu_pending(m, (struct rcu_data *)v);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct seq_operations rcu_pending_op = {
|
||||
.start = r_start,
|
||||
.next = r_next,
|
||||
.stop = r_stop,
|
||||
.show = show_rcu_pending,
|
||||
};
|
||||
|
||||
static int rcu_pending_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, show_rcu_pending, NULL);
|
||||
return r_open(inode, file, &rcu_pending_op);
|
||||
}
|
||||
|
||||
static const struct file_operations rcu_pending_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = rcu_pending_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
.llseek = no_llseek,
|
||||
.release = seq_release,
|
||||
};
|
||||
|
||||
static int show_rcutorture(struct seq_file *m, void *unused)
|
||||
|
|
@ -446,43 +425,58 @@ static struct dentry *rcudir;
|
|||
|
||||
static int __init rcutree_trace_init(void)
|
||||
{
|
||||
struct rcu_state *rsp;
|
||||
struct dentry *retval;
|
||||
struct dentry *rspdir;
|
||||
|
||||
rcudir = debugfs_create_dir("rcu", NULL);
|
||||
if (!rcudir)
|
||||
goto free_out;
|
||||
|
||||
retval = debugfs_create_file("rcubarrier", 0444, rcudir,
|
||||
NULL, &rcubarrier_fops);
|
||||
if (!retval)
|
||||
goto free_out;
|
||||
for_each_rcu_flavor(rsp) {
|
||||
rspdir = debugfs_create_dir(rsp->name, rcudir);
|
||||
if (!rspdir)
|
||||
goto free_out;
|
||||
|
||||
retval = debugfs_create_file("rcudata", 0444, rcudir,
|
||||
NULL, &rcudata_fops);
|
||||
if (!retval)
|
||||
goto free_out;
|
||||
retval = debugfs_create_file("rcudata", 0444,
|
||||
rspdir, rsp, &rcudata_fops);
|
||||
if (!retval)
|
||||
goto free_out;
|
||||
|
||||
retval = debugfs_create_file("rcudata.csv", 0444, rcudir,
|
||||
NULL, &rcudata_csv_fops);
|
||||
if (!retval)
|
||||
goto free_out;
|
||||
retval = debugfs_create_file("rcuexp", 0444,
|
||||
rspdir, rsp, &rcuexp_fops);
|
||||
if (!retval)
|
||||
goto free_out;
|
||||
|
||||
if (rcu_boost_trace_create_file(rcudir))
|
||||
goto free_out;
|
||||
retval = debugfs_create_file("rcu_pending", 0444,
|
||||
rspdir, rsp, &rcu_pending_fops);
|
||||
if (!retval)
|
||||
goto free_out;
|
||||
|
||||
retval = debugfs_create_file("rcugp", 0444, rcudir, NULL, &rcugp_fops);
|
||||
if (!retval)
|
||||
goto free_out;
|
||||
retval = debugfs_create_file("rcubarrier", 0444,
|
||||
rspdir, rsp, &rcubarrier_fops);
|
||||
if (!retval)
|
||||
goto free_out;
|
||||
|
||||
retval = debugfs_create_file("rcuhier", 0444, rcudir,
|
||||
NULL, &rcuhier_fops);
|
||||
if (!retval)
|
||||
goto free_out;
|
||||
#ifdef CONFIG_RCU_BOOST
|
||||
if (rsp == &rcu_preempt_state) {
|
||||
retval = debugfs_create_file("rcuboost", 0444,
|
||||
rspdir, NULL, &rcu_node_boost_fops);
|
||||
if (!retval)
|
||||
goto free_out;
|
||||
}
|
||||
#endif
|
||||
|
||||
retval = debugfs_create_file("rcu_pending", 0444, rcudir,
|
||||
NULL, &rcu_pending_fops);
|
||||
if (!retval)
|
||||
goto free_out;
|
||||
retval = debugfs_create_file("rcugp", 0444,
|
||||
rspdir, rsp, &rcugp_fops);
|
||||
if (!retval)
|
||||
goto free_out;
|
||||
|
||||
retval = debugfs_create_file("rcuhier", 0444,
|
||||
rspdir, rsp, &rcuhier_fops);
|
||||
if (!retval)
|
||||
goto free_out;
|
||||
}
|
||||
|
||||
retval = debugfs_create_file("rcutorture", 0444, rcudir,
|
||||
NULL, &rcutorture_fops);
|
||||
|
|
|
|||
|
|
@ -86,33 +86,39 @@ int res_counter_charge_nofail(struct res_counter *counter, unsigned long val,
|
|||
return __res_counter_charge(counter, val, limit_fail_at, true);
|
||||
}
|
||||
|
||||
void res_counter_uncharge_locked(struct res_counter *counter, unsigned long val)
|
||||
u64 res_counter_uncharge_locked(struct res_counter *counter, unsigned long val)
|
||||
{
|
||||
if (WARN_ON(counter->usage < val))
|
||||
val = counter->usage;
|
||||
|
||||
counter->usage -= val;
|
||||
return counter->usage;
|
||||
}
|
||||
|
||||
void res_counter_uncharge_until(struct res_counter *counter,
|
||||
struct res_counter *top,
|
||||
unsigned long val)
|
||||
u64 res_counter_uncharge_until(struct res_counter *counter,
|
||||
struct res_counter *top,
|
||||
unsigned long val)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct res_counter *c;
|
||||
u64 ret = 0;
|
||||
|
||||
local_irq_save(flags);
|
||||
for (c = counter; c != top; c = c->parent) {
|
||||
u64 r;
|
||||
spin_lock(&c->lock);
|
||||
res_counter_uncharge_locked(c, val);
|
||||
r = res_counter_uncharge_locked(c, val);
|
||||
if (c == counter)
|
||||
ret = r;
|
||||
spin_unlock(&c->lock);
|
||||
}
|
||||
local_irq_restore(flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void res_counter_uncharge(struct res_counter *counter, unsigned long val)
|
||||
u64 res_counter_uncharge(struct res_counter *counter, unsigned long val)
|
||||
{
|
||||
res_counter_uncharge_until(counter, NULL, val);
|
||||
return res_counter_uncharge_until(counter, NULL, val);
|
||||
}
|
||||
|
||||
static inline unsigned long long *
|
||||
|
|
@ -192,25 +198,3 @@ int res_counter_memparse_write_strategy(const char *buf,
|
|||
*res = PAGE_ALIGN(*res);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int res_counter_write(struct res_counter *counter, int member,
|
||||
const char *buf, write_strategy_fn write_strategy)
|
||||
{
|
||||
char *end;
|
||||
unsigned long flags;
|
||||
unsigned long long tmp, *val;
|
||||
|
||||
if (write_strategy) {
|
||||
if (write_strategy(buf, &tmp))
|
||||
return -EINVAL;
|
||||
} else {
|
||||
tmp = simple_strtoull(buf, &end, 10);
|
||||
if (*end != '\0')
|
||||
return -EINVAL;
|
||||
}
|
||||
spin_lock_irqsave(&counter->lock, flags);
|
||||
val = res_counter_member(counter, member);
|
||||
*val = tmp;
|
||||
spin_unlock_irqrestore(&counter->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -116,6 +116,16 @@ void down_read_nested(struct rw_semaphore *sem, int subclass)
|
|||
|
||||
EXPORT_SYMBOL(down_read_nested);
|
||||
|
||||
void _down_write_nest_lock(struct rw_semaphore *sem, struct lockdep_map *nest)
|
||||
{
|
||||
might_sleep();
|
||||
rwsem_acquire_nest(&sem->dep_map, 0, 0, nest, _RET_IP_);
|
||||
|
||||
LOCK_CONTENDED(sem, __down_write_trylock, __down_write);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(_down_write_nest_lock);
|
||||
|
||||
void down_write_nested(struct rw_semaphore *sem, int subclass)
|
||||
{
|
||||
might_sleep();
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/init_task.h>
|
||||
#include <linux/binfmts.h>
|
||||
#include <linux/context_tracking.h>
|
||||
|
||||
#include <asm/switch_to.h>
|
||||
#include <asm/tlb.h>
|
||||
|
|
@ -192,23 +193,10 @@ static void sched_feat_disable(int i) { };
|
|||
static void sched_feat_enable(int i) { };
|
||||
#endif /* HAVE_JUMP_LABEL */
|
||||
|
||||
static ssize_t
|
||||
sched_feat_write(struct file *filp, const char __user *ubuf,
|
||||
size_t cnt, loff_t *ppos)
|
||||
static int sched_feat_set(char *cmp)
|
||||
{
|
||||
char buf[64];
|
||||
char *cmp;
|
||||
int neg = 0;
|
||||
int i;
|
||||
|
||||
if (cnt > 63)
|
||||
cnt = 63;
|
||||
|
||||
if (copy_from_user(&buf, ubuf, cnt))
|
||||
return -EFAULT;
|
||||
|
||||
buf[cnt] = 0;
|
||||
cmp = strstrip(buf);
|
||||
int neg = 0;
|
||||
|
||||
if (strncmp(cmp, "NO_", 3) == 0) {
|
||||
neg = 1;
|
||||
|
|
@ -228,6 +216,27 @@ sched_feat_write(struct file *filp, const char __user *ubuf,
|
|||
}
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
sched_feat_write(struct file *filp, const char __user *ubuf,
|
||||
size_t cnt, loff_t *ppos)
|
||||
{
|
||||
char buf[64];
|
||||
char *cmp;
|
||||
int i;
|
||||
|
||||
if (cnt > 63)
|
||||
cnt = 63;
|
||||
|
||||
if (copy_from_user(&buf, ubuf, cnt))
|
||||
return -EFAULT;
|
||||
|
||||
buf[cnt] = 0;
|
||||
cmp = strstrip(buf);
|
||||
|
||||
i = sched_feat_set(cmp);
|
||||
if (i == __SCHED_FEAT_NR)
|
||||
return -EINVAL;
|
||||
|
||||
|
|
@ -922,6 +931,13 @@ void check_preempt_curr(struct rq *rq, struct task_struct *p, int flags)
|
|||
rq->skip_clock_update = 1;
|
||||
}
|
||||
|
||||
static ATOMIC_NOTIFIER_HEAD(task_migration_notifier);
|
||||
|
||||
void register_task_migration_notifier(struct notifier_block *n)
|
||||
{
|
||||
atomic_notifier_chain_register(&task_migration_notifier, n);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
void set_task_cpu(struct task_struct *p, unsigned int new_cpu)
|
||||
{
|
||||
|
|
@ -952,8 +968,18 @@ void set_task_cpu(struct task_struct *p, unsigned int new_cpu)
|
|||
trace_sched_migrate_task(p, new_cpu);
|
||||
|
||||
if (task_cpu(p) != new_cpu) {
|
||||
struct task_migration_notifier tmn;
|
||||
|
||||
if (p->sched_class->migrate_task_rq)
|
||||
p->sched_class->migrate_task_rq(p, new_cpu);
|
||||
p->se.nr_migrations++;
|
||||
perf_sw_event(PERF_COUNT_SW_CPU_MIGRATIONS, 1, NULL, 0);
|
||||
|
||||
tmn.task = p;
|
||||
tmn.from_cpu = task_cpu(p);
|
||||
tmn.to_cpu = new_cpu;
|
||||
|
||||
atomic_notifier_call_chain(&task_migration_notifier, 0, &tmn);
|
||||
}
|
||||
|
||||
__set_task_cpu(p, new_cpu);
|
||||
|
|
@ -1524,6 +1550,15 @@ static void __sched_fork(struct task_struct *p)
|
|||
p->se.vruntime = 0;
|
||||
INIT_LIST_HEAD(&p->se.group_node);
|
||||
|
||||
/*
|
||||
* Load-tracking only depends on SMP, FAIR_GROUP_SCHED dependency below may be
|
||||
* removed when useful for applications beyond shares distribution (e.g.
|
||||
* load-balance).
|
||||
*/
|
||||
#if defined(CONFIG_SMP) && defined(CONFIG_FAIR_GROUP_SCHED)
|
||||
p->se.avg.runnable_avg_period = 0;
|
||||
p->se.avg.runnable_avg_sum = 0;
|
||||
#endif
|
||||
#ifdef CONFIG_SCHEDSTATS
|
||||
memset(&p->se.statistics, 0, sizeof(p->se.statistics));
|
||||
#endif
|
||||
|
|
@ -1533,8 +1568,41 @@ static void __sched_fork(struct task_struct *p)
|
|||
#ifdef CONFIG_PREEMPT_NOTIFIERS
|
||||
INIT_HLIST_HEAD(&p->preempt_notifiers);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_NUMA_BALANCING
|
||||
if (p->mm && atomic_read(&p->mm->mm_users) == 1) {
|
||||
p->mm->numa_next_scan = jiffies;
|
||||
p->mm->numa_next_reset = jiffies;
|
||||
p->mm->numa_scan_seq = 0;
|
||||
}
|
||||
|
||||
p->node_stamp = 0ULL;
|
||||
p->numa_scan_seq = p->mm ? p->mm->numa_scan_seq : 0;
|
||||
p->numa_migrate_seq = p->mm ? p->mm->numa_scan_seq - 1 : 0;
|
||||
p->numa_scan_period = sysctl_numa_balancing_scan_delay;
|
||||
p->numa_work.next = &p->numa_work;
|
||||
#endif /* CONFIG_NUMA_BALANCING */
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NUMA_BALANCING
|
||||
#ifdef CONFIG_SCHED_DEBUG
|
||||
void set_numabalancing_state(bool enabled)
|
||||
{
|
||||
if (enabled)
|
||||
sched_feat_set("NUMA");
|
||||
else
|
||||
sched_feat_set("NO_NUMA");
|
||||
}
|
||||
#else
|
||||
__read_mostly bool numabalancing_enabled;
|
||||
|
||||
void set_numabalancing_state(bool enabled)
|
||||
{
|
||||
numabalancing_enabled = enabled;
|
||||
}
|
||||
#endif /* CONFIG_SCHED_DEBUG */
|
||||
#endif /* CONFIG_NUMA_BALANCING */
|
||||
|
||||
/*
|
||||
* fork()/clone()-time setup:
|
||||
*/
|
||||
|
|
@ -1886,8 +1954,8 @@ context_switch(struct rq *rq, struct task_struct *prev,
|
|||
spin_release(&rq->lock.dep_map, 1, _THIS_IP_);
|
||||
#endif
|
||||
|
||||
context_tracking_task_switch(prev, next);
|
||||
/* Here we just switch the register state and the stack. */
|
||||
rcu_switch(prev, next);
|
||||
switch_to(prev, next, prev);
|
||||
|
||||
barrier();
|
||||
|
|
@ -2911,7 +2979,7 @@ asmlinkage void __sched schedule(void)
|
|||
}
|
||||
EXPORT_SYMBOL(schedule);
|
||||
|
||||
#ifdef CONFIG_RCU_USER_QS
|
||||
#ifdef CONFIG_CONTEXT_TRACKING
|
||||
asmlinkage void __sched schedule_user(void)
|
||||
{
|
||||
/*
|
||||
|
|
@ -2920,9 +2988,9 @@ asmlinkage void __sched schedule_user(void)
|
|||
* we haven't yet exited the RCU idle mode. Do it here manually until
|
||||
* we find a better solution.
|
||||
*/
|
||||
rcu_user_exit();
|
||||
user_exit();
|
||||
schedule();
|
||||
rcu_user_enter();
|
||||
user_enter();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
@ -3027,7 +3095,7 @@ asmlinkage void __sched preempt_schedule_irq(void)
|
|||
/* Catch callers which need to be fixed */
|
||||
BUG_ON(ti->preempt_count || !irqs_disabled());
|
||||
|
||||
rcu_user_exit();
|
||||
user_exit();
|
||||
do {
|
||||
add_preempt_count(PREEMPT_ACTIVE);
|
||||
local_irq_enable();
|
||||
|
|
@ -4029,8 +4097,14 @@ long sched_setaffinity(pid_t pid, const struct cpumask *in_mask)
|
|||
goto out_free_cpus_allowed;
|
||||
}
|
||||
retval = -EPERM;
|
||||
if (!check_same_owner(p) && !ns_capable(task_user_ns(p), CAP_SYS_NICE))
|
||||
goto out_unlock;
|
||||
if (!check_same_owner(p)) {
|
||||
rcu_read_lock();
|
||||
if (!ns_capable(__task_cred(p)->user_ns, CAP_SYS_NICE)) {
|
||||
rcu_read_unlock();
|
||||
goto out_unlock;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
retval = security_task_setscheduler(p);
|
||||
if (retval)
|
||||
|
|
@ -4474,6 +4548,7 @@ static const char stat_nam[] = TASK_STATE_TO_CHAR_STR;
|
|||
void sched_show_task(struct task_struct *p)
|
||||
{
|
||||
unsigned long free = 0;
|
||||
int ppid;
|
||||
unsigned state;
|
||||
|
||||
state = p->state ? __ffs(p->state) + 1 : 0;
|
||||
|
|
@ -4493,8 +4568,11 @@ void sched_show_task(struct task_struct *p)
|
|||
#ifdef CONFIG_DEBUG_STACK_USAGE
|
||||
free = stack_not_used(p);
|
||||
#endif
|
||||
rcu_read_lock();
|
||||
ppid = task_pid_nr(rcu_dereference(p->real_parent));
|
||||
rcu_read_unlock();
|
||||
printk(KERN_CONT "%5lu %5d %6d 0x%08lx\n", free,
|
||||
task_pid_nr(p), task_pid_nr(rcu_dereference(p->real_parent)),
|
||||
task_pid_nr(p), ppid,
|
||||
(unsigned long)task_thread_info(p)->flags);
|
||||
|
||||
show_stack(p, NULL);
|
||||
|
|
@ -7468,7 +7546,7 @@ static inline struct task_group *cgroup_tg(struct cgroup *cgrp)
|
|||
struct task_group, css);
|
||||
}
|
||||
|
||||
static struct cgroup_subsys_state *cpu_cgroup_create(struct cgroup *cgrp)
|
||||
static struct cgroup_subsys_state *cpu_cgroup_css_alloc(struct cgroup *cgrp)
|
||||
{
|
||||
struct task_group *tg, *parent;
|
||||
|
||||
|
|
@ -7485,7 +7563,7 @@ static struct cgroup_subsys_state *cpu_cgroup_create(struct cgroup *cgrp)
|
|||
return &tg->css;
|
||||
}
|
||||
|
||||
static void cpu_cgroup_destroy(struct cgroup *cgrp)
|
||||
static void cpu_cgroup_css_free(struct cgroup *cgrp)
|
||||
{
|
||||
struct task_group *tg = cgroup_tg(cgrp);
|
||||
|
||||
|
|
@ -7845,8 +7923,8 @@ static struct cftype cpu_files[] = {
|
|||
|
||||
struct cgroup_subsys cpu_cgroup_subsys = {
|
||||
.name = "cpu",
|
||||
.create = cpu_cgroup_create,
|
||||
.destroy = cpu_cgroup_destroy,
|
||||
.css_alloc = cpu_cgroup_css_alloc,
|
||||
.css_free = cpu_cgroup_css_free,
|
||||
.can_attach = cpu_cgroup_can_attach,
|
||||
.attach = cpu_cgroup_attach,
|
||||
.exit = cpu_cgroup_exit,
|
||||
|
|
@ -7869,7 +7947,7 @@ struct cgroup_subsys cpu_cgroup_subsys = {
|
|||
struct cpuacct root_cpuacct;
|
||||
|
||||
/* create a new cpu accounting group */
|
||||
static struct cgroup_subsys_state *cpuacct_create(struct cgroup *cgrp)
|
||||
static struct cgroup_subsys_state *cpuacct_css_alloc(struct cgroup *cgrp)
|
||||
{
|
||||
struct cpuacct *ca;
|
||||
|
||||
|
|
@ -7899,7 +7977,7 @@ out:
|
|||
}
|
||||
|
||||
/* destroy an existing cpu accounting group */
|
||||
static void cpuacct_destroy(struct cgroup *cgrp)
|
||||
static void cpuacct_css_free(struct cgroup *cgrp)
|
||||
{
|
||||
struct cpuacct *ca = cgroup_ca(cgrp);
|
||||
|
||||
|
|
@ -8070,9 +8148,15 @@ void cpuacct_charge(struct task_struct *tsk, u64 cputime)
|
|||
|
||||
struct cgroup_subsys cpuacct_subsys = {
|
||||
.name = "cpuacct",
|
||||
.create = cpuacct_create,
|
||||
.destroy = cpuacct_destroy,
|
||||
.css_alloc = cpuacct_css_alloc,
|
||||
.css_free = cpuacct_css_free,
|
||||
.subsys_id = cpuacct_subsys_id,
|
||||
.base_cftypes = files,
|
||||
};
|
||||
#endif /* CONFIG_CGROUP_CPUACCT */
|
||||
|
||||
void dump_cpu_task(int cpu)
|
||||
{
|
||||
pr_info("Task dump for CPU %d:\n", cpu);
|
||||
sched_show_task(cpu_curr(cpu));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ DEFINE_PER_CPU(seqcount_t, irq_time_seq);
|
|||
* Called before incrementing preempt_count on {soft,}irq_enter
|
||||
* and before decrementing preempt_count on {soft,}irq_exit.
|
||||
*/
|
||||
void vtime_account(struct task_struct *curr)
|
||||
void irqtime_account_irq(struct task_struct *curr)
|
||||
{
|
||||
unsigned long flags;
|
||||
s64 delta;
|
||||
|
|
@ -73,7 +73,7 @@ void vtime_account(struct task_struct *curr)
|
|||
irq_time_write_end();
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(vtime_account);
|
||||
EXPORT_SYMBOL_GPL(irqtime_account_irq);
|
||||
|
||||
static int irqtime_account_hi_update(void)
|
||||
{
|
||||
|
|
@ -288,6 +288,34 @@ static __always_inline bool steal_account_process_tick(void)
|
|||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Accumulate raw cputime values of dead tasks (sig->[us]time) and live
|
||||
* tasks (sum on group iteration) belonging to @tsk's group.
|
||||
*/
|
||||
void thread_group_cputime(struct task_struct *tsk, struct task_cputime *times)
|
||||
{
|
||||
struct signal_struct *sig = tsk->signal;
|
||||
struct task_struct *t;
|
||||
|
||||
times->utime = sig->utime;
|
||||
times->stime = sig->stime;
|
||||
times->sum_exec_runtime = sig->sum_sched_runtime;
|
||||
|
||||
rcu_read_lock();
|
||||
/* make sure we can trust tsk->thread_group list */
|
||||
if (!likely(pid_alive(tsk)))
|
||||
goto out;
|
||||
|
||||
t = tsk;
|
||||
do {
|
||||
times->utime += t->utime;
|
||||
times->stime += t->stime;
|
||||
times->sum_exec_runtime += task_sched_runtime(t);
|
||||
} while_each_thread(tsk, t);
|
||||
out:
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
#ifndef CONFIG_VIRT_CPU_ACCOUNTING
|
||||
|
||||
#ifdef CONFIG_IRQ_TIME_ACCOUNTING
|
||||
|
|
@ -417,13 +445,13 @@ void account_idle_ticks(unsigned long ticks)
|
|||
* Use precise platform statistics if available:
|
||||
*/
|
||||
#ifdef CONFIG_VIRT_CPU_ACCOUNTING
|
||||
void task_times(struct task_struct *p, cputime_t *ut, cputime_t *st)
|
||||
void task_cputime_adjusted(struct task_struct *p, cputime_t *ut, cputime_t *st)
|
||||
{
|
||||
*ut = p->utime;
|
||||
*st = p->stime;
|
||||
}
|
||||
|
||||
void thread_group_times(struct task_struct *p, cputime_t *ut, cputime_t *st)
|
||||
void thread_group_cputime_adjusted(struct task_struct *p, cputime_t *ut, cputime_t *st)
|
||||
{
|
||||
struct task_cputime cputime;
|
||||
|
||||
|
|
@ -433,6 +461,29 @@ void thread_group_times(struct task_struct *p, cputime_t *ut, cputime_t *st)
|
|||
*st = cputime.stime;
|
||||
}
|
||||
|
||||
void vtime_account_system_irqsafe(struct task_struct *tsk)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
local_irq_save(flags);
|
||||
vtime_account_system(tsk);
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(vtime_account_system_irqsafe);
|
||||
|
||||
#ifndef __ARCH_HAS_VTIME_TASK_SWITCH
|
||||
void vtime_task_switch(struct task_struct *prev)
|
||||
{
|
||||
if (is_idle_task(prev))
|
||||
vtime_account_idle(prev);
|
||||
else
|
||||
vtime_account_system(prev);
|
||||
|
||||
vtime_account_user(prev);
|
||||
arch_vtime_task_switch(prev);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Archs that account the whole time spent in the idle task
|
||||
* (outside irq) as idle time can rely on this and just implement
|
||||
|
|
@ -444,16 +495,10 @@ void thread_group_times(struct task_struct *p, cputime_t *ut, cputime_t *st)
|
|||
#ifndef __ARCH_HAS_VTIME_ACCOUNT
|
||||
void vtime_account(struct task_struct *tsk)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
if (in_interrupt() || !is_idle_task(tsk))
|
||||
vtime_account_system(tsk);
|
||||
else
|
||||
vtime_account_idle(tsk);
|
||||
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(vtime_account);
|
||||
#endif /* __ARCH_HAS_VTIME_ACCOUNT */
|
||||
|
|
@ -478,14 +523,30 @@ static cputime_t scale_utime(cputime_t utime, cputime_t rtime, cputime_t total)
|
|||
return (__force cputime_t) temp;
|
||||
}
|
||||
|
||||
void task_times(struct task_struct *p, cputime_t *ut, cputime_t *st)
|
||||
/*
|
||||
* Adjust tick based cputime random precision against scheduler
|
||||
* runtime accounting.
|
||||
*/
|
||||
static void cputime_adjust(struct task_cputime *curr,
|
||||
struct cputime *prev,
|
||||
cputime_t *ut, cputime_t *st)
|
||||
{
|
||||
cputime_t rtime, utime = p->utime, total = utime + p->stime;
|
||||
cputime_t rtime, utime, total;
|
||||
|
||||
utime = curr->utime;
|
||||
total = utime + curr->stime;
|
||||
|
||||
/*
|
||||
* Use CFS's precise accounting:
|
||||
* Tick based cputime accounting depend on random scheduling
|
||||
* timeslices of a task to be interrupted or not by the timer.
|
||||
* Depending on these circumstances, the number of these interrupts
|
||||
* may be over or under-optimistic, matching the real user and system
|
||||
* cputime with a variable precision.
|
||||
*
|
||||
* Fix this by scaling these tick based values against the total
|
||||
* runtime accounted by the CFS scheduler.
|
||||
*/
|
||||
rtime = nsecs_to_cputime(p->se.sum_exec_runtime);
|
||||
rtime = nsecs_to_cputime(curr->sum_exec_runtime);
|
||||
|
||||
if (total)
|
||||
utime = scale_utime(utime, rtime, total);
|
||||
|
|
@ -493,38 +554,36 @@ void task_times(struct task_struct *p, cputime_t *ut, cputime_t *st)
|
|||
utime = rtime;
|
||||
|
||||
/*
|
||||
* Compare with previous values, to keep monotonicity:
|
||||
* If the tick based count grows faster than the scheduler one,
|
||||
* the result of the scaling may go backward.
|
||||
* Let's enforce monotonicity.
|
||||
*/
|
||||
p->prev_utime = max(p->prev_utime, utime);
|
||||
p->prev_stime = max(p->prev_stime, rtime - p->prev_utime);
|
||||
prev->utime = max(prev->utime, utime);
|
||||
prev->stime = max(prev->stime, rtime - prev->utime);
|
||||
|
||||
*ut = p->prev_utime;
|
||||
*st = p->prev_stime;
|
||||
*ut = prev->utime;
|
||||
*st = prev->stime;
|
||||
}
|
||||
|
||||
void task_cputime_adjusted(struct task_struct *p, cputime_t *ut, cputime_t *st)
|
||||
{
|
||||
struct task_cputime cputime = {
|
||||
.utime = p->utime,
|
||||
.stime = p->stime,
|
||||
.sum_exec_runtime = p->se.sum_exec_runtime,
|
||||
};
|
||||
|
||||
cputime_adjust(&cputime, &p->prev_cputime, ut, st);
|
||||
}
|
||||
|
||||
/*
|
||||
* Must be called with siglock held.
|
||||
*/
|
||||
void thread_group_times(struct task_struct *p, cputime_t *ut, cputime_t *st)
|
||||
void thread_group_cputime_adjusted(struct task_struct *p, cputime_t *ut, cputime_t *st)
|
||||
{
|
||||
struct signal_struct *sig = p->signal;
|
||||
struct task_cputime cputime;
|
||||
cputime_t rtime, utime, total;
|
||||
|
||||
thread_group_cputime(p, &cputime);
|
||||
|
||||
total = cputime.utime + cputime.stime;
|
||||
rtime = nsecs_to_cputime(cputime.sum_exec_runtime);
|
||||
|
||||
if (total)
|
||||
utime = scale_utime(cputime.utime, rtime, total);
|
||||
else
|
||||
utime = rtime;
|
||||
|
||||
sig->prev_utime = max(sig->prev_utime, utime);
|
||||
sig->prev_stime = max(sig->prev_stime, rtime - sig->prev_utime);
|
||||
|
||||
*ut = sig->prev_utime;
|
||||
*st = sig->prev_stime;
|
||||
cputime_adjust(&cputime, &p->signal->prev_cputime, ut, st);
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -61,14 +61,20 @@ static unsigned long nsec_low(unsigned long long nsec)
|
|||
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))
|
||||
|
||||
if (!se) {
|
||||
struct sched_avg *avg = &cpu_rq(cpu)->avg;
|
||||
P(avg->runnable_avg_sum);
|
||||
P(avg->runnable_avg_period);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
PN(se->exec_start);
|
||||
PN(se->vruntime);
|
||||
PN(se->sum_exec_runtime);
|
||||
|
|
@ -85,6 +91,12 @@ static void print_cfs_group_stats(struct seq_file *m, int cpu, struct task_group
|
|||
P(se->statistics.wait_count);
|
||||
#endif
|
||||
P(se->load.weight);
|
||||
#ifdef CONFIG_SMP
|
||||
P(se->avg.runnable_avg_sum);
|
||||
P(se->avg.runnable_avg_period);
|
||||
P(se->avg.load_avg_contrib);
|
||||
P(se->avg.decay_count);
|
||||
#endif
|
||||
#undef PN
|
||||
#undef P
|
||||
}
|
||||
|
|
@ -206,14 +218,18 @@ void print_cfs_rq(struct seq_file *m, int cpu, struct cfs_rq *cfs_rq)
|
|||
SEQ_printf(m, " .%-30s: %ld\n", "load", cfs_rq->load.weight);
|
||||
#ifdef CONFIG_FAIR_GROUP_SCHED
|
||||
#ifdef CONFIG_SMP
|
||||
SEQ_printf(m, " .%-30s: %Ld.%06ld\n", "load_avg",
|
||||
SPLIT_NS(cfs_rq->load_avg));
|
||||
SEQ_printf(m, " .%-30s: %Ld.%06ld\n", "load_period",
|
||||
SPLIT_NS(cfs_rq->load_period));
|
||||
SEQ_printf(m, " .%-30s: %ld\n", "load_contrib",
|
||||
cfs_rq->load_contribution);
|
||||
SEQ_printf(m, " .%-30s: %d\n", "load_tg",
|
||||
atomic_read(&cfs_rq->tg->load_weight));
|
||||
SEQ_printf(m, " .%-30s: %lld\n", "runnable_load_avg",
|
||||
cfs_rq->runnable_load_avg);
|
||||
SEQ_printf(m, " .%-30s: %lld\n", "blocked_load_avg",
|
||||
cfs_rq->blocked_load_avg);
|
||||
SEQ_printf(m, " .%-30s: %ld\n", "tg_load_avg",
|
||||
atomic64_read(&cfs_rq->tg->load_avg));
|
||||
SEQ_printf(m, " .%-30s: %lld\n", "tg_load_contrib",
|
||||
cfs_rq->tg_load_contrib);
|
||||
SEQ_printf(m, " .%-30s: %d\n", "tg_runnable_contrib",
|
||||
cfs_rq->tg_runnable_contrib);
|
||||
SEQ_printf(m, " .%-30s: %d\n", "tg->runnable_avg",
|
||||
atomic_read(&cfs_rq->tg->runnable_avg));
|
||||
#endif
|
||||
|
||||
print_cfs_group_stats(m, cpu, cfs_rq->tg);
|
||||
|
|
|
|||
1138
kernel/sched/fair.c
1138
kernel/sched/fair.c
File diff suppressed because it is too large
Load diff
|
|
@ -31,6 +31,11 @@ SCHED_FEAT(LAST_BUDDY, true)
|
|||
*/
|
||||
SCHED_FEAT(CACHE_HOT_BUDDY, true)
|
||||
|
||||
/*
|
||||
* Allow wakeup-time preemption of the current task:
|
||||
*/
|
||||
SCHED_FEAT(WAKEUP_PREEMPTION, true)
|
||||
|
||||
/*
|
||||
* Use arch dependent cpu power functions
|
||||
*/
|
||||
|
|
@ -61,3 +66,14 @@ SCHED_FEAT(TTWU_QUEUE, true)
|
|||
SCHED_FEAT(FORCE_SD_OVERLAP, false)
|
||||
SCHED_FEAT(RT_RUNTIME_SHARE, true)
|
||||
SCHED_FEAT(LB_MIN, false)
|
||||
|
||||
/*
|
||||
* Apply the automatic NUMA scheduling policy. Enabled automatically
|
||||
* at runtime if running on a NUMA machine. Can be controlled via
|
||||
* numa_balancing=. Allow PTE scanning to be forced on UMA machines
|
||||
* for debugging the core machinery.
|
||||
*/
|
||||
#ifdef CONFIG_NUMA_BALANCING
|
||||
SCHED_FEAT(NUMA, false)
|
||||
SCHED_FEAT(NUMA_FORCE, false)
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -112,6 +112,8 @@ struct task_group {
|
|||
unsigned long shares;
|
||||
|
||||
atomic_t load_weight;
|
||||
atomic64_t load_avg;
|
||||
atomic_t runnable_avg;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_RT_GROUP_SCHED
|
||||
|
|
@ -222,6 +224,38 @@ struct cfs_rq {
|
|||
unsigned int nr_spread_over;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
/*
|
||||
* Load-tracking only depends on SMP, FAIR_GROUP_SCHED dependency below may be
|
||||
* removed when useful for applications beyond shares distribution (e.g.
|
||||
* load-balance).
|
||||
*/
|
||||
#ifdef CONFIG_FAIR_GROUP_SCHED
|
||||
/*
|
||||
* CFS Load tracking
|
||||
* Under CFS, load is tracked on a per-entity basis and aggregated up.
|
||||
* This allows for the description of both thread and group usage (in
|
||||
* the FAIR_GROUP_SCHED case).
|
||||
*/
|
||||
u64 runnable_load_avg, blocked_load_avg;
|
||||
atomic64_t decay_counter, removed_load;
|
||||
u64 last_decay;
|
||||
#endif /* CONFIG_FAIR_GROUP_SCHED */
|
||||
/* These always depend on CONFIG_FAIR_GROUP_SCHED */
|
||||
#ifdef CONFIG_FAIR_GROUP_SCHED
|
||||
u32 tg_runnable_contrib;
|
||||
u64 tg_load_contrib;
|
||||
#endif /* CONFIG_FAIR_GROUP_SCHED */
|
||||
|
||||
/*
|
||||
* h_load = weight * f(tg)
|
||||
*
|
||||
* Where f(tg) is the recursive weight fraction assigned to
|
||||
* this group.
|
||||
*/
|
||||
unsigned long h_load;
|
||||
#endif /* CONFIG_SMP */
|
||||
|
||||
#ifdef CONFIG_FAIR_GROUP_SCHED
|
||||
struct rq *rq; /* cpu runqueue to which this cfs_rq is attached */
|
||||
|
||||
|
|
@ -237,34 +271,13 @@ struct cfs_rq {
|
|||
struct list_head leaf_cfs_rq_list;
|
||||
struct task_group *tg; /* group that "owns" this runqueue */
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
/*
|
||||
* h_load = weight * f(tg)
|
||||
*
|
||||
* Where f(tg) is the recursive weight fraction assigned to
|
||||
* this group.
|
||||
*/
|
||||
unsigned long h_load;
|
||||
|
||||
/*
|
||||
* Maintaining per-cpu shares distribution for group scheduling
|
||||
*
|
||||
* load_stamp is the last time we updated the load average
|
||||
* load_last is the last time we updated the load average and saw load
|
||||
* load_unacc_exec_time is currently unaccounted execution time
|
||||
*/
|
||||
u64 load_avg;
|
||||
u64 load_period;
|
||||
u64 load_stamp, load_last, load_unacc_exec_time;
|
||||
|
||||
unsigned long load_contribution;
|
||||
#endif /* CONFIG_SMP */
|
||||
#ifdef CONFIG_CFS_BANDWIDTH
|
||||
int runtime_enabled;
|
||||
u64 runtime_expires;
|
||||
s64 runtime_remaining;
|
||||
|
||||
u64 throttled_timestamp;
|
||||
u64 throttled_clock, throttled_clock_task;
|
||||
u64 throttled_clock_task_time;
|
||||
int throttled, throttle_count;
|
||||
struct list_head throttled_list;
|
||||
#endif /* CONFIG_CFS_BANDWIDTH */
|
||||
|
|
@ -467,6 +480,8 @@ struct rq {
|
|||
#ifdef CONFIG_SMP
|
||||
struct llist_head wake_list;
|
||||
#endif
|
||||
|
||||
struct sched_avg avg;
|
||||
};
|
||||
|
||||
static inline int cpu_of(struct rq *rq)
|
||||
|
|
@ -648,6 +663,18 @@ extern struct static_key sched_feat_keys[__SCHED_FEAT_NR];
|
|||
#define sched_feat(x) (sysctl_sched_features & (1UL << __SCHED_FEAT_##x))
|
||||
#endif /* SCHED_DEBUG && HAVE_JUMP_LABEL */
|
||||
|
||||
#ifdef CONFIG_NUMA_BALANCING
|
||||
#define sched_feat_numa(x) sched_feat(x)
|
||||
#ifdef CONFIG_SCHED_DEBUG
|
||||
#define numabalancing_enabled sched_feat_numa(NUMA)
|
||||
#else
|
||||
extern bool numabalancing_enabled;
|
||||
#endif /* CONFIG_SCHED_DEBUG */
|
||||
#else
|
||||
#define sched_feat_numa(x) (0)
|
||||
#define numabalancing_enabled (0)
|
||||
#endif /* CONFIG_NUMA_BALANCING */
|
||||
|
||||
static inline u64 global_rt_period(void)
|
||||
{
|
||||
return (u64)sysctl_sched_rt_period * NSEC_PER_USEC;
|
||||
|
|
@ -1212,4 +1239,3 @@ static inline u64 irq_time_read(int cpu)
|
|||
}
|
||||
#endif /* CONFIG_64BIT */
|
||||
#endif /* CONFIG_IRQ_TIME_ACCOUNTING */
|
||||
|
||||
|
|
|
|||
|
|
@ -396,25 +396,29 @@ int __secure_computing(int this_syscall)
|
|||
#ifdef CONFIG_SECCOMP_FILTER
|
||||
case SECCOMP_MODE_FILTER: {
|
||||
int data;
|
||||
struct pt_regs *regs = task_pt_regs(current);
|
||||
ret = seccomp_run_filters(this_syscall);
|
||||
data = ret & SECCOMP_RET_DATA;
|
||||
ret &= SECCOMP_RET_ACTION;
|
||||
switch (ret) {
|
||||
case SECCOMP_RET_ERRNO:
|
||||
/* Set the low-order 16-bits as a errno. */
|
||||
syscall_set_return_value(current, task_pt_regs(current),
|
||||
syscall_set_return_value(current, regs,
|
||||
-data, 0);
|
||||
goto skip;
|
||||
case SECCOMP_RET_TRAP:
|
||||
/* Show the handler the original registers. */
|
||||
syscall_rollback(current, task_pt_regs(current));
|
||||
syscall_rollback(current, regs);
|
||||
/* Let the filter pass back 16 bits of data. */
|
||||
seccomp_send_sigsys(this_syscall, data);
|
||||
goto skip;
|
||||
case SECCOMP_RET_TRACE:
|
||||
/* Skip these calls if there is no tracer. */
|
||||
if (!ptrace_event_enabled(current, PTRACE_EVENT_SECCOMP))
|
||||
if (!ptrace_event_enabled(current, PTRACE_EVENT_SECCOMP)) {
|
||||
syscall_set_return_value(current, regs,
|
||||
-ENOSYS, 0);
|
||||
goto skip;
|
||||
}
|
||||
/* Allow the BPF to provide the event message */
|
||||
ptrace_event(PTRACE_EVENT_SECCOMP, data);
|
||||
/*
|
||||
|
|
@ -425,6 +429,9 @@ int __secure_computing(int this_syscall)
|
|||
*/
|
||||
if (fatal_signal_pending(current))
|
||||
break;
|
||||
if (syscall_get_nr(current, regs) < 0)
|
||||
goto skip; /* Explicit request to skip. */
|
||||
|
||||
return 0;
|
||||
case SECCOMP_RET_ALLOW:
|
||||
return 0;
|
||||
|
|
|
|||
120
kernel/signal.c
120
kernel/signal.c
|
|
@ -31,6 +31,7 @@
|
|||
#include <linux/nsproxy.h>
|
||||
#include <linux/user_namespace.h>
|
||||
#include <linux/uprobes.h>
|
||||
#include <linux/compat.h>
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include <trace/events/signal.h>
|
||||
|
||||
|
|
@ -1159,8 +1160,9 @@ static int send_signal(int sig, struct siginfo *info, struct task_struct *t,
|
|||
return __send_signal(sig, info, t, group, from_ancestor_ns);
|
||||
}
|
||||
|
||||
static void print_fatal_signal(struct pt_regs *regs, int signr)
|
||||
static void print_fatal_signal(int signr)
|
||||
{
|
||||
struct pt_regs *regs = signal_pt_regs();
|
||||
printk("%s/%d: potentially unexpected fatal signal %d.\n",
|
||||
current->comm, task_pid_nr(current), signr);
|
||||
|
||||
|
|
@ -1752,7 +1754,7 @@ static void do_notify_parent_cldstop(struct task_struct *tsk,
|
|||
* see comment in do_notify_parent() about the following 4 lines
|
||||
*/
|
||||
rcu_read_lock();
|
||||
info.si_pid = task_pid_nr_ns(tsk, parent->nsproxy->pid_ns);
|
||||
info.si_pid = task_pid_nr_ns(tsk, task_active_pid_ns(parent));
|
||||
info.si_uid = from_kuid_munged(task_cred_xxx(parent, user_ns), task_uid(tsk));
|
||||
rcu_read_unlock();
|
||||
|
||||
|
|
@ -1908,7 +1910,7 @@ static void ptrace_stop(int exit_code, int why, int clear_code, siginfo_t *info)
|
|||
preempt_disable();
|
||||
read_unlock(&tasklist_lock);
|
||||
preempt_enable_no_resched();
|
||||
schedule();
|
||||
freezable_schedule();
|
||||
} else {
|
||||
/*
|
||||
* By the time we got the lock, our tracer went away.
|
||||
|
|
@ -1929,13 +1931,6 @@ static void ptrace_stop(int exit_code, int why, int clear_code, siginfo_t *info)
|
|||
read_unlock(&tasklist_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* While in TASK_TRACED, we were considered "frozen enough".
|
||||
* Now that we woke up, it's crucial if we're supposed to be
|
||||
* frozen that we freeze now before running anything substantial.
|
||||
*/
|
||||
try_to_freeze();
|
||||
|
||||
/*
|
||||
* We are back. Now reacquire the siglock before touching
|
||||
* last_siginfo, so that we are sure to have synchronized with
|
||||
|
|
@ -2092,7 +2087,7 @@ static bool do_signal_stop(int signr)
|
|||
}
|
||||
|
||||
/* Now we don't run again until woken by SIGCONT or SIGKILL */
|
||||
schedule();
|
||||
freezable_schedule();
|
||||
return true;
|
||||
} else {
|
||||
/*
|
||||
|
|
@ -2138,10 +2133,9 @@ static void do_jobctl_trap(void)
|
|||
}
|
||||
}
|
||||
|
||||
static int ptrace_signal(int signr, siginfo_t *info,
|
||||
struct pt_regs *regs, void *cookie)
|
||||
static int ptrace_signal(int signr, siginfo_t *info)
|
||||
{
|
||||
ptrace_signal_deliver(regs, cookie);
|
||||
ptrace_signal_deliver();
|
||||
/*
|
||||
* We do not check sig_kernel_stop(signr) but set this marker
|
||||
* unconditionally because we do not know whether debugger will
|
||||
|
|
@ -2200,15 +2194,14 @@ int get_signal_to_deliver(siginfo_t *info, struct k_sigaction *return_ka,
|
|||
if (unlikely(uprobe_deny_signal()))
|
||||
return 0;
|
||||
|
||||
relock:
|
||||
/*
|
||||
* We'll jump back here after any time we were stopped in TASK_STOPPED.
|
||||
* While in TASK_STOPPED, we were considered "frozen enough".
|
||||
* Now that we woke up, it's crucial if we're supposed to be
|
||||
* frozen that we freeze now before running anything substantial.
|
||||
* Do this once, we can't return to user-mode if freezing() == T.
|
||||
* do_signal_stop() and ptrace_stop() do freezable_schedule() and
|
||||
* thus do not need another check after return.
|
||||
*/
|
||||
try_to_freeze();
|
||||
|
||||
relock:
|
||||
spin_lock_irq(&sighand->siglock);
|
||||
/*
|
||||
* Every stopped thread goes here after wakeup. Check to see if
|
||||
|
|
@ -2265,8 +2258,7 @@ relock:
|
|||
break; /* will return 0 */
|
||||
|
||||
if (unlikely(current->ptrace) && signr != SIGKILL) {
|
||||
signr = ptrace_signal(signr, info,
|
||||
regs, cookie);
|
||||
signr = ptrace_signal(signr, info);
|
||||
if (!signr)
|
||||
continue;
|
||||
}
|
||||
|
|
@ -2351,7 +2343,7 @@ relock:
|
|||
|
||||
if (sig_kernel_coredump(signr)) {
|
||||
if (print_fatal_signals)
|
||||
print_fatal_signal(regs, info->si_signo);
|
||||
print_fatal_signal(info->si_signo);
|
||||
/*
|
||||
* If it was able to dump core, this kills all
|
||||
* other threads in the group and synchronizes with
|
||||
|
|
@ -2360,7 +2352,7 @@ relock:
|
|||
* first and our do_group_exit call below will use
|
||||
* that value and ignore the one we pass it.
|
||||
*/
|
||||
do_coredump(info, regs);
|
||||
do_coredump(info);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -2536,11 +2528,8 @@ static void __set_task_blocked(struct task_struct *tsk, const sigset_t *newset)
|
|||
*/
|
||||
void set_current_blocked(sigset_t *newset)
|
||||
{
|
||||
struct task_struct *tsk = current;
|
||||
sigdelsetmask(newset, sigmask(SIGKILL) | sigmask(SIGSTOP));
|
||||
spin_lock_irq(&tsk->sighand->siglock);
|
||||
__set_task_blocked(tsk, newset);
|
||||
spin_unlock_irq(&tsk->sighand->siglock);
|
||||
__set_current_blocked(newset);
|
||||
}
|
||||
|
||||
void __set_current_blocked(const sigset_t *newset)
|
||||
|
|
@ -3103,6 +3092,79 @@ do_sigaltstack (const stack_t __user *uss, stack_t __user *uoss, unsigned long s
|
|||
out:
|
||||
return error;
|
||||
}
|
||||
#ifdef CONFIG_GENERIC_SIGALTSTACK
|
||||
SYSCALL_DEFINE2(sigaltstack,const stack_t __user *,uss, stack_t __user *,uoss)
|
||||
{
|
||||
return do_sigaltstack(uss, uoss, current_user_stack_pointer());
|
||||
}
|
||||
#endif
|
||||
|
||||
int restore_altstack(const stack_t __user *uss)
|
||||
{
|
||||
int err = do_sigaltstack(uss, NULL, current_user_stack_pointer());
|
||||
/* squash all but EFAULT for now */
|
||||
return err == -EFAULT ? err : 0;
|
||||
}
|
||||
|
||||
int __save_altstack(stack_t __user *uss, unsigned long sp)
|
||||
{
|
||||
struct task_struct *t = current;
|
||||
return __put_user((void __user *)t->sas_ss_sp, &uss->ss_sp) |
|
||||
__put_user(sas_ss_flags(sp), &uss->ss_flags) |
|
||||
__put_user(t->sas_ss_size, &uss->ss_size);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
#ifdef CONFIG_GENERIC_SIGALTSTACK
|
||||
asmlinkage long compat_sys_sigaltstack(const compat_stack_t __user *uss_ptr,
|
||||
compat_stack_t __user *uoss_ptr)
|
||||
{
|
||||
stack_t uss, uoss;
|
||||
int ret;
|
||||
mm_segment_t seg;
|
||||
|
||||
if (uss_ptr) {
|
||||
compat_stack_t uss32;
|
||||
|
||||
memset(&uss, 0, sizeof(stack_t));
|
||||
if (copy_from_user(&uss32, uss_ptr, sizeof(compat_stack_t)))
|
||||
return -EFAULT;
|
||||
uss.ss_sp = compat_ptr(uss32.ss_sp);
|
||||
uss.ss_flags = uss32.ss_flags;
|
||||
uss.ss_size = uss32.ss_size;
|
||||
}
|
||||
seg = get_fs();
|
||||
set_fs(KERNEL_DS);
|
||||
ret = do_sigaltstack((stack_t __force __user *) (uss_ptr ? &uss : NULL),
|
||||
(stack_t __force __user *) &uoss,
|
||||
compat_user_stack_pointer());
|
||||
set_fs(seg);
|
||||
if (ret >= 0 && uoss_ptr) {
|
||||
if (!access_ok(VERIFY_WRITE, uoss_ptr, sizeof(compat_stack_t)) ||
|
||||
__put_user(ptr_to_compat(uoss.ss_sp), &uoss_ptr->ss_sp) ||
|
||||
__put_user(uoss.ss_flags, &uoss_ptr->ss_flags) ||
|
||||
__put_user(uoss.ss_size, &uoss_ptr->ss_size))
|
||||
ret = -EFAULT;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int compat_restore_altstack(const compat_stack_t __user *uss)
|
||||
{
|
||||
int err = compat_sys_sigaltstack(uss, NULL);
|
||||
/* squash all but -EFAULT for now */
|
||||
return err == -EFAULT ? err : 0;
|
||||
}
|
||||
|
||||
int __compat_save_altstack(compat_stack_t __user *uss, unsigned long sp)
|
||||
{
|
||||
struct task_struct *t = current;
|
||||
return __put_user(ptr_to_compat((void __user *)t->sas_ss_sp), &uss->ss_sp) |
|
||||
__put_user(sas_ss_flags(sp), &uss->ss_flags) |
|
||||
__put_user(t->sas_ss_size, &uss->ss_size);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __ARCH_WANT_SYS_SIGPENDING
|
||||
|
||||
|
|
@ -3139,7 +3201,6 @@ SYSCALL_DEFINE3(sigprocmask, int, how, old_sigset_t __user *, nset,
|
|||
if (nset) {
|
||||
if (copy_from_user(&new_set, nset, sizeof(*nset)))
|
||||
return -EFAULT;
|
||||
new_set &= ~(sigmask(SIGKILL) | sigmask(SIGSTOP));
|
||||
|
||||
new_blocked = current->blocked;
|
||||
|
||||
|
|
@ -3157,7 +3218,7 @@ SYSCALL_DEFINE3(sigprocmask, int, how, old_sigset_t __user *, nset,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
__set_current_blocked(&new_blocked);
|
||||
set_current_blocked(&new_blocked);
|
||||
}
|
||||
|
||||
if (oset) {
|
||||
|
|
@ -3221,6 +3282,7 @@ SYSCALL_DEFINE1(ssetmask, int, newmask)
|
|||
int old = current->blocked.sig[0];
|
||||
sigset_t newset;
|
||||
|
||||
siginitset(&newset, newmask);
|
||||
set_current_blocked(&newset);
|
||||
|
||||
return old;
|
||||
|
|
|
|||
|
|
@ -221,7 +221,7 @@ asmlinkage void __do_softirq(void)
|
|||
current->flags &= ~PF_MEMALLOC;
|
||||
|
||||
pending = local_softirq_pending();
|
||||
vtime_account(current);
|
||||
vtime_account_irq_enter(current);
|
||||
|
||||
__local_bh_disable((unsigned long)__builtin_return_address(0),
|
||||
SOFTIRQ_OFFSET);
|
||||
|
|
@ -272,7 +272,7 @@ restart:
|
|||
|
||||
lockdep_softirq_exit();
|
||||
|
||||
vtime_account(current);
|
||||
vtime_account_irq_exit(current);
|
||||
__local_bh_enable(SOFTIRQ_OFFSET);
|
||||
tsk_restore_flags(current, old_flags, PF_MEMALLOC);
|
||||
}
|
||||
|
|
@ -341,7 +341,7 @@ static inline void invoke_softirq(void)
|
|||
*/
|
||||
void irq_exit(void)
|
||||
{
|
||||
vtime_account(current);
|
||||
vtime_account_irq_exit(current);
|
||||
trace_hardirq_exit();
|
||||
sub_preempt_count(IRQ_EXIT_OFFSET);
|
||||
if (!in_interrupt() && local_softirq_pending())
|
||||
|
|
|
|||
|
|
@ -16,8 +16,10 @@
|
|||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* Copyright (C) IBM Corporation, 2006
|
||||
* Copyright (C) Fujitsu, 2012
|
||||
*
|
||||
* Author: Paul McKenney <paulmck@us.ibm.com>
|
||||
* Lai Jiangshan <laijs@cn.fujitsu.com>
|
||||
*
|
||||
* For detailed explanation of Read-Copy Update mechanism see -
|
||||
* Documentation/RCU/ *.txt
|
||||
|
|
@ -34,6 +36,10 @@
|
|||
#include <linux/delay.h>
|
||||
#include <linux/srcu.h>
|
||||
|
||||
#include <trace/events/rcu.h>
|
||||
|
||||
#include "rcu.h"
|
||||
|
||||
/*
|
||||
* Initialize an rcu_batch structure to empty.
|
||||
*/
|
||||
|
|
@ -92,9 +98,6 @@ static inline void rcu_batch_move(struct rcu_batch *to, struct rcu_batch *from)
|
|||
}
|
||||
}
|
||||
|
||||
/* single-thread state-machine */
|
||||
static void process_srcu(struct work_struct *work);
|
||||
|
||||
static int init_srcu_struct_fields(struct srcu_struct *sp)
|
||||
{
|
||||
sp->completed = 0;
|
||||
|
|
@ -464,7 +467,9 @@ static void __synchronize_srcu(struct srcu_struct *sp, int trycount)
|
|||
*/
|
||||
void synchronize_srcu(struct srcu_struct *sp)
|
||||
{
|
||||
__synchronize_srcu(sp, SYNCHRONIZE_SRCU_TRYCOUNT);
|
||||
__synchronize_srcu(sp, rcu_expedited
|
||||
? SYNCHRONIZE_SRCU_EXP_TRYCOUNT
|
||||
: SYNCHRONIZE_SRCU_TRYCOUNT);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(synchronize_srcu);
|
||||
|
||||
|
|
@ -637,7 +642,7 @@ static void srcu_reschedule(struct srcu_struct *sp)
|
|||
/*
|
||||
* This is the work-queue function that handles SRCU grace periods.
|
||||
*/
|
||||
static void process_srcu(struct work_struct *work)
|
||||
void process_srcu(struct work_struct *work)
|
||||
{
|
||||
struct srcu_struct *sp;
|
||||
|
||||
|
|
@ -648,3 +653,4 @@ static void process_srcu(struct work_struct *work)
|
|||
srcu_invoke_callbacks(sp);
|
||||
srcu_reschedule(sp);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(process_srcu);
|
||||
|
|
|
|||
|
|
@ -1046,7 +1046,7 @@ void do_sys_times(struct tms *tms)
|
|||
cputime_t tgutime, tgstime, cutime, cstime;
|
||||
|
||||
spin_lock_irq(¤t->sighand->siglock);
|
||||
thread_group_times(current, &tgutime, &tgstime);
|
||||
thread_group_cputime_adjusted(current, &tgutime, &tgstime);
|
||||
cutime = current->signal->cutime;
|
||||
cstime = current->signal->cstime;
|
||||
spin_unlock_irq(¤t->sighand->siglock);
|
||||
|
|
@ -1704,7 +1704,7 @@ static void k_getrusage(struct task_struct *p, int who, struct rusage *r)
|
|||
utime = stime = 0;
|
||||
|
||||
if (who == RUSAGE_THREAD) {
|
||||
task_times(current, &utime, &stime);
|
||||
task_cputime_adjusted(current, &utime, &stime);
|
||||
accumulate_thread_rusage(p, r);
|
||||
maxrss = p->signal->maxrss;
|
||||
goto out;
|
||||
|
|
@ -1730,7 +1730,7 @@ static void k_getrusage(struct task_struct *p, int who, struct rusage *r)
|
|||
break;
|
||||
|
||||
case RUSAGE_SELF:
|
||||
thread_group_times(p, &tgutime, &tgstime);
|
||||
thread_group_cputime_adjusted(p, &tgutime, &tgstime);
|
||||
utime += tgutime;
|
||||
stime += tgstime;
|
||||
r->ru_nvcsw += p->signal->nvcsw;
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ cond_syscall(sys_swapoff);
|
|||
cond_syscall(sys_kexec_load);
|
||||
cond_syscall(compat_sys_kexec_load);
|
||||
cond_syscall(sys_init_module);
|
||||
cond_syscall(sys_finit_module);
|
||||
cond_syscall(sys_delete_module);
|
||||
cond_syscall(sys_socketpair);
|
||||
cond_syscall(sys_bind);
|
||||
|
|
|
|||
|
|
@ -256,9 +256,11 @@ static int min_sched_granularity_ns = 100000; /* 100 usecs */
|
|||
static int max_sched_granularity_ns = NSEC_PER_SEC; /* 1 second */
|
||||
static int min_wakeup_granularity_ns; /* 0 usecs */
|
||||
static int max_wakeup_granularity_ns = NSEC_PER_SEC; /* 1 second */
|
||||
#ifdef CONFIG_SMP
|
||||
static int min_sched_tunable_scaling = SCHED_TUNABLESCALING_NONE;
|
||||
static int max_sched_tunable_scaling = SCHED_TUNABLESCALING_END-1;
|
||||
#endif
|
||||
#endif /* CONFIG_SMP */
|
||||
#endif /* CONFIG_SCHED_DEBUG */
|
||||
|
||||
#ifdef CONFIG_COMPACTION
|
||||
static int min_extfrag_threshold;
|
||||
|
|
@ -301,6 +303,7 @@ static struct ctl_table kern_table[] = {
|
|||
.extra1 = &min_wakeup_granularity_ns,
|
||||
.extra2 = &max_wakeup_granularity_ns,
|
||||
},
|
||||
#ifdef CONFIG_SMP
|
||||
{
|
||||
.procname = "sched_tunable_scaling",
|
||||
.data = &sysctl_sched_tunable_scaling,
|
||||
|
|
@ -347,7 +350,45 @@ static struct ctl_table kern_table[] = {
|
|||
.extra1 = &zero,
|
||||
.extra2 = &one,
|
||||
},
|
||||
#endif
|
||||
#endif /* CONFIG_SMP */
|
||||
#ifdef CONFIG_NUMA_BALANCING
|
||||
{
|
||||
.procname = "numa_balancing_scan_delay_ms",
|
||||
.data = &sysctl_numa_balancing_scan_delay,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec,
|
||||
},
|
||||
{
|
||||
.procname = "numa_balancing_scan_period_min_ms",
|
||||
.data = &sysctl_numa_balancing_scan_period_min,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec,
|
||||
},
|
||||
{
|
||||
.procname = "numa_balancing_scan_period_reset",
|
||||
.data = &sysctl_numa_balancing_scan_period_reset,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec,
|
||||
},
|
||||
{
|
||||
.procname = "numa_balancing_scan_period_max_ms",
|
||||
.data = &sysctl_numa_balancing_scan_period_max,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec,
|
||||
},
|
||||
{
|
||||
.procname = "numa_balancing_scan_size_mb",
|
||||
.data = &sysctl_numa_balancing_scan_size,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec,
|
||||
},
|
||||
#endif /* CONFIG_NUMA_BALANCING */
|
||||
#endif /* CONFIG_SCHED_DEBUG */
|
||||
{
|
||||
.procname = "sched_rt_period_us",
|
||||
.data = &sysctl_sched_rt_period,
|
||||
|
|
@ -565,7 +606,7 @@ static struct ctl_table kern_table[] = {
|
|||
.extra2 = &one,
|
||||
},
|
||||
#endif
|
||||
#ifdef CONFIG_HOTPLUG
|
||||
|
||||
{
|
||||
.procname = "hotplug",
|
||||
.data = &uevent_helper,
|
||||
|
|
@ -573,7 +614,7 @@ static struct ctl_table kern_table[] = {
|
|||
.mode = 0644,
|
||||
.proc_handler = proc_dostring,
|
||||
},
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_CHR_DEV_SG
|
||||
{
|
||||
.procname = "sg-big-buff",
|
||||
|
|
|
|||
|
|
@ -1344,7 +1344,7 @@ static ssize_t binary_sysctl(const int *name, int nlen,
|
|||
goto out_putname;
|
||||
}
|
||||
|
||||
mnt = current->nsproxy->pid_ns->proc_mnt;
|
||||
mnt = task_active_pid_ns(current)->proc_mnt;
|
||||
file = file_open_root(mnt->mnt_root, mnt, pathname, flags);
|
||||
result = PTR_ERR(file);
|
||||
if (IS_ERR(file))
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
obj-y += timekeeping.o ntp.o clocksource.o jiffies.o timer_list.o timecompare.o
|
||||
obj-y += timekeeping.o ntp.o clocksource.o jiffies.o timer_list.o
|
||||
obj-y += timeconv.o posix-clock.o alarmtimer.o
|
||||
|
||||
obj-$(CONFIG_GENERIC_CLOCKEVENTS_BUILD) += clockevents.o
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ static cycle_t jiffies_read(struct clocksource *cs)
|
|||
return (cycle_t) jiffies;
|
||||
}
|
||||
|
||||
struct clocksource clocksource_jiffies = {
|
||||
static struct clocksource clocksource_jiffies = {
|
||||
.name = "jiffies",
|
||||
.rating = 1, /* lowest valid rating*/
|
||||
.read = jiffies_read,
|
||||
|
|
@ -67,6 +67,8 @@ struct clocksource clocksource_jiffies = {
|
|||
.shift = JIFFIES_SHIFT,
|
||||
};
|
||||
|
||||
__cacheline_aligned_in_smp DEFINE_SEQLOCK(jiffies_lock);
|
||||
|
||||
#if (BITS_PER_LONG < 64)
|
||||
u64 get_jiffies_64(void)
|
||||
{
|
||||
|
|
@ -74,9 +76,9 @@ u64 get_jiffies_64(void)
|
|||
u64 ret;
|
||||
|
||||
do {
|
||||
seq = read_seqbegin(&xtime_lock);
|
||||
seq = read_seqbegin(&jiffies_lock);
|
||||
ret = jiffies_64;
|
||||
} while (read_seqretry(&xtime_lock, seq));
|
||||
} while (read_seqretry(&jiffies_lock, seq));
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(get_jiffies_64);
|
||||
|
|
|
|||
|
|
@ -63,13 +63,13 @@ int tick_is_oneshot_available(void)
|
|||
static void tick_periodic(int cpu)
|
||||
{
|
||||
if (tick_do_timer_cpu == cpu) {
|
||||
write_seqlock(&xtime_lock);
|
||||
write_seqlock(&jiffies_lock);
|
||||
|
||||
/* Keep track of the next tick event */
|
||||
tick_next_period = ktime_add(tick_next_period, tick_period);
|
||||
|
||||
do_timer(1);
|
||||
write_sequnlock(&xtime_lock);
|
||||
write_sequnlock(&jiffies_lock);
|
||||
}
|
||||
|
||||
update_process_times(user_mode(get_irq_regs()));
|
||||
|
|
@ -130,9 +130,9 @@ void tick_setup_periodic(struct clock_event_device *dev, int broadcast)
|
|||
ktime_t next;
|
||||
|
||||
do {
|
||||
seq = read_seqbegin(&xtime_lock);
|
||||
seq = read_seqbegin(&jiffies_lock);
|
||||
next = tick_next_period;
|
||||
} while (read_seqretry(&xtime_lock, seq));
|
||||
} while (read_seqretry(&jiffies_lock, seq));
|
||||
|
||||
clockevents_set_mode(dev, CLOCK_EVT_MODE_ONESHOT);
|
||||
|
||||
|
|
|
|||
|
|
@ -141,4 +141,3 @@ static inline int tick_device_is_functional(struct clock_event_device *dev)
|
|||
#endif
|
||||
|
||||
extern void do_timer(unsigned long ticks);
|
||||
extern seqlock_t xtime_lock;
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@
|
|||
static DEFINE_PER_CPU(struct tick_sched, tick_cpu_sched);
|
||||
|
||||
/*
|
||||
* The time, when the last jiffy update happened. Protected by xtime_lock.
|
||||
* The time, when the last jiffy update happened. Protected by jiffies_lock.
|
||||
*/
|
||||
static ktime_t last_jiffies_update;
|
||||
|
||||
|
|
@ -49,14 +49,14 @@ static void tick_do_update_jiffies64(ktime_t now)
|
|||
ktime_t delta;
|
||||
|
||||
/*
|
||||
* Do a quick check without holding xtime_lock:
|
||||
* Do a quick check without holding jiffies_lock:
|
||||
*/
|
||||
delta = ktime_sub(now, last_jiffies_update);
|
||||
if (delta.tv64 < tick_period.tv64)
|
||||
return;
|
||||
|
||||
/* Reevalute with xtime_lock held */
|
||||
write_seqlock(&xtime_lock);
|
||||
/* Reevalute with jiffies_lock held */
|
||||
write_seqlock(&jiffies_lock);
|
||||
|
||||
delta = ktime_sub(now, last_jiffies_update);
|
||||
if (delta.tv64 >= tick_period.tv64) {
|
||||
|
|
@ -79,7 +79,7 @@ static void tick_do_update_jiffies64(ktime_t now)
|
|||
/* Keep the tick_next_period variable up to date */
|
||||
tick_next_period = ktime_add(last_jiffies_update, tick_period);
|
||||
}
|
||||
write_sequnlock(&xtime_lock);
|
||||
write_sequnlock(&jiffies_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -89,15 +89,58 @@ static ktime_t tick_init_jiffy_update(void)
|
|||
{
|
||||
ktime_t period;
|
||||
|
||||
write_seqlock(&xtime_lock);
|
||||
write_seqlock(&jiffies_lock);
|
||||
/* Did we start the jiffies update yet ? */
|
||||
if (last_jiffies_update.tv64 == 0)
|
||||
last_jiffies_update = tick_next_period;
|
||||
period = last_jiffies_update;
|
||||
write_sequnlock(&xtime_lock);
|
||||
write_sequnlock(&jiffies_lock);
|
||||
return period;
|
||||
}
|
||||
|
||||
|
||||
static void tick_sched_do_timer(ktime_t now)
|
||||
{
|
||||
int cpu = smp_processor_id();
|
||||
|
||||
#ifdef CONFIG_NO_HZ
|
||||
/*
|
||||
* Check if the do_timer duty was dropped. We don't care about
|
||||
* concurrency: This happens only when the cpu in charge went
|
||||
* into a long sleep. If two cpus happen to assign themself to
|
||||
* this duty, then the jiffies update is still serialized by
|
||||
* jiffies_lock.
|
||||
*/
|
||||
if (unlikely(tick_do_timer_cpu == TICK_DO_TIMER_NONE))
|
||||
tick_do_timer_cpu = cpu;
|
||||
#endif
|
||||
|
||||
/* Check, if the jiffies need an update */
|
||||
if (tick_do_timer_cpu == cpu)
|
||||
tick_do_update_jiffies64(now);
|
||||
}
|
||||
|
||||
static void tick_sched_handle(struct tick_sched *ts, struct pt_regs *regs)
|
||||
{
|
||||
#ifdef CONFIG_NO_HZ
|
||||
/*
|
||||
* When we are idle and the tick is stopped, we have to touch
|
||||
* the watchdog as we might not schedule for a really long
|
||||
* time. This happens on complete idle SMP systems while
|
||||
* waiting on the login prompt. We also increment the "start of
|
||||
* idle" jiffy stamp so the idle accounting adjustment we do
|
||||
* when we go busy again does not account too much ticks.
|
||||
*/
|
||||
if (ts->tick_stopped) {
|
||||
touch_softlockup_watchdog();
|
||||
if (is_idle_task(current))
|
||||
ts->idle_jiffies++;
|
||||
}
|
||||
#endif
|
||||
update_process_times(user_mode(regs));
|
||||
profile_tick(CPU_PROFILING);
|
||||
}
|
||||
|
||||
/*
|
||||
* NOHZ - aka dynamic tick functionality
|
||||
*/
|
||||
|
|
@ -282,11 +325,11 @@ static ktime_t tick_nohz_stop_sched_tick(struct tick_sched *ts,
|
|||
|
||||
/* Read jiffies and the time when jiffies were updated last */
|
||||
do {
|
||||
seq = read_seqbegin(&xtime_lock);
|
||||
seq = read_seqbegin(&jiffies_lock);
|
||||
last_update = last_jiffies_update;
|
||||
last_jiffies = jiffies;
|
||||
time_delta = timekeeping_max_deferment();
|
||||
} while (read_seqretry(&xtime_lock, seq));
|
||||
} while (read_seqretry(&jiffies_lock, seq));
|
||||
|
||||
if (rcu_needs_cpu(cpu, &rcu_delta_jiffies) || printk_needs_cpu(cpu) ||
|
||||
arch_needs_cpu(cpu)) {
|
||||
|
|
@ -526,6 +569,8 @@ void tick_nohz_irq_exit(void)
|
|||
if (!ts->inidle)
|
||||
return;
|
||||
|
||||
/* Cancel the timer because CPU already waken up from the C-states*/
|
||||
menu_hrtimer_cancel();
|
||||
__tick_nohz_idle_enter(ts);
|
||||
}
|
||||
|
||||
|
|
@ -621,6 +666,8 @@ void tick_nohz_idle_exit(void)
|
|||
|
||||
ts->inidle = 0;
|
||||
|
||||
/* Cancel the timer because CPU already waken up from the C-states*/
|
||||
menu_hrtimer_cancel();
|
||||
if (ts->idle_active || ts->tick_stopped)
|
||||
now = ktime_get();
|
||||
|
||||
|
|
@ -648,40 +695,12 @@ static void tick_nohz_handler(struct clock_event_device *dev)
|
|||
{
|
||||
struct tick_sched *ts = &__get_cpu_var(tick_cpu_sched);
|
||||
struct pt_regs *regs = get_irq_regs();
|
||||
int cpu = smp_processor_id();
|
||||
ktime_t now = ktime_get();
|
||||
|
||||
dev->next_event.tv64 = KTIME_MAX;
|
||||
|
||||
/*
|
||||
* Check if the do_timer duty was dropped. We don't care about
|
||||
* concurrency: This happens only when the cpu in charge went
|
||||
* into a long sleep. If two cpus happen to assign themself to
|
||||
* this duty, then the jiffies update is still serialized by
|
||||
* xtime_lock.
|
||||
*/
|
||||
if (unlikely(tick_do_timer_cpu == TICK_DO_TIMER_NONE))
|
||||
tick_do_timer_cpu = cpu;
|
||||
|
||||
/* Check, if the jiffies need an update */
|
||||
if (tick_do_timer_cpu == cpu)
|
||||
tick_do_update_jiffies64(now);
|
||||
|
||||
/*
|
||||
* When we are idle and the tick is stopped, we have to touch
|
||||
* the watchdog as we might not schedule for a really long
|
||||
* time. This happens on complete idle SMP systems while
|
||||
* waiting on the login prompt. We also increment the "start
|
||||
* of idle" jiffy stamp so the idle accounting adjustment we
|
||||
* do when we go busy again does not account too much ticks.
|
||||
*/
|
||||
if (ts->tick_stopped) {
|
||||
touch_softlockup_watchdog();
|
||||
ts->idle_jiffies++;
|
||||
}
|
||||
|
||||
update_process_times(user_mode(regs));
|
||||
profile_tick(CPU_PROFILING);
|
||||
tick_sched_do_timer(now);
|
||||
tick_sched_handle(ts, regs);
|
||||
|
||||
while (tick_nohz_reprogram(ts, now)) {
|
||||
now = ktime_get();
|
||||
|
|
@ -794,7 +813,7 @@ void tick_check_idle(int cpu)
|
|||
#ifdef CONFIG_HIGH_RES_TIMERS
|
||||
/*
|
||||
* We rearm the timer until we get disabled by the idle code.
|
||||
* Called with interrupts disabled and timer->base->cpu_base->lock held.
|
||||
* Called with interrupts disabled.
|
||||
*/
|
||||
static enum hrtimer_restart tick_sched_timer(struct hrtimer *timer)
|
||||
{
|
||||
|
|
@ -802,45 +821,15 @@ static enum hrtimer_restart tick_sched_timer(struct hrtimer *timer)
|
|||
container_of(timer, struct tick_sched, sched_timer);
|
||||
struct pt_regs *regs = get_irq_regs();
|
||||
ktime_t now = ktime_get();
|
||||
int cpu = smp_processor_id();
|
||||
|
||||
#ifdef CONFIG_NO_HZ
|
||||
/*
|
||||
* Check if the do_timer duty was dropped. We don't care about
|
||||
* concurrency: This happens only when the cpu in charge went
|
||||
* into a long sleep. If two cpus happen to assign themself to
|
||||
* this duty, then the jiffies update is still serialized by
|
||||
* xtime_lock.
|
||||
*/
|
||||
if (unlikely(tick_do_timer_cpu == TICK_DO_TIMER_NONE))
|
||||
tick_do_timer_cpu = cpu;
|
||||
#endif
|
||||
|
||||
/* Check, if the jiffies need an update */
|
||||
if (tick_do_timer_cpu == cpu)
|
||||
tick_do_update_jiffies64(now);
|
||||
tick_sched_do_timer(now);
|
||||
|
||||
/*
|
||||
* Do not call, when we are not in irq context and have
|
||||
* no valid regs pointer
|
||||
*/
|
||||
if (regs) {
|
||||
/*
|
||||
* When we are idle and the tick is stopped, we have to touch
|
||||
* the watchdog as we might not schedule for a really long
|
||||
* time. This happens on complete idle SMP systems while
|
||||
* waiting on the login prompt. We also increment the "start of
|
||||
* idle" jiffy stamp so the idle accounting adjustment we do
|
||||
* when we go busy again does not account too much ticks.
|
||||
*/
|
||||
if (ts->tick_stopped) {
|
||||
touch_softlockup_watchdog();
|
||||
if (is_idle_task(current))
|
||||
ts->idle_jiffies++;
|
||||
}
|
||||
update_process_times(user_mode(regs));
|
||||
profile_tick(CPU_PROFILING);
|
||||
}
|
||||
if (regs)
|
||||
tick_sched_handle(ts, regs);
|
||||
|
||||
hrtimer_forward(timer, now, tick_period);
|
||||
|
||||
|
|
@ -874,7 +863,7 @@ void tick_setup_sched_timer(void)
|
|||
/* Get the next period (per cpu) */
|
||||
hrtimer_set_expires(&ts->sched_timer, tick_init_jiffy_update());
|
||||
|
||||
/* Offset the tick to avert xtime_lock contention. */
|
||||
/* Offset the tick to avert jiffies_lock contention. */
|
||||
if (sched_skew_tick) {
|
||||
u64 offset = ktime_to_ns(tick_period) >> 1;
|
||||
do_div(offset, num_possible_cpus());
|
||||
|
|
|
|||
|
|
@ -1,193 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2009 Intel Corporation.
|
||||
* Author: Patrick Ohly <patrick.ohly@intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/timecompare.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
/*
|
||||
* fixed point arithmetic scale factor for skew
|
||||
*
|
||||
* Usually one would measure skew in ppb (parts per billion, 1e9), but
|
||||
* using a factor of 2 simplifies the math.
|
||||
*/
|
||||
#define TIMECOMPARE_SKEW_RESOLUTION (((s64)1)<<30)
|
||||
|
||||
ktime_t timecompare_transform(struct timecompare *sync,
|
||||
u64 source_tstamp)
|
||||
{
|
||||
u64 nsec;
|
||||
|
||||
nsec = source_tstamp + sync->offset;
|
||||
nsec += (s64)(source_tstamp - sync->last_update) * sync->skew /
|
||||
TIMECOMPARE_SKEW_RESOLUTION;
|
||||
|
||||
return ns_to_ktime(nsec);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(timecompare_transform);
|
||||
|
||||
int timecompare_offset(struct timecompare *sync,
|
||||
s64 *offset,
|
||||
u64 *source_tstamp)
|
||||
{
|
||||
u64 start_source = 0, end_source = 0;
|
||||
struct {
|
||||
s64 offset;
|
||||
s64 duration_target;
|
||||
} buffer[10], sample, *samples;
|
||||
int counter = 0, i;
|
||||
int used;
|
||||
int index;
|
||||
int num_samples = sync->num_samples;
|
||||
|
||||
if (num_samples > ARRAY_SIZE(buffer)) {
|
||||
samples = kmalloc(sizeof(*samples) * num_samples, GFP_ATOMIC);
|
||||
if (!samples) {
|
||||
samples = buffer;
|
||||
num_samples = ARRAY_SIZE(buffer);
|
||||
}
|
||||
} else {
|
||||
samples = buffer;
|
||||
}
|
||||
|
||||
/* run until we have enough valid samples, but do not try forever */
|
||||
i = 0;
|
||||
counter = 0;
|
||||
while (1) {
|
||||
u64 ts;
|
||||
ktime_t start, end;
|
||||
|
||||
start = sync->target();
|
||||
ts = timecounter_read(sync->source);
|
||||
end = sync->target();
|
||||
|
||||
if (!i)
|
||||
start_source = ts;
|
||||
|
||||
/* ignore negative durations */
|
||||
sample.duration_target = ktime_to_ns(ktime_sub(end, start));
|
||||
if (sample.duration_target >= 0) {
|
||||
/*
|
||||
* assume symetric delay to and from source:
|
||||
* average target time corresponds to measured
|
||||
* source time
|
||||
*/
|
||||
sample.offset =
|
||||
(ktime_to_ns(end) + ktime_to_ns(start)) / 2 -
|
||||
ts;
|
||||
|
||||
/* simple insertion sort based on duration */
|
||||
index = counter - 1;
|
||||
while (index >= 0) {
|
||||
if (samples[index].duration_target <
|
||||
sample.duration_target)
|
||||
break;
|
||||
samples[index + 1] = samples[index];
|
||||
index--;
|
||||
}
|
||||
samples[index + 1] = sample;
|
||||
counter++;
|
||||
}
|
||||
|
||||
i++;
|
||||
if (counter >= num_samples || i >= 100000) {
|
||||
end_source = ts;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*source_tstamp = (end_source + start_source) / 2;
|
||||
|
||||
/* remove outliers by only using 75% of the samples */
|
||||
used = counter * 3 / 4;
|
||||
if (!used)
|
||||
used = counter;
|
||||
if (used) {
|
||||
/* calculate average */
|
||||
s64 off = 0;
|
||||
for (index = 0; index < used; index++)
|
||||
off += samples[index].offset;
|
||||
*offset = div_s64(off, used);
|
||||
}
|
||||
|
||||
if (samples && samples != buffer)
|
||||
kfree(samples);
|
||||
|
||||
return used;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(timecompare_offset);
|
||||
|
||||
void __timecompare_update(struct timecompare *sync,
|
||||
u64 source_tstamp)
|
||||
{
|
||||
s64 offset;
|
||||
u64 average_time;
|
||||
|
||||
if (!timecompare_offset(sync, &offset, &average_time))
|
||||
return;
|
||||
|
||||
if (!sync->last_update) {
|
||||
sync->last_update = average_time;
|
||||
sync->offset = offset;
|
||||
sync->skew = 0;
|
||||
} else {
|
||||
s64 delta_nsec = average_time - sync->last_update;
|
||||
|
||||
/* avoid division by negative or small deltas */
|
||||
if (delta_nsec >= 10000) {
|
||||
s64 delta_offset_nsec = offset - sync->offset;
|
||||
s64 skew; /* delta_offset_nsec *
|
||||
TIMECOMPARE_SKEW_RESOLUTION /
|
||||
delta_nsec */
|
||||
u64 divisor;
|
||||
|
||||
/* div_s64() is limited to 32 bit divisor */
|
||||
skew = delta_offset_nsec * TIMECOMPARE_SKEW_RESOLUTION;
|
||||
divisor = delta_nsec;
|
||||
while (unlikely(divisor >= ((s64)1) << 32)) {
|
||||
/* divide both by 2; beware, right shift
|
||||
of negative value has undefined
|
||||
behavior and can only be used for
|
||||
the positive divisor */
|
||||
skew = div_s64(skew, 2);
|
||||
divisor >>= 1;
|
||||
}
|
||||
skew = div_s64(skew, divisor);
|
||||
|
||||
/*
|
||||
* Calculate new overall skew as 4/16 the
|
||||
* old value and 12/16 the new one. This is
|
||||
* a rather arbitrary tradeoff between
|
||||
* only using the latest measurement (0/16 and
|
||||
* 16/16) and even more weight on past measurements.
|
||||
*/
|
||||
#define TIMECOMPARE_NEW_SKEW_PER_16 12
|
||||
sync->skew =
|
||||
div_s64((16 - TIMECOMPARE_NEW_SKEW_PER_16) *
|
||||
sync->skew +
|
||||
TIMECOMPARE_NEW_SKEW_PER_16 * skew,
|
||||
16);
|
||||
sync->last_update = average_time;
|
||||
sync->offset = offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__timecompare_update);
|
||||
|
|
@ -21,16 +21,11 @@
|
|||
#include <linux/time.h>
|
||||
#include <linux/tick.h>
|
||||
#include <linux/stop_machine.h>
|
||||
#include <linux/pvclock_gtod.h>
|
||||
|
||||
|
||||
static struct timekeeper timekeeper;
|
||||
|
||||
/*
|
||||
* This read-write spinlock protects us from races in SMP while
|
||||
* playing with xtime.
|
||||
*/
|
||||
__cacheline_aligned_in_smp DEFINE_SEQLOCK(xtime_lock);
|
||||
|
||||
/* flag for if timekeeping is suspended */
|
||||
int __read_mostly timekeeping_suspended;
|
||||
|
||||
|
|
@ -180,6 +175,54 @@ static inline s64 timekeeping_get_ns_raw(struct timekeeper *tk)
|
|||
return nsec + arch_gettimeoffset();
|
||||
}
|
||||
|
||||
static RAW_NOTIFIER_HEAD(pvclock_gtod_chain);
|
||||
|
||||
static void update_pvclock_gtod(struct timekeeper *tk)
|
||||
{
|
||||
raw_notifier_call_chain(&pvclock_gtod_chain, 0, tk);
|
||||
}
|
||||
|
||||
/**
|
||||
* pvclock_gtod_register_notifier - register a pvclock timedata update listener
|
||||
*
|
||||
* Must hold write on timekeeper.lock
|
||||
*/
|
||||
int pvclock_gtod_register_notifier(struct notifier_block *nb)
|
||||
{
|
||||
struct timekeeper *tk = &timekeeper;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
write_seqlock_irqsave(&tk->lock, flags);
|
||||
ret = raw_notifier_chain_register(&pvclock_gtod_chain, nb);
|
||||
/* update timekeeping data */
|
||||
update_pvclock_gtod(tk);
|
||||
write_sequnlock_irqrestore(&tk->lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pvclock_gtod_register_notifier);
|
||||
|
||||
/**
|
||||
* pvclock_gtod_unregister_notifier - unregister a pvclock
|
||||
* timedata update listener
|
||||
*
|
||||
* Must hold write on timekeeper.lock
|
||||
*/
|
||||
int pvclock_gtod_unregister_notifier(struct notifier_block *nb)
|
||||
{
|
||||
struct timekeeper *tk = &timekeeper;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
write_seqlock_irqsave(&tk->lock, flags);
|
||||
ret = raw_notifier_chain_unregister(&pvclock_gtod_chain, nb);
|
||||
write_sequnlock_irqrestore(&tk->lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pvclock_gtod_unregister_notifier);
|
||||
|
||||
/* must hold write on timekeeper.lock */
|
||||
static void timekeeping_update(struct timekeeper *tk, bool clearntp)
|
||||
{
|
||||
|
|
@ -188,6 +231,7 @@ static void timekeeping_update(struct timekeeper *tk, bool clearntp)
|
|||
ntp_clear();
|
||||
}
|
||||
update_vsyscall(tk);
|
||||
update_pvclock_gtod(tk);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1299,9 +1343,7 @@ struct timespec get_monotonic_coarse(void)
|
|||
}
|
||||
|
||||
/*
|
||||
* The 64-bit jiffies value is not atomic - you MUST NOT read it
|
||||
* without sampling the sequence number in xtime_lock.
|
||||
* jiffies is defined in the linker script...
|
||||
* Must hold jiffies_lock
|
||||
*/
|
||||
void do_timer(unsigned long ticks)
|
||||
{
|
||||
|
|
@ -1389,7 +1431,7 @@ EXPORT_SYMBOL_GPL(ktime_get_monotonic_offset);
|
|||
*/
|
||||
void xtime_update(unsigned long ticks)
|
||||
{
|
||||
write_seqlock(&xtime_lock);
|
||||
write_seqlock(&jiffies_lock);
|
||||
do_timer(ticks);
|
||||
write_sequnlock(&xtime_lock);
|
||||
write_sequnlock(&jiffies_lock);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -119,6 +119,7 @@ config TRACING
|
|||
select BINARY_PRINTF
|
||||
select EVENT_TRACING
|
||||
select TRACE_CLOCK
|
||||
select IRQ_WORK
|
||||
|
||||
config GENERIC_TRACER
|
||||
bool
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
* Based on code in the latency_tracer, that is:
|
||||
*
|
||||
* Copyright (C) 2004-2006 Ingo Molnar
|
||||
* Copyright (C) 2004 William Lee Irwin III
|
||||
* Copyright (C) 2004 Nadia Yvette Chambers
|
||||
*/
|
||||
|
||||
#include <linux/stop_machine.h>
|
||||
|
|
@ -2437,7 +2437,7 @@ static void reset_iter_read(struct ftrace_iterator *iter)
|
|||
{
|
||||
iter->pos = 0;
|
||||
iter->func_pos = 0;
|
||||
iter->flags &= ~(FTRACE_ITER_PRINTALL & FTRACE_ITER_HASH);
|
||||
iter->flags &= ~(FTRACE_ITER_PRINTALL | FTRACE_ITER_HASH);
|
||||
}
|
||||
|
||||
static void *t_start(struct seq_file *m, loff_t *pos)
|
||||
|
|
@ -2675,12 +2675,12 @@ ftrace_notrace_open(struct inode *inode, struct file *file)
|
|||
}
|
||||
|
||||
loff_t
|
||||
ftrace_regex_lseek(struct file *file, loff_t offset, int origin)
|
||||
ftrace_regex_lseek(struct file *file, loff_t offset, int whence)
|
||||
{
|
||||
loff_t ret;
|
||||
|
||||
if (file->f_mode & FMODE_READ)
|
||||
ret = seq_lseek(file, offset, origin);
|
||||
ret = seq_lseek(file, offset, whence);
|
||||
else
|
||||
file->f_pos = ret = 1;
|
||||
|
||||
|
|
@ -2868,7 +2868,7 @@ static int __init ftrace_mod_cmd_init(void)
|
|||
{
|
||||
return register_ftrace_command(&ftrace_mod_cmd);
|
||||
}
|
||||
device_initcall(ftrace_mod_cmd_init);
|
||||
core_initcall(ftrace_mod_cmd_init);
|
||||
|
||||
static void function_trace_probe_call(unsigned long ip, unsigned long parent_ip,
|
||||
struct ftrace_ops *op, struct pt_regs *pt_regs)
|
||||
|
|
@ -4055,7 +4055,7 @@ static int __init ftrace_nodyn_init(void)
|
|||
ftrace_enabled = 1;
|
||||
return 0;
|
||||
}
|
||||
device_initcall(ftrace_nodyn_init);
|
||||
core_initcall(ftrace_nodyn_init);
|
||||
|
||||
static inline int ftrace_init_dyn_debugfs(struct dentry *d_tracer) { return 0; }
|
||||
static inline void ftrace_startup_enable(int command) { }
|
||||
|
|
@ -4381,7 +4381,7 @@ ftrace_pid_write(struct file *filp, const char __user *ubuf,
|
|||
if (strlen(tmp) == 0)
|
||||
return 1;
|
||||
|
||||
ret = strict_strtol(tmp, 10, &val);
|
||||
ret = kstrtol(tmp, 10, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
|
|
|||
|
|
@ -460,9 +460,10 @@ struct ring_buffer_per_cpu {
|
|||
unsigned long lost_events;
|
||||
unsigned long last_overrun;
|
||||
local_t entries_bytes;
|
||||
local_t commit_overrun;
|
||||
local_t overrun;
|
||||
local_t entries;
|
||||
local_t overrun;
|
||||
local_t commit_overrun;
|
||||
local_t dropped_events;
|
||||
local_t committing;
|
||||
local_t commits;
|
||||
unsigned long read;
|
||||
|
|
@ -1396,6 +1397,8 @@ rb_insert_pages(struct ring_buffer_per_cpu *cpu_buffer)
|
|||
struct list_head *head_page_with_bit;
|
||||
|
||||
head_page = &rb_set_head_page(cpu_buffer)->list;
|
||||
if (!head_page)
|
||||
break;
|
||||
prev_page = head_page->prev;
|
||||
|
||||
first_page = pages->next;
|
||||
|
|
@ -1820,7 +1823,7 @@ rb_add_time_stamp(struct ring_buffer_event *event, u64 delta)
|
|||
}
|
||||
|
||||
/**
|
||||
* ring_buffer_update_event - update event type and data
|
||||
* rb_update_event - update event type and data
|
||||
* @event: the even to update
|
||||
* @type: the type of event
|
||||
* @length: the size of the event field in the ring buffer
|
||||
|
|
@ -2155,8 +2158,10 @@ rb_move_tail(struct ring_buffer_per_cpu *cpu_buffer,
|
|||
* If we are not in overwrite mode,
|
||||
* this is easy, just stop here.
|
||||
*/
|
||||
if (!(buffer->flags & RB_FL_OVERWRITE))
|
||||
if (!(buffer->flags & RB_FL_OVERWRITE)) {
|
||||
local_inc(&cpu_buffer->dropped_events);
|
||||
goto out_reset;
|
||||
}
|
||||
|
||||
ret = rb_handle_head_page(cpu_buffer,
|
||||
tail_page,
|
||||
|
|
@ -2720,8 +2725,8 @@ EXPORT_SYMBOL_GPL(ring_buffer_discard_commit);
|
|||
* and not the length of the event which would hold the header.
|
||||
*/
|
||||
int ring_buffer_write(struct ring_buffer *buffer,
|
||||
unsigned long length,
|
||||
void *data)
|
||||
unsigned long length,
|
||||
void *data)
|
||||
{
|
||||
struct ring_buffer_per_cpu *cpu_buffer;
|
||||
struct ring_buffer_event *event;
|
||||
|
|
@ -2929,12 +2934,12 @@ rb_num_of_entries(struct ring_buffer_per_cpu *cpu_buffer)
|
|||
* @buffer: The ring buffer
|
||||
* @cpu: The per CPU buffer to read from.
|
||||
*/
|
||||
unsigned long ring_buffer_oldest_event_ts(struct ring_buffer *buffer, int cpu)
|
||||
u64 ring_buffer_oldest_event_ts(struct ring_buffer *buffer, int cpu)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct ring_buffer_per_cpu *cpu_buffer;
|
||||
struct buffer_page *bpage;
|
||||
unsigned long ret;
|
||||
u64 ret = 0;
|
||||
|
||||
if (!cpumask_test_cpu(cpu, buffer->cpumask))
|
||||
return 0;
|
||||
|
|
@ -2949,7 +2954,8 @@ unsigned long ring_buffer_oldest_event_ts(struct ring_buffer *buffer, int cpu)
|
|||
bpage = cpu_buffer->reader_page;
|
||||
else
|
||||
bpage = rb_set_head_page(cpu_buffer);
|
||||
ret = bpage->page->time_stamp;
|
||||
if (bpage)
|
||||
ret = bpage->page->time_stamp;
|
||||
raw_spin_unlock_irqrestore(&cpu_buffer->reader_lock, flags);
|
||||
|
||||
return ret;
|
||||
|
|
@ -2995,7 +3001,8 @@ unsigned long ring_buffer_entries_cpu(struct ring_buffer *buffer, int cpu)
|
|||
EXPORT_SYMBOL_GPL(ring_buffer_entries_cpu);
|
||||
|
||||
/**
|
||||
* ring_buffer_overrun_cpu - get the number of overruns in a cpu_buffer
|
||||
* ring_buffer_overrun_cpu - get the number of overruns caused by the ring
|
||||
* buffer wrapping around (only if RB_FL_OVERWRITE is on).
|
||||
* @buffer: The ring buffer
|
||||
* @cpu: The per CPU buffer to get the number of overruns from
|
||||
*/
|
||||
|
|
@ -3015,7 +3022,9 @@ unsigned long ring_buffer_overrun_cpu(struct ring_buffer *buffer, int cpu)
|
|||
EXPORT_SYMBOL_GPL(ring_buffer_overrun_cpu);
|
||||
|
||||
/**
|
||||
* ring_buffer_commit_overrun_cpu - get the number of overruns caused by commits
|
||||
* ring_buffer_commit_overrun_cpu - get the number of overruns caused by
|
||||
* commits failing due to the buffer wrapping around while there are uncommitted
|
||||
* events, such as during an interrupt storm.
|
||||
* @buffer: The ring buffer
|
||||
* @cpu: The per CPU buffer to get the number of overruns from
|
||||
*/
|
||||
|
|
@ -3035,6 +3044,28 @@ ring_buffer_commit_overrun_cpu(struct ring_buffer *buffer, int cpu)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(ring_buffer_commit_overrun_cpu);
|
||||
|
||||
/**
|
||||
* ring_buffer_dropped_events_cpu - get the number of dropped events caused by
|
||||
* the ring buffer filling up (only if RB_FL_OVERWRITE is off).
|
||||
* @buffer: The ring buffer
|
||||
* @cpu: The per CPU buffer to get the number of overruns from
|
||||
*/
|
||||
unsigned long
|
||||
ring_buffer_dropped_events_cpu(struct ring_buffer *buffer, int cpu)
|
||||
{
|
||||
struct ring_buffer_per_cpu *cpu_buffer;
|
||||
unsigned long ret;
|
||||
|
||||
if (!cpumask_test_cpu(cpu, buffer->cpumask))
|
||||
return 0;
|
||||
|
||||
cpu_buffer = buffer->buffers[cpu];
|
||||
ret = local_read(&cpu_buffer->dropped_events);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ring_buffer_dropped_events_cpu);
|
||||
|
||||
/**
|
||||
* ring_buffer_entries - get the number of entries in a buffer
|
||||
* @buffer: The ring buffer
|
||||
|
|
@ -3260,6 +3291,8 @@ rb_get_reader_page(struct ring_buffer_per_cpu *cpu_buffer)
|
|||
* Splice the empty reader page into the list around the head.
|
||||
*/
|
||||
reader = rb_set_head_page(cpu_buffer);
|
||||
if (!reader)
|
||||
goto out;
|
||||
cpu_buffer->reader_page->list.next = rb_list_head(reader->list.next);
|
||||
cpu_buffer->reader_page->list.prev = reader->list.prev;
|
||||
|
||||
|
|
@ -3778,12 +3811,17 @@ void
|
|||
ring_buffer_read_finish(struct ring_buffer_iter *iter)
|
||||
{
|
||||
struct ring_buffer_per_cpu *cpu_buffer = iter->cpu_buffer;
|
||||
unsigned long flags;
|
||||
|
||||
/*
|
||||
* Ring buffer is disabled from recording, here's a good place
|
||||
* to check the integrity of the ring buffer.
|
||||
* to check the integrity of the ring buffer.
|
||||
* Must prevent readers from trying to read, as the check
|
||||
* clears the HEAD page and readers require it.
|
||||
*/
|
||||
raw_spin_lock_irqsave(&cpu_buffer->reader_lock, flags);
|
||||
rb_check_pages(cpu_buffer);
|
||||
raw_spin_unlock_irqrestore(&cpu_buffer->reader_lock, flags);
|
||||
|
||||
atomic_dec(&cpu_buffer->record_disabled);
|
||||
atomic_dec(&cpu_buffer->buffer->resize_disabled);
|
||||
|
|
@ -3864,9 +3902,10 @@ rb_reset_cpu(struct ring_buffer_per_cpu *cpu_buffer)
|
|||
local_set(&cpu_buffer->reader_page->page->commit, 0);
|
||||
cpu_buffer->reader_page->read = 0;
|
||||
|
||||
local_set(&cpu_buffer->commit_overrun, 0);
|
||||
local_set(&cpu_buffer->entries_bytes, 0);
|
||||
local_set(&cpu_buffer->overrun, 0);
|
||||
local_set(&cpu_buffer->commit_overrun, 0);
|
||||
local_set(&cpu_buffer->dropped_events, 0);
|
||||
local_set(&cpu_buffer->entries, 0);
|
||||
local_set(&cpu_buffer->committing, 0);
|
||||
local_set(&cpu_buffer->commits, 0);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
*
|
||||
* Based on code from the latency_tracer, that is:
|
||||
* Copyright (C) 2004-2006 Ingo Molnar
|
||||
* Copyright (C) 2004 William Lee Irwin III
|
||||
* Copyright (C) 2004 Nadia Yvette Chambers
|
||||
*/
|
||||
#include <linux/ring_buffer.h>
|
||||
#include <generated/utsrelease.h>
|
||||
|
|
@ -19,6 +19,7 @@
|
|||
#include <linux/seq_file.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/irqflags.h>
|
||||
#include <linux/irq_work.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/hardirq.h>
|
||||
|
|
@ -77,6 +78,21 @@ static int dummy_set_flag(u32 old_flags, u32 bit, int set)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* To prevent the comm cache from being overwritten when no
|
||||
* tracing is active, only save the comm when a trace event
|
||||
* occurred.
|
||||
*/
|
||||
static DEFINE_PER_CPU(bool, trace_cmdline_save);
|
||||
|
||||
/*
|
||||
* When a reader is waiting for data, then this variable is
|
||||
* set to true.
|
||||
*/
|
||||
static bool trace_wakeup_needed;
|
||||
|
||||
static struct irq_work trace_work_wakeup;
|
||||
|
||||
/*
|
||||
* Kill all tracing for good (never come back).
|
||||
* It is initialized to 1 but will turn to zero if the initialization
|
||||
|
|
@ -139,6 +155,18 @@ static int __init set_ftrace_dump_on_oops(char *str)
|
|||
}
|
||||
__setup("ftrace_dump_on_oops", set_ftrace_dump_on_oops);
|
||||
|
||||
|
||||
static char trace_boot_options_buf[MAX_TRACER_SIZE] __initdata;
|
||||
static char *trace_boot_options __initdata;
|
||||
|
||||
static int __init set_trace_boot_options(char *str)
|
||||
{
|
||||
strncpy(trace_boot_options_buf, str, MAX_TRACER_SIZE);
|
||||
trace_boot_options = trace_boot_options_buf;
|
||||
return 0;
|
||||
}
|
||||
__setup("trace_options=", set_trace_boot_options);
|
||||
|
||||
unsigned long long ns2usecs(cycle_t nsec)
|
||||
{
|
||||
nsec += 500;
|
||||
|
|
@ -198,20 +226,9 @@ static struct trace_array max_tr;
|
|||
|
||||
static DEFINE_PER_CPU(struct trace_array_cpu, max_tr_data);
|
||||
|
||||
/* tracer_enabled is used to toggle activation of a tracer */
|
||||
static int tracer_enabled = 1;
|
||||
|
||||
/**
|
||||
* tracing_is_enabled - return tracer_enabled status
|
||||
*
|
||||
* This function is used by other tracers to know the status
|
||||
* of the tracer_enabled flag. Tracers may use this function
|
||||
* to know if it should enable their features when starting
|
||||
* up. See irqsoff tracer for an example (start_irqsoff_tracer).
|
||||
*/
|
||||
int tracing_is_enabled(void)
|
||||
{
|
||||
return tracer_enabled;
|
||||
return tracing_is_on();
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -333,12 +350,18 @@ unsigned long trace_flags = TRACE_ITER_PRINT_PARENT | TRACE_ITER_PRINTK |
|
|||
static int trace_stop_count;
|
||||
static DEFINE_RAW_SPINLOCK(tracing_start_lock);
|
||||
|
||||
static void wakeup_work_handler(struct work_struct *work)
|
||||
/**
|
||||
* trace_wake_up - wake up tasks waiting for trace input
|
||||
*
|
||||
* Schedules a delayed work to wake up any task that is blocked on the
|
||||
* trace_wait queue. These is used with trace_poll for tasks polling the
|
||||
* trace.
|
||||
*/
|
||||
static void trace_wake_up(struct irq_work *work)
|
||||
{
|
||||
wake_up(&trace_wait);
|
||||
}
|
||||
wake_up_all(&trace_wait);
|
||||
|
||||
static DECLARE_DELAYED_WORK(wakeup_work, wakeup_work_handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* tracing_on - enable tracing buffers
|
||||
|
|
@ -393,22 +416,6 @@ int tracing_is_on(void)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(tracing_is_on);
|
||||
|
||||
/**
|
||||
* trace_wake_up - wake up tasks waiting for trace input
|
||||
*
|
||||
* Schedules a delayed work to wake up any task that is blocked on the
|
||||
* trace_wait queue. These is used with trace_poll for tasks polling the
|
||||
* trace.
|
||||
*/
|
||||
void trace_wake_up(void)
|
||||
{
|
||||
const unsigned long delay = msecs_to_jiffies(2);
|
||||
|
||||
if (trace_flags & TRACE_ITER_BLOCK)
|
||||
return;
|
||||
schedule_delayed_work(&wakeup_work, delay);
|
||||
}
|
||||
|
||||
static int __init set_buf_size(char *str)
|
||||
{
|
||||
unsigned long buf_size;
|
||||
|
|
@ -431,7 +438,7 @@ static int __init set_tracing_thresh(char *str)
|
|||
|
||||
if (!str)
|
||||
return 0;
|
||||
ret = strict_strtoul(str, 0, &threshold);
|
||||
ret = kstrtoul(str, 0, &threshold);
|
||||
if (ret < 0)
|
||||
return 0;
|
||||
tracing_thresh = threshold * 1000;
|
||||
|
|
@ -477,10 +484,12 @@ static const char *trace_options[] = {
|
|||
static struct {
|
||||
u64 (*func)(void);
|
||||
const char *name;
|
||||
int in_ns; /* is this clock in nanoseconds? */
|
||||
} trace_clocks[] = {
|
||||
{ trace_clock_local, "local" },
|
||||
{ trace_clock_global, "global" },
|
||||
{ trace_clock_counter, "counter" },
|
||||
{ trace_clock_local, "local", 1 },
|
||||
{ trace_clock_global, "global", 1 },
|
||||
{ trace_clock_counter, "counter", 0 },
|
||||
ARCH_TRACE_CLOCKS
|
||||
};
|
||||
|
||||
int trace_clock_id;
|
||||
|
|
@ -757,6 +766,40 @@ update_max_tr_single(struct trace_array *tr, struct task_struct *tsk, int cpu)
|
|||
}
|
||||
#endif /* CONFIG_TRACER_MAX_TRACE */
|
||||
|
||||
static void default_wait_pipe(struct trace_iterator *iter)
|
||||
{
|
||||
DEFINE_WAIT(wait);
|
||||
|
||||
prepare_to_wait(&trace_wait, &wait, TASK_INTERRUPTIBLE);
|
||||
|
||||
/*
|
||||
* The events can happen in critical sections where
|
||||
* checking a work queue can cause deadlocks.
|
||||
* After adding a task to the queue, this flag is set
|
||||
* only to notify events to try to wake up the queue
|
||||
* using irq_work.
|
||||
*
|
||||
* We don't clear it even if the buffer is no longer
|
||||
* empty. The flag only causes the next event to run
|
||||
* irq_work to do the work queue wake up. The worse
|
||||
* that can happen if we race with !trace_empty() is that
|
||||
* an event will cause an irq_work to try to wake up
|
||||
* an empty queue.
|
||||
*
|
||||
* There's no reason to protect this flag either, as
|
||||
* the work queue and irq_work logic will do the necessary
|
||||
* synchronization for the wake ups. The only thing
|
||||
* that is necessary is that the wake up happens after
|
||||
* a task has been queued. It's OK for spurious wake ups.
|
||||
*/
|
||||
trace_wakeup_needed = true;
|
||||
|
||||
if (trace_empty(iter))
|
||||
schedule();
|
||||
|
||||
finish_wait(&trace_wait, &wait);
|
||||
}
|
||||
|
||||
/**
|
||||
* register_tracer - register a tracer with the ftrace system.
|
||||
* @type - the plugin for the tracer
|
||||
|
|
@ -875,32 +918,6 @@ int register_tracer(struct tracer *type)
|
|||
return ret;
|
||||
}
|
||||
|
||||
void unregister_tracer(struct tracer *type)
|
||||
{
|
||||
struct tracer **t;
|
||||
|
||||
mutex_lock(&trace_types_lock);
|
||||
for (t = &trace_types; *t; t = &(*t)->next) {
|
||||
if (*t == type)
|
||||
goto found;
|
||||
}
|
||||
pr_info("Tracer %s not registered\n", type->name);
|
||||
goto out;
|
||||
|
||||
found:
|
||||
*t = (*t)->next;
|
||||
|
||||
if (type == current_trace && tracer_enabled) {
|
||||
tracer_enabled = 0;
|
||||
tracing_stop();
|
||||
if (current_trace->stop)
|
||||
current_trace->stop(&global_trace);
|
||||
current_trace = &nop_trace;
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&trace_types_lock);
|
||||
}
|
||||
|
||||
void tracing_reset(struct trace_array *tr, int cpu)
|
||||
{
|
||||
struct ring_buffer *buffer = tr->buffer;
|
||||
|
|
@ -1131,10 +1148,14 @@ void trace_find_cmdline(int pid, char comm[])
|
|||
|
||||
void tracing_record_cmdline(struct task_struct *tsk)
|
||||
{
|
||||
if (atomic_read(&trace_record_cmdline_disabled) || !tracer_enabled ||
|
||||
!tracing_is_on())
|
||||
if (atomic_read(&trace_record_cmdline_disabled) || !tracing_is_on())
|
||||
return;
|
||||
|
||||
if (!__this_cpu_read(trace_cmdline_save))
|
||||
return;
|
||||
|
||||
__this_cpu_write(trace_cmdline_save, false);
|
||||
|
||||
trace_save_cmdline(tsk);
|
||||
}
|
||||
|
||||
|
|
@ -1178,27 +1199,36 @@ trace_buffer_lock_reserve(struct ring_buffer *buffer,
|
|||
return event;
|
||||
}
|
||||
|
||||
void
|
||||
__buffer_unlock_commit(struct ring_buffer *buffer, struct ring_buffer_event *event)
|
||||
{
|
||||
__this_cpu_write(trace_cmdline_save, true);
|
||||
if (trace_wakeup_needed) {
|
||||
trace_wakeup_needed = false;
|
||||
/* irq_work_queue() supplies it's own memory barriers */
|
||||
irq_work_queue(&trace_work_wakeup);
|
||||
}
|
||||
ring_buffer_unlock_commit(buffer, event);
|
||||
}
|
||||
|
||||
static inline void
|
||||
__trace_buffer_unlock_commit(struct ring_buffer *buffer,
|
||||
struct ring_buffer_event *event,
|
||||
unsigned long flags, int pc,
|
||||
int wake)
|
||||
unsigned long flags, int pc)
|
||||
{
|
||||
ring_buffer_unlock_commit(buffer, event);
|
||||
__buffer_unlock_commit(buffer, event);
|
||||
|
||||
ftrace_trace_stack(buffer, flags, 6, pc);
|
||||
ftrace_trace_userstack(buffer, flags, pc);
|
||||
|
||||
if (wake)
|
||||
trace_wake_up();
|
||||
}
|
||||
|
||||
void trace_buffer_unlock_commit(struct ring_buffer *buffer,
|
||||
struct ring_buffer_event *event,
|
||||
unsigned long flags, int pc)
|
||||
{
|
||||
__trace_buffer_unlock_commit(buffer, event, flags, pc, 1);
|
||||
__trace_buffer_unlock_commit(buffer, event, flags, pc);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(trace_buffer_unlock_commit);
|
||||
|
||||
struct ring_buffer_event *
|
||||
trace_current_buffer_lock_reserve(struct ring_buffer **current_rb,
|
||||
|
|
@ -1215,29 +1245,21 @@ void trace_current_buffer_unlock_commit(struct ring_buffer *buffer,
|
|||
struct ring_buffer_event *event,
|
||||
unsigned long flags, int pc)
|
||||
{
|
||||
__trace_buffer_unlock_commit(buffer, event, flags, pc, 1);
|
||||
__trace_buffer_unlock_commit(buffer, event, flags, pc);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(trace_current_buffer_unlock_commit);
|
||||
|
||||
void trace_nowake_buffer_unlock_commit(struct ring_buffer *buffer,
|
||||
struct ring_buffer_event *event,
|
||||
unsigned long flags, int pc)
|
||||
void trace_buffer_unlock_commit_regs(struct ring_buffer *buffer,
|
||||
struct ring_buffer_event *event,
|
||||
unsigned long flags, int pc,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
__trace_buffer_unlock_commit(buffer, event, flags, pc, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(trace_nowake_buffer_unlock_commit);
|
||||
|
||||
void trace_nowake_buffer_unlock_commit_regs(struct ring_buffer *buffer,
|
||||
struct ring_buffer_event *event,
|
||||
unsigned long flags, int pc,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
ring_buffer_unlock_commit(buffer, event);
|
||||
__buffer_unlock_commit(buffer, event);
|
||||
|
||||
ftrace_trace_stack_regs(buffer, flags, 0, pc, regs);
|
||||
ftrace_trace_userstack(buffer, flags, pc);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(trace_nowake_buffer_unlock_commit_regs);
|
||||
EXPORT_SYMBOL_GPL(trace_buffer_unlock_commit_regs);
|
||||
|
||||
void trace_current_buffer_discard_commit(struct ring_buffer *buffer,
|
||||
struct ring_buffer_event *event)
|
||||
|
|
@ -1269,7 +1291,7 @@ trace_function(struct trace_array *tr,
|
|||
entry->parent_ip = parent_ip;
|
||||
|
||||
if (!filter_check_discard(call, entry, buffer, event))
|
||||
ring_buffer_unlock_commit(buffer, event);
|
||||
__buffer_unlock_commit(buffer, event);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -1362,7 +1384,7 @@ static void __ftrace_trace_stack(struct ring_buffer *buffer,
|
|||
entry->size = trace.nr_entries;
|
||||
|
||||
if (!filter_check_discard(call, entry, buffer, event))
|
||||
ring_buffer_unlock_commit(buffer, event);
|
||||
__buffer_unlock_commit(buffer, event);
|
||||
|
||||
out:
|
||||
/* Again, don't let gcc optimize things here */
|
||||
|
|
@ -1458,7 +1480,7 @@ ftrace_trace_userstack(struct ring_buffer *buffer, unsigned long flags, int pc)
|
|||
|
||||
save_stack_trace_user(&trace);
|
||||
if (!filter_check_discard(call, entry, buffer, event))
|
||||
ring_buffer_unlock_commit(buffer, event);
|
||||
__buffer_unlock_commit(buffer, event);
|
||||
|
||||
out_drop_count:
|
||||
__this_cpu_dec(user_stack_count);
|
||||
|
|
@ -1559,10 +1581,10 @@ static int alloc_percpu_trace_buffer(void)
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static int buffers_allocated;
|
||||
|
||||
void trace_printk_init_buffers(void)
|
||||
{
|
||||
static int buffers_allocated;
|
||||
|
||||
if (buffers_allocated)
|
||||
return;
|
||||
|
||||
|
|
@ -1571,7 +1593,38 @@ void trace_printk_init_buffers(void)
|
|||
|
||||
pr_info("ftrace: Allocated trace_printk buffers\n");
|
||||
|
||||
/* Expand the buffers to set size */
|
||||
tracing_update_buffers();
|
||||
|
||||
buffers_allocated = 1;
|
||||
|
||||
/*
|
||||
* trace_printk_init_buffers() can be called by modules.
|
||||
* If that happens, then we need to start cmdline recording
|
||||
* directly here. If the global_trace.buffer is already
|
||||
* allocated here, then this was called by module code.
|
||||
*/
|
||||
if (global_trace.buffer)
|
||||
tracing_start_cmdline_record();
|
||||
}
|
||||
|
||||
void trace_printk_start_comm(void)
|
||||
{
|
||||
/* Start tracing comms if trace printk is set */
|
||||
if (!buffers_allocated)
|
||||
return;
|
||||
tracing_start_cmdline_record();
|
||||
}
|
||||
|
||||
static void trace_printk_start_stop_comm(int enabled)
|
||||
{
|
||||
if (!buffers_allocated)
|
||||
return;
|
||||
|
||||
if (enabled)
|
||||
tracing_start_cmdline_record();
|
||||
else
|
||||
tracing_stop_cmdline_record();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1622,7 +1675,7 @@ int trace_vbprintk(unsigned long ip, const char *fmt, va_list args)
|
|||
|
||||
memcpy(entry->buf, tbuffer, sizeof(u32) * len);
|
||||
if (!filter_check_discard(call, entry, buffer, event)) {
|
||||
ring_buffer_unlock_commit(buffer, event);
|
||||
__buffer_unlock_commit(buffer, event);
|
||||
ftrace_trace_stack(buffer, flags, 6, pc);
|
||||
}
|
||||
|
||||
|
|
@ -1693,7 +1746,7 @@ int trace_array_vprintk(struct trace_array *tr,
|
|||
memcpy(&entry->buf, tbuffer, len);
|
||||
entry->buf[len] = '\0';
|
||||
if (!filter_check_discard(call, entry, buffer, event)) {
|
||||
ring_buffer_unlock_commit(buffer, event);
|
||||
__buffer_unlock_commit(buffer, event);
|
||||
ftrace_trace_stack(buffer, flags, 6, pc);
|
||||
}
|
||||
out:
|
||||
|
|
@ -2426,6 +2479,10 @@ __tracing_open(struct inode *inode, struct file *file)
|
|||
if (ring_buffer_overruns(iter->tr->buffer))
|
||||
iter->iter_flags |= TRACE_FILE_ANNOTATE;
|
||||
|
||||
/* Output in nanoseconds only if we are using a clock in nanoseconds. */
|
||||
if (trace_clocks[trace_clock_id].in_ns)
|
||||
iter->iter_flags |= TRACE_FILE_TIME_IN_NS;
|
||||
|
||||
/* stop the trace while dumping */
|
||||
tracing_stop();
|
||||
|
||||
|
|
@ -2794,26 +2851,19 @@ static void set_tracer_flags(unsigned int mask, int enabled)
|
|||
|
||||
if (mask == TRACE_ITER_OVERWRITE)
|
||||
ring_buffer_change_overwrite(global_trace.buffer, enabled);
|
||||
|
||||
if (mask == TRACE_ITER_PRINTK)
|
||||
trace_printk_start_stop_comm(enabled);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
tracing_trace_options_write(struct file *filp, const char __user *ubuf,
|
||||
size_t cnt, loff_t *ppos)
|
||||
static int trace_set_options(char *option)
|
||||
{
|
||||
char buf[64];
|
||||
char *cmp;
|
||||
int neg = 0;
|
||||
int ret;
|
||||
int ret = 0;
|
||||
int i;
|
||||
|
||||
if (cnt >= sizeof(buf))
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_user(&buf, ubuf, cnt))
|
||||
return -EFAULT;
|
||||
|
||||
buf[cnt] = 0;
|
||||
cmp = strstrip(buf);
|
||||
cmp = strstrip(option);
|
||||
|
||||
if (strncmp(cmp, "no", 2) == 0) {
|
||||
neg = 1;
|
||||
|
|
@ -2832,10 +2882,27 @@ tracing_trace_options_write(struct file *filp, const char __user *ubuf,
|
|||
mutex_lock(&trace_types_lock);
|
||||
ret = set_tracer_option(current_trace, cmp, neg);
|
||||
mutex_unlock(&trace_types_lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
tracing_trace_options_write(struct file *filp, const char __user *ubuf,
|
||||
size_t cnt, loff_t *ppos)
|
||||
{
|
||||
char buf[64];
|
||||
|
||||
if (cnt >= sizeof(buf))
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_user(&buf, ubuf, cnt))
|
||||
return -EFAULT;
|
||||
|
||||
buf[cnt] = 0;
|
||||
|
||||
trace_set_options(buf);
|
||||
|
||||
*ppos += cnt;
|
||||
|
||||
return cnt;
|
||||
|
|
@ -2939,56 +3006,6 @@ static const struct file_operations tracing_saved_cmdlines_fops = {
|
|||
.llseek = generic_file_llseek,
|
||||
};
|
||||
|
||||
static ssize_t
|
||||
tracing_ctrl_read(struct file *filp, char __user *ubuf,
|
||||
size_t cnt, loff_t *ppos)
|
||||
{
|
||||
char buf[64];
|
||||
int r;
|
||||
|
||||
r = sprintf(buf, "%u\n", tracer_enabled);
|
||||
return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
tracing_ctrl_write(struct file *filp, const char __user *ubuf,
|
||||
size_t cnt, loff_t *ppos)
|
||||
{
|
||||
struct trace_array *tr = filp->private_data;
|
||||
unsigned long val;
|
||||
int ret;
|
||||
|
||||
ret = kstrtoul_from_user(ubuf, cnt, 10, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val = !!val;
|
||||
|
||||
mutex_lock(&trace_types_lock);
|
||||
if (tracer_enabled ^ val) {
|
||||
|
||||
/* Only need to warn if this is used to change the state */
|
||||
WARN_ONCE(1, "tracing_enabled is deprecated. Use tracing_on");
|
||||
|
||||
if (val) {
|
||||
tracer_enabled = 1;
|
||||
if (current_trace->start)
|
||||
current_trace->start(tr);
|
||||
tracing_start();
|
||||
} else {
|
||||
tracer_enabled = 0;
|
||||
tracing_stop();
|
||||
if (current_trace->stop)
|
||||
current_trace->stop(tr);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&trace_types_lock);
|
||||
|
||||
*ppos += cnt;
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
tracing_set_trace_read(struct file *filp, char __user *ubuf,
|
||||
size_t cnt, loff_t *ppos)
|
||||
|
|
@ -3019,6 +3036,31 @@ static void set_buffer_entries(struct trace_array *tr, unsigned long val)
|
|||
tr->data[cpu]->entries = val;
|
||||
}
|
||||
|
||||
/* resize @tr's buffer to the size of @size_tr's entries */
|
||||
static int resize_buffer_duplicate_size(struct trace_array *tr,
|
||||
struct trace_array *size_tr, int cpu_id)
|
||||
{
|
||||
int cpu, ret = 0;
|
||||
|
||||
if (cpu_id == RING_BUFFER_ALL_CPUS) {
|
||||
for_each_tracing_cpu(cpu) {
|
||||
ret = ring_buffer_resize(tr->buffer,
|
||||
size_tr->data[cpu]->entries, cpu);
|
||||
if (ret < 0)
|
||||
break;
|
||||
tr->data[cpu]->entries = size_tr->data[cpu]->entries;
|
||||
}
|
||||
} else {
|
||||
ret = ring_buffer_resize(tr->buffer,
|
||||
size_tr->data[cpu_id]->entries, cpu_id);
|
||||
if (ret == 0)
|
||||
tr->data[cpu_id]->entries =
|
||||
size_tr->data[cpu_id]->entries;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __tracing_resize_ring_buffer(unsigned long size, int cpu)
|
||||
{
|
||||
int ret;
|
||||
|
|
@ -3030,6 +3072,10 @@ static int __tracing_resize_ring_buffer(unsigned long size, int cpu)
|
|||
*/
|
||||
ring_buffer_expanded = 1;
|
||||
|
||||
/* May be called before buffers are initialized */
|
||||
if (!global_trace.buffer)
|
||||
return 0;
|
||||
|
||||
ret = ring_buffer_resize(global_trace.buffer, size, cpu);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
|
@ -3039,23 +3085,8 @@ static int __tracing_resize_ring_buffer(unsigned long size, int cpu)
|
|||
|
||||
ret = ring_buffer_resize(max_tr.buffer, size, cpu);
|
||||
if (ret < 0) {
|
||||
int r = 0;
|
||||
|
||||
if (cpu == RING_BUFFER_ALL_CPUS) {
|
||||
int i;
|
||||
for_each_tracing_cpu(i) {
|
||||
r = ring_buffer_resize(global_trace.buffer,
|
||||
global_trace.data[i]->entries,
|
||||
i);
|
||||
if (r < 0)
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
r = ring_buffer_resize(global_trace.buffer,
|
||||
global_trace.data[cpu]->entries,
|
||||
cpu);
|
||||
}
|
||||
|
||||
int r = resize_buffer_duplicate_size(&global_trace,
|
||||
&global_trace, cpu);
|
||||
if (r < 0) {
|
||||
/*
|
||||
* AARGH! We are left with different
|
||||
|
|
@ -3193,17 +3224,11 @@ static int tracing_set_tracer(const char *buf)
|
|||
|
||||
topts = create_trace_option_files(t);
|
||||
if (t->use_max_tr) {
|
||||
int cpu;
|
||||
/* we need to make per cpu buffer sizes equivalent */
|
||||
for_each_tracing_cpu(cpu) {
|
||||
ret = ring_buffer_resize(max_tr.buffer,
|
||||
global_trace.data[cpu]->entries,
|
||||
cpu);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
max_tr.data[cpu]->entries =
|
||||
global_trace.data[cpu]->entries;
|
||||
}
|
||||
ret = resize_buffer_duplicate_size(&max_tr, &global_trace,
|
||||
RING_BUFFER_ALL_CPUS);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (t->init) {
|
||||
|
|
@ -3325,6 +3350,10 @@ static int tracing_open_pipe(struct inode *inode, struct file *filp)
|
|||
if (trace_flags & TRACE_ITER_LATENCY_FMT)
|
||||
iter->iter_flags |= TRACE_FILE_LAT_FMT;
|
||||
|
||||
/* Output in nanoseconds only if we are using a clock in nanoseconds. */
|
||||
if (trace_clocks[trace_clock_id].in_ns)
|
||||
iter->iter_flags |= TRACE_FILE_TIME_IN_NS;
|
||||
|
||||
iter->cpu_file = cpu_file;
|
||||
iter->tr = &global_trace;
|
||||
mutex_init(&iter->mutex);
|
||||
|
|
@ -3385,19 +3414,6 @@ tracing_poll_pipe(struct file *filp, poll_table *poll_table)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void default_wait_pipe(struct trace_iterator *iter)
|
||||
{
|
||||
DEFINE_WAIT(wait);
|
||||
|
||||
prepare_to_wait(&trace_wait, &wait, TASK_INTERRUPTIBLE);
|
||||
|
||||
if (trace_empty(iter))
|
||||
schedule();
|
||||
|
||||
finish_wait(&trace_wait, &wait);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is a make-shift waitqueue.
|
||||
* A tracer might use this callback on some rare cases:
|
||||
|
|
@ -3446,7 +3462,7 @@ static int tracing_wait_pipe(struct file *filp)
|
|||
*
|
||||
* iter->pos will be 0 if we haven't read anything.
|
||||
*/
|
||||
if (!tracer_enabled && iter->pos)
|
||||
if (!tracing_is_enabled() && iter->pos)
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -3955,7 +3971,7 @@ tracing_mark_write(struct file *filp, const char __user *ubuf,
|
|||
} else
|
||||
entry->buf[cnt] = '\0';
|
||||
|
||||
ring_buffer_unlock_commit(buffer, event);
|
||||
__buffer_unlock_commit(buffer, event);
|
||||
|
||||
written = cnt;
|
||||
|
||||
|
|
@ -4016,6 +4032,14 @@ static ssize_t tracing_clock_write(struct file *filp, const char __user *ubuf,
|
|||
if (max_tr.buffer)
|
||||
ring_buffer_set_clock(max_tr.buffer, trace_clocks[i].func);
|
||||
|
||||
/*
|
||||
* New clock may not be consistent with the previous clock.
|
||||
* Reset the buffer so that it doesn't have incomparable timestamps.
|
||||
*/
|
||||
tracing_reset_online_cpus(&global_trace);
|
||||
if (max_tr.buffer)
|
||||
tracing_reset_online_cpus(&max_tr);
|
||||
|
||||
mutex_unlock(&trace_types_lock);
|
||||
|
||||
*fpos += cnt;
|
||||
|
|
@ -4037,13 +4061,6 @@ static const struct file_operations tracing_max_lat_fops = {
|
|||
.llseek = generic_file_llseek,
|
||||
};
|
||||
|
||||
static const struct file_operations tracing_ctrl_fops = {
|
||||
.open = tracing_open_generic,
|
||||
.read = tracing_ctrl_read,
|
||||
.write = tracing_ctrl_write,
|
||||
.llseek = generic_file_llseek,
|
||||
};
|
||||
|
||||
static const struct file_operations set_tracer_fops = {
|
||||
.open = tracing_open_generic,
|
||||
.read = tracing_set_trace_read,
|
||||
|
|
@ -4260,13 +4277,11 @@ tracing_buffers_splice_read(struct file *file, loff_t *ppos,
|
|||
return -ENOMEM;
|
||||
|
||||
if (*ppos & (PAGE_SIZE - 1)) {
|
||||
WARN_ONCE(1, "Ftrace: previous read must page-align\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (len & (PAGE_SIZE - 1)) {
|
||||
WARN_ONCE(1, "Ftrace: splice_read should page-align\n");
|
||||
if (len < PAGE_SIZE) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
|
|
@ -4377,13 +4392,27 @@ tracing_stats_read(struct file *filp, char __user *ubuf,
|
|||
cnt = ring_buffer_bytes_cpu(tr->buffer, cpu);
|
||||
trace_seq_printf(s, "bytes: %ld\n", cnt);
|
||||
|
||||
t = ns2usecs(ring_buffer_oldest_event_ts(tr->buffer, cpu));
|
||||
usec_rem = do_div(t, USEC_PER_SEC);
|
||||
trace_seq_printf(s, "oldest event ts: %5llu.%06lu\n", t, usec_rem);
|
||||
if (trace_clocks[trace_clock_id].in_ns) {
|
||||
/* local or global for trace_clock */
|
||||
t = ns2usecs(ring_buffer_oldest_event_ts(tr->buffer, cpu));
|
||||
usec_rem = do_div(t, USEC_PER_SEC);
|
||||
trace_seq_printf(s, "oldest event ts: %5llu.%06lu\n",
|
||||
t, usec_rem);
|
||||
|
||||
t = ns2usecs(ring_buffer_time_stamp(tr->buffer, cpu));
|
||||
usec_rem = do_div(t, USEC_PER_SEC);
|
||||
trace_seq_printf(s, "now ts: %5llu.%06lu\n", t, usec_rem);
|
||||
t = ns2usecs(ring_buffer_time_stamp(tr->buffer, cpu));
|
||||
usec_rem = do_div(t, USEC_PER_SEC);
|
||||
trace_seq_printf(s, "now ts: %5llu.%06lu\n", t, usec_rem);
|
||||
} else {
|
||||
/* counter or tsc mode for trace_clock */
|
||||
trace_seq_printf(s, "oldest event ts: %llu\n",
|
||||
ring_buffer_oldest_event_ts(tr->buffer, cpu));
|
||||
|
||||
trace_seq_printf(s, "now ts: %llu\n",
|
||||
ring_buffer_time_stamp(tr->buffer, cpu));
|
||||
}
|
||||
|
||||
cnt = ring_buffer_dropped_events_cpu(tr->buffer, cpu);
|
||||
trace_seq_printf(s, "dropped events: %ld\n", cnt);
|
||||
|
||||
count = simple_read_from_buffer(ubuf, count, ppos, s->buffer, s->len);
|
||||
|
||||
|
|
@ -4788,10 +4817,17 @@ rb_simple_write(struct file *filp, const char __user *ubuf,
|
|||
return ret;
|
||||
|
||||
if (buffer) {
|
||||
if (val)
|
||||
mutex_lock(&trace_types_lock);
|
||||
if (val) {
|
||||
ring_buffer_record_on(buffer);
|
||||
else
|
||||
if (current_trace->start)
|
||||
current_trace->start(tr);
|
||||
} else {
|
||||
ring_buffer_record_off(buffer);
|
||||
if (current_trace->stop)
|
||||
current_trace->stop(tr);
|
||||
}
|
||||
mutex_unlock(&trace_types_lock);
|
||||
}
|
||||
|
||||
(*ppos)++;
|
||||
|
|
@ -4815,9 +4851,6 @@ static __init int tracer_init_debugfs(void)
|
|||
|
||||
d_tracer = tracing_init_dentry();
|
||||
|
||||
trace_create_file("tracing_enabled", 0644, d_tracer,
|
||||
&global_trace, &tracing_ctrl_fops);
|
||||
|
||||
trace_create_file("trace_options", 0644, d_tracer,
|
||||
NULL, &tracing_iter_fops);
|
||||
|
||||
|
|
@ -5089,6 +5122,7 @@ __init static int tracer_alloc_buffers(void)
|
|||
|
||||
/* Only allocate trace_printk buffers if a trace_printk exists */
|
||||
if (__stop___trace_bprintk_fmt != __start___trace_bprintk_fmt)
|
||||
/* Must be called before global_trace.buffer is allocated */
|
||||
trace_printk_init_buffers();
|
||||
|
||||
/* To save memory, keep the ring buffer size to its minimum */
|
||||
|
|
@ -5136,6 +5170,7 @@ __init static int tracer_alloc_buffers(void)
|
|||
#endif
|
||||
|
||||
trace_init_cmdlines();
|
||||
init_irq_work(&trace_work_wakeup, trace_wake_up);
|
||||
|
||||
register_tracer(&nop_trace);
|
||||
current_trace = &nop_trace;
|
||||
|
|
@ -5147,6 +5182,13 @@ __init static int tracer_alloc_buffers(void)
|
|||
|
||||
register_die_notifier(&trace_die_notifier);
|
||||
|
||||
while (trace_boot_options) {
|
||||
char *option;
|
||||
|
||||
option = strsep(&trace_boot_options, ",");
|
||||
trace_set_options(option);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_free_cpumask:
|
||||
|
|
|
|||
|
|
@ -285,8 +285,8 @@ struct tracer {
|
|||
int (*set_flag)(u32 old_flags, u32 bit, int set);
|
||||
struct tracer *next;
|
||||
struct tracer_flags *flags;
|
||||
int print_max;
|
||||
int use_max_tr;
|
||||
bool print_max;
|
||||
bool use_max_tr;
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -327,7 +327,6 @@ trace_buffer_iter(struct trace_iterator *iter, int cpu)
|
|||
|
||||
int tracer_init(struct tracer *t, struct trace_array *tr);
|
||||
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);
|
||||
void tracing_reset_current(int cpu);
|
||||
|
|
@ -349,9 +348,6 @@ trace_buffer_lock_reserve(struct ring_buffer *buffer,
|
|||
unsigned long len,
|
||||
unsigned long flags,
|
||||
int pc);
|
||||
void trace_buffer_unlock_commit(struct ring_buffer *buffer,
|
||||
struct ring_buffer_event *event,
|
||||
unsigned long flags, int pc);
|
||||
|
||||
struct trace_entry *tracing_get_trace_entry(struct trace_array *tr,
|
||||
struct trace_array_cpu *data);
|
||||
|
|
@ -359,6 +355,9 @@ struct trace_entry *tracing_get_trace_entry(struct trace_array *tr,
|
|||
struct trace_entry *trace_find_next_entry(struct trace_iterator *iter,
|
||||
int *ent_cpu, u64 *ent_ts);
|
||||
|
||||
void __buffer_unlock_commit(struct ring_buffer *buffer,
|
||||
struct ring_buffer_event *event);
|
||||
|
||||
int trace_empty(struct trace_iterator *iter);
|
||||
|
||||
void *trace_find_next_entry_inc(struct trace_iterator *iter);
|
||||
|
|
@ -367,7 +366,6 @@ void trace_init_global_iter(struct trace_iterator *iter);
|
|||
|
||||
void tracing_iter_reset(struct trace_iterator *iter, int cpu);
|
||||
|
||||
void default_wait_pipe(struct trace_iterator *iter);
|
||||
void poll_wait_pipe(struct trace_iterator *iter);
|
||||
|
||||
void ftrace(struct trace_array *tr,
|
||||
|
|
@ -407,12 +405,7 @@ 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);
|
||||
int is_tracing_stopped(void);
|
||||
enum trace_file_type {
|
||||
TRACE_FILE_LAT_FMT = 1,
|
||||
TRACE_FILE_ANNOTATE = 2,
|
||||
};
|
||||
|
||||
extern cpumask_var_t __read_mostly tracing_buffer_mask;
|
||||
|
||||
|
|
@ -841,6 +834,7 @@ extern const char *__start___trace_bprintk_fmt[];
|
|||
extern const char *__stop___trace_bprintk_fmt[];
|
||||
|
||||
void trace_printk_init_buffers(void);
|
||||
void trace_printk_start_comm(void);
|
||||
|
||||
#undef FTRACE_ENTRY
|
||||
#define FTRACE_ENTRY(call, struct_name, id, tstruct, print, filter) \
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ probe_likely_condition(struct ftrace_branch_data *f, int val, int expect)
|
|||
entry->correct = val == expect;
|
||||
|
||||
if (!filter_check_discard(call, entry, buffer, event))
|
||||
ring_buffer_unlock_commit(buffer, event);
|
||||
__buffer_unlock_commit(buffer, event);
|
||||
|
||||
out:
|
||||
atomic_dec(&tr->data[cpu]->disabled);
|
||||
|
|
@ -199,7 +199,7 @@ __init static int init_branch_tracer(void)
|
|||
}
|
||||
return register_tracer(&branch_trace);
|
||||
}
|
||||
device_initcall(init_branch_tracer);
|
||||
core_initcall(init_branch_tracer);
|
||||
|
||||
#else
|
||||
static inline
|
||||
|
|
|
|||
|
|
@ -491,19 +491,6 @@ static void t_stop(struct seq_file *m, void *p)
|
|||
mutex_unlock(&event_mutex);
|
||||
}
|
||||
|
||||
static int
|
||||
ftrace_event_seq_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
const struct seq_operations *seq_ops;
|
||||
|
||||
if ((file->f_mode & FMODE_WRITE) &&
|
||||
(file->f_flags & O_TRUNC))
|
||||
ftrace_clear_events();
|
||||
|
||||
seq_ops = inode->i_private;
|
||||
return seq_open(file, seq_ops);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
event_enable_read(struct file *filp, char __user *ubuf, size_t cnt,
|
||||
loff_t *ppos)
|
||||
|
|
@ -980,6 +967,9 @@ show_header(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos)
|
|||
return r;
|
||||
}
|
||||
|
||||
static int ftrace_event_avail_open(struct inode *inode, struct file *file);
|
||||
static int ftrace_event_set_open(struct inode *inode, struct file *file);
|
||||
|
||||
static const struct seq_operations show_event_seq_ops = {
|
||||
.start = t_start,
|
||||
.next = t_next,
|
||||
|
|
@ -995,14 +985,14 @@ static const struct seq_operations show_set_event_seq_ops = {
|
|||
};
|
||||
|
||||
static const struct file_operations ftrace_avail_fops = {
|
||||
.open = ftrace_event_seq_open,
|
||||
.open = ftrace_event_avail_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = seq_release,
|
||||
};
|
||||
|
||||
static const struct file_operations ftrace_set_event_fops = {
|
||||
.open = ftrace_event_seq_open,
|
||||
.open = ftrace_event_set_open,
|
||||
.read = seq_read,
|
||||
.write = ftrace_event_write,
|
||||
.llseek = seq_lseek,
|
||||
|
|
@ -1078,6 +1068,26 @@ static struct dentry *event_trace_events_dir(void)
|
|||
return d_events;
|
||||
}
|
||||
|
||||
static int
|
||||
ftrace_event_avail_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
const struct seq_operations *seq_ops = &show_event_seq_ops;
|
||||
|
||||
return seq_open(file, seq_ops);
|
||||
}
|
||||
|
||||
static int
|
||||
ftrace_event_set_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
const struct seq_operations *seq_ops = &show_set_event_seq_ops;
|
||||
|
||||
if ((file->f_mode & FMODE_WRITE) &&
|
||||
(file->f_flags & O_TRUNC))
|
||||
ftrace_clear_events();
|
||||
|
||||
return seq_open(file, seq_ops);
|
||||
}
|
||||
|
||||
static struct dentry *
|
||||
event_subsystem_dir(const char *name, struct dentry *d_events)
|
||||
{
|
||||
|
|
@ -1489,6 +1499,9 @@ static __init int event_trace_enable(void)
|
|||
if (ret)
|
||||
pr_warn("Failed to enable trace event: %s\n", token);
|
||||
}
|
||||
|
||||
trace_printk_start_comm();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1505,15 +1518,13 @@ static __init int event_trace_init(void)
|
|||
return 0;
|
||||
|
||||
entry = debugfs_create_file("available_events", 0444, d_tracer,
|
||||
(void *)&show_event_seq_ops,
|
||||
&ftrace_avail_fops);
|
||||
NULL, &ftrace_avail_fops);
|
||||
if (!entry)
|
||||
pr_warning("Could not create debugfs "
|
||||
"'available_events' entry\n");
|
||||
|
||||
entry = debugfs_create_file("set_event", 0644, d_tracer,
|
||||
(void *)&show_set_event_seq_ops,
|
||||
&ftrace_set_event_fops);
|
||||
NULL, &ftrace_set_event_fops);
|
||||
if (!entry)
|
||||
pr_warning("Could not create debugfs "
|
||||
"'set_event' entry\n");
|
||||
|
|
@ -1749,7 +1760,7 @@ function_test_events_call(unsigned long ip, unsigned long parent_ip,
|
|||
entry->ip = ip;
|
||||
entry->parent_ip = parent_ip;
|
||||
|
||||
trace_nowake_buffer_unlock_commit(buffer, event, flags, pc);
|
||||
trace_buffer_unlock_commit(buffer, event, flags, pc);
|
||||
|
||||
out:
|
||||
atomic_dec(&per_cpu(ftrace_test_event_disable, cpu));
|
||||
|
|
|
|||
|
|
@ -1000,9 +1000,9 @@ static int init_pred(struct filter_parse_state *ps,
|
|||
}
|
||||
} else {
|
||||
if (field->is_signed)
|
||||
ret = strict_strtoll(pred->regex.pattern, 0, &val);
|
||||
ret = kstrtoll(pred->regex.pattern, 0, &val);
|
||||
else
|
||||
ret = strict_strtoull(pred->regex.pattern, 0, &val);
|
||||
ret = kstrtoull(pred->regex.pattern, 0, &val);
|
||||
if (ret) {
|
||||
parse_error(ps, FILT_ERR_ILLEGAL_INTVAL, 0);
|
||||
return -EINVAL;
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
* Based on code from the latency_tracer, that is:
|
||||
*
|
||||
* Copyright (C) 2004-2006 Ingo Molnar
|
||||
* Copyright (C) 2004 William Lee Irwin III
|
||||
* Copyright (C) 2004 Nadia Yvette Chambers
|
||||
*/
|
||||
#include <linux/ring_buffer.h>
|
||||
#include <linux/debugfs.h>
|
||||
|
|
@ -366,7 +366,7 @@ ftrace_trace_onoff_callback(struct ftrace_hash *hash,
|
|||
* We use the callback data field (which is a pointer)
|
||||
* as our counter.
|
||||
*/
|
||||
ret = strict_strtoul(number, 0, (unsigned long *)&count);
|
||||
ret = kstrtoul(number, 0, (unsigned long *)&count);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
@ -411,5 +411,4 @@ static __init int init_function_trace(void)
|
|||
init_func_cmd_traceon();
|
||||
return register_tracer(&function_trace);
|
||||
}
|
||||
device_initcall(init_function_trace);
|
||||
|
||||
core_initcall(init_function_trace);
|
||||
|
|
|
|||
|
|
@ -223,7 +223,7 @@ int __trace_graph_entry(struct trace_array *tr,
|
|||
entry = ring_buffer_event_data(event);
|
||||
entry->graph_ent = *trace;
|
||||
if (!filter_current_check_discard(buffer, call, entry, event))
|
||||
ring_buffer_unlock_commit(buffer, event);
|
||||
__buffer_unlock_commit(buffer, event);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -327,7 +327,7 @@ void __trace_graph_return(struct trace_array *tr,
|
|||
entry = ring_buffer_event_data(event);
|
||||
entry->ret = *trace;
|
||||
if (!filter_current_check_discard(buffer, call, entry, event))
|
||||
ring_buffer_unlock_commit(buffer, event);
|
||||
__buffer_unlock_commit(buffer, event);
|
||||
}
|
||||
|
||||
void trace_graph_return(struct ftrace_graph_ret *trace)
|
||||
|
|
@ -1474,4 +1474,4 @@ static __init int init_graph_trace(void)
|
|||
return register_tracer(&graph_trace);
|
||||
}
|
||||
|
||||
device_initcall(init_graph_trace);
|
||||
core_initcall(init_graph_trace);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
* From code in the latency_tracer, that is:
|
||||
*
|
||||
* Copyright (C) 2004-2006 Ingo Molnar
|
||||
* Copyright (C) 2004 William Lee Irwin III
|
||||
* Copyright (C) 2004 Nadia Yvette Chambers
|
||||
*/
|
||||
#include <linux/kallsyms.h>
|
||||
#include <linux/debugfs.h>
|
||||
|
|
@ -604,7 +604,7 @@ static struct tracer irqsoff_tracer __read_mostly =
|
|||
.reset = irqsoff_tracer_reset,
|
||||
.start = irqsoff_tracer_start,
|
||||
.stop = irqsoff_tracer_stop,
|
||||
.print_max = 1,
|
||||
.print_max = true,
|
||||
.print_header = irqsoff_print_header,
|
||||
.print_line = irqsoff_print_line,
|
||||
.flags = &tracer_flags,
|
||||
|
|
@ -614,7 +614,7 @@ static struct tracer irqsoff_tracer __read_mostly =
|
|||
#endif
|
||||
.open = irqsoff_trace_open,
|
||||
.close = irqsoff_trace_close,
|
||||
.use_max_tr = 1,
|
||||
.use_max_tr = true,
|
||||
};
|
||||
# define register_irqsoff(trace) register_tracer(&trace)
|
||||
#else
|
||||
|
|
@ -637,7 +637,7 @@ static struct tracer preemptoff_tracer __read_mostly =
|
|||
.reset = irqsoff_tracer_reset,
|
||||
.start = irqsoff_tracer_start,
|
||||
.stop = irqsoff_tracer_stop,
|
||||
.print_max = 1,
|
||||
.print_max = true,
|
||||
.print_header = irqsoff_print_header,
|
||||
.print_line = irqsoff_print_line,
|
||||
.flags = &tracer_flags,
|
||||
|
|
@ -647,7 +647,7 @@ static struct tracer preemptoff_tracer __read_mostly =
|
|||
#endif
|
||||
.open = irqsoff_trace_open,
|
||||
.close = irqsoff_trace_close,
|
||||
.use_max_tr = 1,
|
||||
.use_max_tr = true,
|
||||
};
|
||||
# define register_preemptoff(trace) register_tracer(&trace)
|
||||
#else
|
||||
|
|
@ -672,7 +672,7 @@ static struct tracer preemptirqsoff_tracer __read_mostly =
|
|||
.reset = irqsoff_tracer_reset,
|
||||
.start = irqsoff_tracer_start,
|
||||
.stop = irqsoff_tracer_stop,
|
||||
.print_max = 1,
|
||||
.print_max = true,
|
||||
.print_header = irqsoff_print_header,
|
||||
.print_line = irqsoff_print_line,
|
||||
.flags = &tracer_flags,
|
||||
|
|
@ -682,7 +682,7 @@ static struct tracer preemptirqsoff_tracer __read_mostly =
|
|||
#endif
|
||||
.open = irqsoff_trace_open,
|
||||
.close = irqsoff_trace_close,
|
||||
.use_max_tr = 1,
|
||||
.use_max_tr = true,
|
||||
};
|
||||
|
||||
# define register_preemptirqsoff(trace) register_tracer(&trace)
|
||||
|
|
@ -698,4 +698,4 @@ __init static int init_irqsoff_tracer(void)
|
|||
|
||||
return 0;
|
||||
}
|
||||
device_initcall(init_irqsoff_tracer);
|
||||
core_initcall(init_irqsoff_tracer);
|
||||
|
|
|
|||
|
|
@ -444,7 +444,7 @@ static int create_trace_probe(int argc, char **argv)
|
|||
return -EINVAL;
|
||||
}
|
||||
/* an address specified */
|
||||
ret = strict_strtoul(&argv[1][0], 0, (unsigned long *)&addr);
|
||||
ret = kstrtoul(&argv[1][0], 0, (unsigned long *)&addr);
|
||||
if (ret) {
|
||||
pr_info("Failed to parse address.\n");
|
||||
return ret;
|
||||
|
|
@ -751,8 +751,8 @@ static __kprobes void kprobe_trace_func(struct kprobe *kp, struct pt_regs *regs)
|
|||
store_trace_args(sizeof(*entry), tp, regs, (u8 *)&entry[1], dsize);
|
||||
|
||||
if (!filter_current_check_discard(buffer, call, entry, event))
|
||||
trace_nowake_buffer_unlock_commit_regs(buffer, event,
|
||||
irq_flags, pc, regs);
|
||||
trace_buffer_unlock_commit_regs(buffer, event,
|
||||
irq_flags, pc, regs);
|
||||
}
|
||||
|
||||
/* Kretprobe handler */
|
||||
|
|
@ -784,8 +784,8 @@ static __kprobes void kretprobe_trace_func(struct kretprobe_instance *ri,
|
|||
store_trace_args(sizeof(*entry), tp, regs, (u8 *)&entry[1], dsize);
|
||||
|
||||
if (!filter_current_check_discard(buffer, call, entry, event))
|
||||
trace_nowake_buffer_unlock_commit_regs(buffer, event,
|
||||
irq_flags, pc, regs);
|
||||
trace_buffer_unlock_commit_regs(buffer, event,
|
||||
irq_flags, pc, regs);
|
||||
}
|
||||
|
||||
/* Event entry printers */
|
||||
|
|
|
|||
|
|
@ -610,24 +610,54 @@ lat_print_generic(struct trace_seq *s, struct trace_entry *entry, int cpu)
|
|||
return trace_print_lat_fmt(s, entry);
|
||||
}
|
||||
|
||||
static unsigned long preempt_mark_thresh = 100;
|
||||
static unsigned long preempt_mark_thresh_us = 100;
|
||||
|
||||
static int
|
||||
lat_print_timestamp(struct trace_seq *s, u64 abs_usecs,
|
||||
unsigned long rel_usecs)
|
||||
lat_print_timestamp(struct trace_iterator *iter, u64 next_ts)
|
||||
{
|
||||
return trace_seq_printf(s, " %4lldus%c: ", abs_usecs,
|
||||
rel_usecs > preempt_mark_thresh ? '!' :
|
||||
rel_usecs > 1 ? '+' : ' ');
|
||||
unsigned long verbose = trace_flags & TRACE_ITER_VERBOSE;
|
||||
unsigned long in_ns = iter->iter_flags & TRACE_FILE_TIME_IN_NS;
|
||||
unsigned long long abs_ts = iter->ts - iter->tr->time_start;
|
||||
unsigned long long rel_ts = next_ts - iter->ts;
|
||||
struct trace_seq *s = &iter->seq;
|
||||
|
||||
if (in_ns) {
|
||||
abs_ts = ns2usecs(abs_ts);
|
||||
rel_ts = ns2usecs(rel_ts);
|
||||
}
|
||||
|
||||
if (verbose && in_ns) {
|
||||
unsigned long abs_usec = do_div(abs_ts, USEC_PER_MSEC);
|
||||
unsigned long abs_msec = (unsigned long)abs_ts;
|
||||
unsigned long rel_usec = do_div(rel_ts, USEC_PER_MSEC);
|
||||
unsigned long rel_msec = (unsigned long)rel_ts;
|
||||
|
||||
return trace_seq_printf(
|
||||
s, "[%08llx] %ld.%03ldms (+%ld.%03ldms): ",
|
||||
ns2usecs(iter->ts),
|
||||
abs_msec, abs_usec,
|
||||
rel_msec, rel_usec);
|
||||
} else if (verbose && !in_ns) {
|
||||
return trace_seq_printf(
|
||||
s, "[%016llx] %lld (+%lld): ",
|
||||
iter->ts, abs_ts, rel_ts);
|
||||
} else if (!verbose && in_ns) {
|
||||
return trace_seq_printf(
|
||||
s, " %4lldus%c: ",
|
||||
abs_ts,
|
||||
rel_ts > preempt_mark_thresh_us ? '!' :
|
||||
rel_ts > 1 ? '+' : ' ');
|
||||
} else { /* !verbose && !in_ns */
|
||||
return trace_seq_printf(s, " %4lld: ", abs_ts);
|
||||
}
|
||||
}
|
||||
|
||||
int trace_print_context(struct trace_iterator *iter)
|
||||
{
|
||||
struct trace_seq *s = &iter->seq;
|
||||
struct trace_entry *entry = iter->ent;
|
||||
unsigned long long t = ns2usecs(iter->ts);
|
||||
unsigned long usec_rem = do_div(t, USEC_PER_SEC);
|
||||
unsigned long secs = (unsigned long)t;
|
||||
unsigned long long t;
|
||||
unsigned long secs, usec_rem;
|
||||
char comm[TASK_COMM_LEN];
|
||||
int ret;
|
||||
|
||||
|
|
@ -644,8 +674,13 @@ int trace_print_context(struct trace_iterator *iter)
|
|||
return 0;
|
||||
}
|
||||
|
||||
return trace_seq_printf(s, " %5lu.%06lu: ",
|
||||
secs, usec_rem);
|
||||
if (iter->iter_flags & TRACE_FILE_TIME_IN_NS) {
|
||||
t = ns2usecs(iter->ts);
|
||||
usec_rem = do_div(t, USEC_PER_SEC);
|
||||
secs = (unsigned long)t;
|
||||
return trace_seq_printf(s, " %5lu.%06lu: ", secs, usec_rem);
|
||||
} else
|
||||
return trace_seq_printf(s, " %12llu: ", iter->ts);
|
||||
}
|
||||
|
||||
int trace_print_lat_context(struct trace_iterator *iter)
|
||||
|
|
@ -659,36 +694,29 @@ int trace_print_lat_context(struct trace_iterator *iter)
|
|||
*next_entry = trace_find_next_entry(iter, NULL,
|
||||
&next_ts);
|
||||
unsigned long verbose = (trace_flags & TRACE_ITER_VERBOSE);
|
||||
unsigned long abs_usecs = ns2usecs(iter->ts - iter->tr->time_start);
|
||||
unsigned long rel_usecs;
|
||||
|
||||
/* Restore the original ent_size */
|
||||
iter->ent_size = ent_size;
|
||||
|
||||
if (!next_entry)
|
||||
next_ts = iter->ts;
|
||||
rel_usecs = ns2usecs(next_ts - iter->ts);
|
||||
|
||||
if (verbose) {
|
||||
char comm[TASK_COMM_LEN];
|
||||
|
||||
trace_find_cmdline(entry->pid, comm);
|
||||
|
||||
ret = trace_seq_printf(s, "%16s %5d %3d %d %08x %08lx [%08llx]"
|
||||
" %ld.%03ldms (+%ld.%03ldms): ", comm,
|
||||
entry->pid, iter->cpu, entry->flags,
|
||||
entry->preempt_count, iter->idx,
|
||||
ns2usecs(iter->ts),
|
||||
abs_usecs / USEC_PER_MSEC,
|
||||
abs_usecs % USEC_PER_MSEC,
|
||||
rel_usecs / USEC_PER_MSEC,
|
||||
rel_usecs % USEC_PER_MSEC);
|
||||
ret = trace_seq_printf(
|
||||
s, "%16s %5d %3d %d %08x %08lx ",
|
||||
comm, entry->pid, iter->cpu, entry->flags,
|
||||
entry->preempt_count, iter->idx);
|
||||
} else {
|
||||
ret = lat_print_generic(s, entry, iter->cpu);
|
||||
if (ret)
|
||||
ret = lat_print_timestamp(s, abs_usecs, rel_usecs);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
ret = lat_print_timestamp(iter, next_ts);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -441,7 +441,7 @@ static const struct fetch_type *find_fetch_type(const char *type)
|
|||
goto fail;
|
||||
|
||||
type++;
|
||||
if (strict_strtoul(type, 0, &bs))
|
||||
if (kstrtoul(type, 0, &bs))
|
||||
goto fail;
|
||||
|
||||
switch (bs) {
|
||||
|
|
@ -501,8 +501,8 @@ int traceprobe_split_symbol_offset(char *symbol, unsigned long *offset)
|
|||
|
||||
tmp = strchr(symbol, '+');
|
||||
if (tmp) {
|
||||
/* skip sign because strict_strtol doesn't accept '+' */
|
||||
ret = strict_strtoul(tmp + 1, 0, offset);
|
||||
/* skip sign because kstrtoul doesn't accept '+' */
|
||||
ret = kstrtoul(tmp + 1, 0, offset);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
@ -533,7 +533,7 @@ static int parse_probe_vars(char *arg, const struct fetch_type *t,
|
|||
else
|
||||
ret = -EINVAL;
|
||||
} else if (isdigit(arg[5])) {
|
||||
ret = strict_strtoul(arg + 5, 10, ¶m);
|
||||
ret = kstrtoul(arg + 5, 10, ¶m);
|
||||
if (ret || param > PARAM_MAX_STACK)
|
||||
ret = -EINVAL;
|
||||
else {
|
||||
|
|
@ -579,7 +579,7 @@ static int parse_probe_arg(char *arg, const struct fetch_type *t,
|
|||
|
||||
case '@': /* memory or symbol */
|
||||
if (isdigit(arg[1])) {
|
||||
ret = strict_strtoul(arg + 1, 0, ¶m);
|
||||
ret = kstrtoul(arg + 1, 0, ¶m);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
|
|
@ -597,14 +597,14 @@ static int parse_probe_arg(char *arg, const struct fetch_type *t,
|
|||
break;
|
||||
|
||||
case '+': /* deref memory */
|
||||
arg++; /* Skip '+', because strict_strtol() rejects it. */
|
||||
arg++; /* Skip '+', because kstrtol() rejects it. */
|
||||
case '-':
|
||||
tmp = strchr(arg, '(');
|
||||
if (!tmp)
|
||||
break;
|
||||
|
||||
*tmp = '\0';
|
||||
ret = strict_strtol(arg, 0, &offset);
|
||||
ret = kstrtol(arg, 0, &offset);
|
||||
|
||||
if (ret)
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -102,9 +102,7 @@ tracing_sched_wakeup_trace(struct trace_array *tr,
|
|||
entry->next_cpu = task_cpu(wakee);
|
||||
|
||||
if (!filter_check_discard(call, entry, buffer, event))
|
||||
ring_buffer_unlock_commit(buffer, event);
|
||||
ftrace_trace_stack(tr->buffer, flags, 6, pc);
|
||||
ftrace_trace_userstack(tr->buffer, flags, pc);
|
||||
trace_buffer_unlock_commit(buffer, event, flags, pc);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
* Based on code from the latency_tracer, that is:
|
||||
*
|
||||
* Copyright (C) 2004-2006 Ingo Molnar
|
||||
* Copyright (C) 2004 William Lee Irwin III
|
||||
* Copyright (C) 2004 Nadia Yvette Chambers
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/fs.h>
|
||||
|
|
@ -589,7 +589,7 @@ static struct tracer wakeup_tracer __read_mostly =
|
|||
.reset = wakeup_tracer_reset,
|
||||
.start = wakeup_tracer_start,
|
||||
.stop = wakeup_tracer_stop,
|
||||
.print_max = 1,
|
||||
.print_max = true,
|
||||
.print_header = wakeup_print_header,
|
||||
.print_line = wakeup_print_line,
|
||||
.flags = &tracer_flags,
|
||||
|
|
@ -599,7 +599,7 @@ static struct tracer wakeup_tracer __read_mostly =
|
|||
#endif
|
||||
.open = wakeup_trace_open,
|
||||
.close = wakeup_trace_close,
|
||||
.use_max_tr = 1,
|
||||
.use_max_tr = true,
|
||||
};
|
||||
|
||||
static struct tracer wakeup_rt_tracer __read_mostly =
|
||||
|
|
@ -610,7 +610,7 @@ static struct tracer wakeup_rt_tracer __read_mostly =
|
|||
.start = wakeup_tracer_start,
|
||||
.stop = wakeup_tracer_stop,
|
||||
.wait_pipe = poll_wait_pipe,
|
||||
.print_max = 1,
|
||||
.print_max = true,
|
||||
.print_header = wakeup_print_header,
|
||||
.print_line = wakeup_print_line,
|
||||
.flags = &tracer_flags,
|
||||
|
|
@ -620,7 +620,7 @@ static struct tracer wakeup_rt_tracer __read_mostly =
|
|||
#endif
|
||||
.open = wakeup_trace_open,
|
||||
.close = wakeup_trace_close,
|
||||
.use_max_tr = 1,
|
||||
.use_max_tr = true,
|
||||
};
|
||||
|
||||
__init static int init_wakeup_tracer(void)
|
||||
|
|
@ -637,4 +637,4 @@ __init static int init_wakeup_tracer(void)
|
|||
|
||||
return 0;
|
||||
}
|
||||
device_initcall(init_wakeup_tracer);
|
||||
core_initcall(init_wakeup_tracer);
|
||||
|
|
|
|||
|
|
@ -320,7 +320,6 @@ int trace_selftest_startup_dynamic_tracing(struct tracer *trace,
|
|||
int (*func)(void))
|
||||
{
|
||||
int save_ftrace_enabled = ftrace_enabled;
|
||||
int save_tracer_enabled = tracer_enabled;
|
||||
unsigned long count;
|
||||
char *func_name;
|
||||
int ret;
|
||||
|
|
@ -331,7 +330,6 @@ int trace_selftest_startup_dynamic_tracing(struct tracer *trace,
|
|||
|
||||
/* enable tracing, and record the filter function */
|
||||
ftrace_enabled = 1;
|
||||
tracer_enabled = 1;
|
||||
|
||||
/* passed in by parameter to fool gcc from optimizing */
|
||||
func();
|
||||
|
|
@ -395,7 +393,6 @@ int trace_selftest_startup_dynamic_tracing(struct tracer *trace,
|
|||
|
||||
out:
|
||||
ftrace_enabled = save_ftrace_enabled;
|
||||
tracer_enabled = save_tracer_enabled;
|
||||
|
||||
/* Enable tracing on all functions again */
|
||||
ftrace_set_global_filter(NULL, 0, 1);
|
||||
|
|
@ -452,7 +449,6 @@ static int
|
|||
trace_selftest_function_recursion(void)
|
||||
{
|
||||
int save_ftrace_enabled = ftrace_enabled;
|
||||
int save_tracer_enabled = tracer_enabled;
|
||||
char *func_name;
|
||||
int len;
|
||||
int ret;
|
||||
|
|
@ -465,7 +461,6 @@ trace_selftest_function_recursion(void)
|
|||
|
||||
/* enable tracing, and record the filter function */
|
||||
ftrace_enabled = 1;
|
||||
tracer_enabled = 1;
|
||||
|
||||
/* Handle PPC64 '.' name */
|
||||
func_name = "*" __stringify(DYN_FTRACE_TEST_NAME);
|
||||
|
|
@ -534,7 +529,6 @@ trace_selftest_function_recursion(void)
|
|||
ret = 0;
|
||||
out:
|
||||
ftrace_enabled = save_ftrace_enabled;
|
||||
tracer_enabled = save_tracer_enabled;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -569,7 +563,6 @@ static int
|
|||
trace_selftest_function_regs(void)
|
||||
{
|
||||
int save_ftrace_enabled = ftrace_enabled;
|
||||
int save_tracer_enabled = tracer_enabled;
|
||||
char *func_name;
|
||||
int len;
|
||||
int ret;
|
||||
|
|
@ -586,7 +579,6 @@ trace_selftest_function_regs(void)
|
|||
|
||||
/* enable tracing, and record the filter function */
|
||||
ftrace_enabled = 1;
|
||||
tracer_enabled = 1;
|
||||
|
||||
/* Handle PPC64 '.' name */
|
||||
func_name = "*" __stringify(DYN_FTRACE_TEST_NAME);
|
||||
|
|
@ -648,7 +640,6 @@ trace_selftest_function_regs(void)
|
|||
ret = 0;
|
||||
out:
|
||||
ftrace_enabled = save_ftrace_enabled;
|
||||
tracer_enabled = save_tracer_enabled;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -662,7 +653,6 @@ int
|
|||
trace_selftest_startup_function(struct tracer *trace, struct trace_array *tr)
|
||||
{
|
||||
int save_ftrace_enabled = ftrace_enabled;
|
||||
int save_tracer_enabled = tracer_enabled;
|
||||
unsigned long count;
|
||||
int ret;
|
||||
|
||||
|
|
@ -671,7 +661,6 @@ trace_selftest_startup_function(struct tracer *trace, struct trace_array *tr)
|
|||
|
||||
/* start the tracing */
|
||||
ftrace_enabled = 1;
|
||||
tracer_enabled = 1;
|
||||
|
||||
ret = tracer_init(trace, tr);
|
||||
if (ret) {
|
||||
|
|
@ -708,7 +697,6 @@ trace_selftest_startup_function(struct tracer *trace, struct trace_array *tr)
|
|||
ret = trace_selftest_function_regs();
|
||||
out:
|
||||
ftrace_enabled = save_ftrace_enabled;
|
||||
tracer_enabled = save_tracer_enabled;
|
||||
|
||||
/* kill ftrace totally if we failed */
|
||||
if (ret)
|
||||
|
|
@ -1106,6 +1094,7 @@ trace_selftest_startup_wakeup(struct tracer *trace, struct trace_array *tr)
|
|||
tracing_stop();
|
||||
/* check both trace buffers */
|
||||
ret = trace_test_buffer(tr, NULL);
|
||||
printk("ret = %d\n", ret);
|
||||
if (!ret)
|
||||
ret = trace_test_buffer(&max_tr, &count);
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@ static unsigned long max_stack_size;
|
|||
static arch_spinlock_t max_stack_lock =
|
||||
(arch_spinlock_t)__ARCH_SPIN_LOCK_UNLOCKED;
|
||||
|
||||
static int stack_trace_disabled __read_mostly;
|
||||
static DEFINE_PER_CPU(int, trace_active);
|
||||
static DEFINE_MUTEX(stack_sysctl_mutex);
|
||||
|
||||
|
|
@ -116,9 +115,6 @@ stack_trace_call(unsigned long ip, unsigned long parent_ip,
|
|||
{
|
||||
int cpu;
|
||||
|
||||
if (unlikely(!ftrace_enabled || stack_trace_disabled))
|
||||
return;
|
||||
|
||||
preempt_disable_notrace();
|
||||
|
||||
cpu = raw_smp_processor_id();
|
||||
|
|
|
|||
|
|
@ -21,9 +21,6 @@ static int syscall_enter_register(struct ftrace_event_call *event,
|
|||
static int syscall_exit_register(struct ftrace_event_call *event,
|
||||
enum trace_reg type, void *data);
|
||||
|
||||
static int syscall_enter_define_fields(struct ftrace_event_call *call);
|
||||
static int syscall_exit_define_fields(struct ftrace_event_call *call);
|
||||
|
||||
static struct list_head *
|
||||
syscall_get_enter_fields(struct ftrace_event_call *call)
|
||||
{
|
||||
|
|
@ -32,30 +29,6 @@ syscall_get_enter_fields(struct ftrace_event_call *call)
|
|||
return &entry->enter_fields;
|
||||
}
|
||||
|
||||
struct trace_event_functions enter_syscall_print_funcs = {
|
||||
.trace = print_syscall_enter,
|
||||
};
|
||||
|
||||
struct trace_event_functions exit_syscall_print_funcs = {
|
||||
.trace = print_syscall_exit,
|
||||
};
|
||||
|
||||
struct ftrace_event_class event_class_syscall_enter = {
|
||||
.system = "syscalls",
|
||||
.reg = syscall_enter_register,
|
||||
.define_fields = syscall_enter_define_fields,
|
||||
.get_fields = syscall_get_enter_fields,
|
||||
.raw_init = init_syscall_trace,
|
||||
};
|
||||
|
||||
struct ftrace_event_class event_class_syscall_exit = {
|
||||
.system = "syscalls",
|
||||
.reg = syscall_exit_register,
|
||||
.define_fields = syscall_exit_define_fields,
|
||||
.fields = LIST_HEAD_INIT(event_class_syscall_exit.fields),
|
||||
.raw_init = init_syscall_trace,
|
||||
};
|
||||
|
||||
extern struct syscall_metadata *__start_syscalls_metadata[];
|
||||
extern struct syscall_metadata *__stop_syscalls_metadata[];
|
||||
|
||||
|
|
@ -432,7 +405,7 @@ void unreg_event_syscall_exit(struct ftrace_event_call *call)
|
|||
mutex_unlock(&syscall_trace_lock);
|
||||
}
|
||||
|
||||
int init_syscall_trace(struct ftrace_event_call *call)
|
||||
static int init_syscall_trace(struct ftrace_event_call *call)
|
||||
{
|
||||
int id;
|
||||
int num;
|
||||
|
|
@ -457,6 +430,30 @@ int init_syscall_trace(struct ftrace_event_call *call)
|
|||
return id;
|
||||
}
|
||||
|
||||
struct trace_event_functions enter_syscall_print_funcs = {
|
||||
.trace = print_syscall_enter,
|
||||
};
|
||||
|
||||
struct trace_event_functions exit_syscall_print_funcs = {
|
||||
.trace = print_syscall_exit,
|
||||
};
|
||||
|
||||
struct ftrace_event_class event_class_syscall_enter = {
|
||||
.system = "syscalls",
|
||||
.reg = syscall_enter_register,
|
||||
.define_fields = syscall_enter_define_fields,
|
||||
.get_fields = syscall_get_enter_fields,
|
||||
.raw_init = init_syscall_trace,
|
||||
};
|
||||
|
||||
struct ftrace_event_class event_class_syscall_exit = {
|
||||
.system = "syscalls",
|
||||
.reg = syscall_exit_register,
|
||||
.define_fields = syscall_exit_define_fields,
|
||||
.fields = LIST_HEAD_INIT(event_class_syscall_exit.fields),
|
||||
.raw_init = init_syscall_trace,
|
||||
};
|
||||
|
||||
unsigned long __init __weak arch_syscall_addr(int nr)
|
||||
{
|
||||
return (unsigned long)sys_call_table[nr];
|
||||
|
|
@ -537,7 +534,7 @@ static void perf_syscall_enter(void *ignore, struct pt_regs *regs, long id)
|
|||
perf_trace_buf_submit(rec, size, rctx, 0, 1, regs, head, NULL);
|
||||
}
|
||||
|
||||
int perf_sysenter_enable(struct ftrace_event_call *call)
|
||||
static int perf_sysenter_enable(struct ftrace_event_call *call)
|
||||
{
|
||||
int ret = 0;
|
||||
int num;
|
||||
|
|
@ -558,7 +555,7 @@ int perf_sysenter_enable(struct ftrace_event_call *call)
|
|||
return ret;
|
||||
}
|
||||
|
||||
void perf_sysenter_disable(struct ftrace_event_call *call)
|
||||
static void perf_sysenter_disable(struct ftrace_event_call *call)
|
||||
{
|
||||
int num;
|
||||
|
||||
|
|
@ -615,7 +612,7 @@ static void perf_syscall_exit(void *ignore, struct pt_regs *regs, long ret)
|
|||
perf_trace_buf_submit(rec, size, rctx, 0, 1, regs, head, NULL);
|
||||
}
|
||||
|
||||
int perf_sysexit_enable(struct ftrace_event_call *call)
|
||||
static int perf_sysexit_enable(struct ftrace_event_call *call)
|
||||
{
|
||||
int ret = 0;
|
||||
int num;
|
||||
|
|
@ -636,7 +633,7 @@ int perf_sysexit_enable(struct ftrace_event_call *call)
|
|||
return ret;
|
||||
}
|
||||
|
||||
void perf_sysexit_disable(struct ftrace_event_call *call)
|
||||
static void perf_sysexit_disable(struct ftrace_event_call *call)
|
||||
{
|
||||
int num;
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#include <linux/uaccess.h>
|
||||
#include <linux/uprobes.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include "trace_probe.h"
|
||||
|
||||
|
|
@ -189,7 +190,7 @@ static int create_trace_uprobe(int argc, char **argv)
|
|||
if (argv[0][0] == '-')
|
||||
is_delete = true;
|
||||
else if (argv[0][0] != 'p') {
|
||||
pr_info("Probe definition must be started with 'p', 'r' or" " '-'.\n");
|
||||
pr_info("Probe definition must be started with 'p' or '-'.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
|
@ -252,7 +253,7 @@ static int create_trace_uprobe(int argc, char **argv)
|
|||
if (ret)
|
||||
goto fail_address_parse;
|
||||
|
||||
ret = strict_strtoul(arg, 0, &offset);
|
||||
ret = kstrtoul(arg, 0, &offset);
|
||||
if (ret)
|
||||
goto fail_address_parse;
|
||||
|
||||
|
|
@ -263,16 +264,15 @@ static int create_trace_uprobe(int argc, char **argv)
|
|||
|
||||
/* setup a probe */
|
||||
if (!event) {
|
||||
char *tail = strrchr(filename, '/');
|
||||
char *tail;
|
||||
char *ptr;
|
||||
|
||||
ptr = kstrdup((tail ? tail + 1 : filename), GFP_KERNEL);
|
||||
if (!ptr) {
|
||||
tail = kstrdup(kbasename(filename), GFP_KERNEL);
|
||||
if (!tail) {
|
||||
ret = -ENOMEM;
|
||||
goto fail_address_parse;
|
||||
}
|
||||
|
||||
tail = ptr;
|
||||
ptr = strpbrk(tail, ".-_");
|
||||
if (ptr)
|
||||
*ptr = '\0';
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
#include <linux/interrupt.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/user_namespace.h>
|
||||
#include <linux/proc_fs.h>
|
||||
|
||||
/*
|
||||
* userns count is 1 for root user, 1 for init_uts_ns,
|
||||
|
|
@ -51,6 +52,7 @@ struct user_namespace init_user_ns = {
|
|||
},
|
||||
.owner = GLOBAL_ROOT_UID,
|
||||
.group = GLOBAL_ROOT_GID,
|
||||
.proc_inum = PROC_USER_INIT_INO,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(init_user_ns);
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include <linux/nsproxy.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/user_namespace.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/highuid.h>
|
||||
#include <linux/cred.h>
|
||||
#include <linux/securebits.h>
|
||||
|
|
@ -26,6 +27,24 @@ static struct kmem_cache *user_ns_cachep __read_mostly;
|
|||
static bool new_idmap_permitted(struct user_namespace *ns, int cap_setid,
|
||||
struct uid_gid_map *map);
|
||||
|
||||
static void set_cred_user_ns(struct cred *cred, struct user_namespace *user_ns)
|
||||
{
|
||||
/* Start with the same capabilities as init but useless for doing
|
||||
* anything as the capabilities are bound to the new user namespace.
|
||||
*/
|
||||
cred->securebits = SECUREBITS_DEFAULT;
|
||||
cred->cap_inheritable = CAP_EMPTY_SET;
|
||||
cred->cap_permitted = CAP_FULL_SET;
|
||||
cred->cap_effective = CAP_FULL_SET;
|
||||
cred->cap_bset = CAP_FULL_SET;
|
||||
#ifdef CONFIG_KEYS
|
||||
key_put(cred->request_key_auth);
|
||||
cred->request_key_auth = NULL;
|
||||
#endif
|
||||
/* tgcred will be cleared in our caller bc CLONE_THREAD won't be set */
|
||||
cred->user_ns = user_ns;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
|
@ -39,6 +58,7 @@ int create_user_ns(struct cred *new)
|
|||
struct user_namespace *ns, *parent_ns = new->user_ns;
|
||||
kuid_t owner = new->euid;
|
||||
kgid_t group = new->egid;
|
||||
int ret;
|
||||
|
||||
/* The creator needs a mapping in the parent user namespace
|
||||
* or else we won't be able to reasonably tell userspace who
|
||||
|
|
@ -52,38 +72,45 @@ int create_user_ns(struct cred *new)
|
|||
if (!ns)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = proc_alloc_inum(&ns->proc_inum);
|
||||
if (ret) {
|
||||
kmem_cache_free(user_ns_cachep, ns);
|
||||
return ret;
|
||||
}
|
||||
|
||||
kref_init(&ns->kref);
|
||||
/* Leave the new->user_ns reference with the new user namespace. */
|
||||
ns->parent = parent_ns;
|
||||
ns->owner = owner;
|
||||
ns->group = group;
|
||||
|
||||
/* Start with the same capabilities as init but useless for doing
|
||||
* anything as the capabilities are bound to the new user namespace.
|
||||
*/
|
||||
new->securebits = SECUREBITS_DEFAULT;
|
||||
new->cap_inheritable = CAP_EMPTY_SET;
|
||||
new->cap_permitted = CAP_FULL_SET;
|
||||
new->cap_effective = CAP_FULL_SET;
|
||||
new->cap_bset = CAP_FULL_SET;
|
||||
#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 */
|
||||
|
||||
/* Leave the new->user_ns reference with the new user namespace. */
|
||||
/* Leave the reference to our user_ns with the new cred. */
|
||||
new->user_ns = ns;
|
||||
set_cred_user_ns(new, ns);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int unshare_userns(unsigned long unshare_flags, struct cred **new_cred)
|
||||
{
|
||||
struct cred *cred;
|
||||
|
||||
if (!(unshare_flags & CLONE_NEWUSER))
|
||||
return 0;
|
||||
|
||||
cred = prepare_creds();
|
||||
if (!cred)
|
||||
return -ENOMEM;
|
||||
|
||||
*new_cred = cred;
|
||||
return create_user_ns(cred);
|
||||
}
|
||||
|
||||
void free_user_ns(struct kref *kref)
|
||||
{
|
||||
struct user_namespace *parent, *ns =
|
||||
container_of(kref, struct user_namespace, kref);
|
||||
|
||||
parent = ns->parent;
|
||||
proc_free_inum(ns->proc_inum);
|
||||
kmem_cache_free(user_ns_cachep, ns);
|
||||
put_user_ns(parent);
|
||||
}
|
||||
|
|
@ -372,7 +399,7 @@ static int uid_m_show(struct seq_file *seq, void *v)
|
|||
struct user_namespace *lower_ns;
|
||||
uid_t lower;
|
||||
|
||||
lower_ns = current_user_ns();
|
||||
lower_ns = seq_user_ns(seq);
|
||||
if ((lower_ns == ns) && lower_ns->parent)
|
||||
lower_ns = lower_ns->parent;
|
||||
|
||||
|
|
@ -393,7 +420,7 @@ static int gid_m_show(struct seq_file *seq, void *v)
|
|||
struct user_namespace *lower_ns;
|
||||
gid_t lower;
|
||||
|
||||
lower_ns = current_user_ns();
|
||||
lower_ns = seq_user_ns(seq);
|
||||
if ((lower_ns == ns) && lower_ns->parent)
|
||||
lower_ns = lower_ns->parent;
|
||||
|
||||
|
|
@ -669,10 +696,14 @@ ssize_t proc_uid_map_write(struct file *file, const char __user *buf, size_t siz
|
|||
{
|
||||
struct seq_file *seq = file->private_data;
|
||||
struct user_namespace *ns = seq->private;
|
||||
struct user_namespace *seq_ns = seq_user_ns(seq);
|
||||
|
||||
if (!ns->parent)
|
||||
return -EPERM;
|
||||
|
||||
if ((seq_ns != ns) && (seq_ns != ns->parent))
|
||||
return -EPERM;
|
||||
|
||||
return map_write(file, buf, size, ppos, CAP_SETUID,
|
||||
&ns->uid_map, &ns->parent->uid_map);
|
||||
}
|
||||
|
|
@ -681,10 +712,14 @@ ssize_t proc_gid_map_write(struct file *file, const char __user *buf, size_t siz
|
|||
{
|
||||
struct seq_file *seq = file->private_data;
|
||||
struct user_namespace *ns = seq->private;
|
||||
struct user_namespace *seq_ns = seq_user_ns(seq);
|
||||
|
||||
if (!ns->parent)
|
||||
return -EPERM;
|
||||
|
||||
if ((seq_ns != ns) && (seq_ns != ns->parent))
|
||||
return -EPERM;
|
||||
|
||||
return map_write(file, buf, size, ppos, CAP_SETGID,
|
||||
&ns->gid_map, &ns->parent->gid_map);
|
||||
}
|
||||
|
|
@ -709,6 +744,21 @@ ssize_t proc_projid_map_write(struct file *file, const char __user *buf, size_t
|
|||
static bool new_idmap_permitted(struct user_namespace *ns, int cap_setid,
|
||||
struct uid_gid_map *new_map)
|
||||
{
|
||||
/* Allow mapping to your own filesystem ids */
|
||||
if ((new_map->nr_extents == 1) && (new_map->extent[0].count == 1)) {
|
||||
u32 id = new_map->extent[0].lower_first;
|
||||
if (cap_setid == CAP_SETUID) {
|
||||
kuid_t uid = make_kuid(ns->parent, id);
|
||||
if (uid_eq(uid, current_fsuid()))
|
||||
return true;
|
||||
}
|
||||
else if (cap_setid == CAP_SETGID) {
|
||||
kgid_t gid = make_kgid(ns->parent, id);
|
||||
if (gid_eq(gid, current_fsgid()))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Allow anyone to set a mapping that doesn't require privilege */
|
||||
if (!cap_valid(cap_setid))
|
||||
return true;
|
||||
|
|
@ -722,6 +772,65 @@ static bool new_idmap_permitted(struct user_namespace *ns, int cap_setid,
|
|||
return false;
|
||||
}
|
||||
|
||||
static void *userns_get(struct task_struct *task)
|
||||
{
|
||||
struct user_namespace *user_ns;
|
||||
|
||||
rcu_read_lock();
|
||||
user_ns = get_user_ns(__task_cred(task)->user_ns);
|
||||
rcu_read_unlock();
|
||||
|
||||
return user_ns;
|
||||
}
|
||||
|
||||
static void userns_put(void *ns)
|
||||
{
|
||||
put_user_ns(ns);
|
||||
}
|
||||
|
||||
static int userns_install(struct nsproxy *nsproxy, void *ns)
|
||||
{
|
||||
struct user_namespace *user_ns = ns;
|
||||
struct cred *cred;
|
||||
|
||||
/* Don't allow gaining capabilities by reentering
|
||||
* the same user namespace.
|
||||
*/
|
||||
if (user_ns == current_user_ns())
|
||||
return -EINVAL;
|
||||
|
||||
/* Threaded processes may not enter a different user namespace */
|
||||
if (atomic_read(¤t->mm->mm_users) > 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (!ns_capable(user_ns, CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
cred = prepare_creds();
|
||||
if (!cred)
|
||||
return -ENOMEM;
|
||||
|
||||
put_user_ns(cred->user_ns);
|
||||
set_cred_user_ns(cred, get_user_ns(user_ns));
|
||||
|
||||
return commit_creds(cred);
|
||||
}
|
||||
|
||||
static unsigned int userns_inum(void *ns)
|
||||
{
|
||||
struct user_namespace *user_ns = ns;
|
||||
return user_ns->proc_inum;
|
||||
}
|
||||
|
||||
const struct proc_ns_operations userns_operations = {
|
||||
.name = "user",
|
||||
.type = CLONE_NEWUSER,
|
||||
.get = userns_get,
|
||||
.put = userns_put,
|
||||
.install = userns_install,
|
||||
.inum = userns_inum,
|
||||
};
|
||||
|
||||
static __init int user_namespaces_init(void)
|
||||
{
|
||||
user_ns_cachep = KMEM_CACHE(user_namespace, SLAB_PANIC);
|
||||
|
|
|
|||
|
|
@ -32,18 +32,25 @@ static struct uts_namespace *create_uts_ns(void)
|
|||
* @old_ns: namespace to clone
|
||||
* Return NULL on error (failure to kmalloc), new ns otherwise
|
||||
*/
|
||||
static struct uts_namespace *clone_uts_ns(struct task_struct *tsk,
|
||||
static struct uts_namespace *clone_uts_ns(struct user_namespace *user_ns,
|
||||
struct uts_namespace *old_ns)
|
||||
{
|
||||
struct uts_namespace *ns;
|
||||
int err;
|
||||
|
||||
ns = create_uts_ns();
|
||||
if (!ns)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
err = proc_alloc_inum(&ns->proc_inum);
|
||||
if (err) {
|
||||
kfree(ns);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
down_read(&uts_sem);
|
||||
memcpy(&ns->name, &old_ns->name, sizeof(ns->name));
|
||||
ns->user_ns = get_user_ns(task_cred_xxx(tsk, user_ns));
|
||||
ns->user_ns = get_user_ns(user_ns);
|
||||
up_read(&uts_sem);
|
||||
return ns;
|
||||
}
|
||||
|
|
@ -55,9 +62,8 @@ static struct uts_namespace *clone_uts_ns(struct task_struct *tsk,
|
|||
* versa.
|
||||
*/
|
||||
struct uts_namespace *copy_utsname(unsigned long flags,
|
||||
struct task_struct *tsk)
|
||||
struct user_namespace *user_ns, struct uts_namespace *old_ns)
|
||||
{
|
||||
struct uts_namespace *old_ns = tsk->nsproxy->uts_ns;
|
||||
struct uts_namespace *new_ns;
|
||||
|
||||
BUG_ON(!old_ns);
|
||||
|
|
@ -66,7 +72,7 @@ struct uts_namespace *copy_utsname(unsigned long flags,
|
|||
if (!(flags & CLONE_NEWUTS))
|
||||
return old_ns;
|
||||
|
||||
new_ns = clone_uts_ns(tsk, old_ns);
|
||||
new_ns = clone_uts_ns(user_ns, old_ns);
|
||||
|
||||
put_uts_ns(old_ns);
|
||||
return new_ns;
|
||||
|
|
@ -78,6 +84,7 @@ void free_uts_ns(struct kref *kref)
|
|||
|
||||
ns = container_of(kref, struct uts_namespace, kref);
|
||||
put_user_ns(ns->user_ns);
|
||||
proc_free_inum(ns->proc_inum);
|
||||
kfree(ns);
|
||||
}
|
||||
|
||||
|
|
@ -102,19 +109,32 @@ static void utsns_put(void *ns)
|
|||
put_uts_ns(ns);
|
||||
}
|
||||
|
||||
static int utsns_install(struct nsproxy *nsproxy, void *ns)
|
||||
static int utsns_install(struct nsproxy *nsproxy, void *new)
|
||||
{
|
||||
struct uts_namespace *ns = new;
|
||||
|
||||
if (!ns_capable(ns->user_ns, CAP_SYS_ADMIN) ||
|
||||
!nsown_capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
get_uts_ns(ns);
|
||||
put_uts_ns(nsproxy->uts_ns);
|
||||
nsproxy->uts_ns = ns;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int utsns_inum(void *vp)
|
||||
{
|
||||
struct uts_namespace *ns = vp;
|
||||
|
||||
return ns->proc_inum;
|
||||
}
|
||||
|
||||
const struct proc_ns_operations utsns_operations = {
|
||||
.name = "uts",
|
||||
.type = CLONE_NEWUTS,
|
||||
.get = utsns_get,
|
||||
.put = utsns_put,
|
||||
.install = utsns_install,
|
||||
.inum = utsns_inum,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* Generic waiting primitives.
|
||||
*
|
||||
* (C) 2004 William Irwin, Oracle
|
||||
* (C) 2004 Nadia Yvette Chambers, Oracle
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/export.h>
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue