mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-24 13:44:26 -04:00
Merge branches 'tracing/ftrace', 'tracing/kmemtrace' and 'linus' into tracing/core
This commit is contained in:
commit
dc573f9b20
1490 changed files with 27283 additions and 12277 deletions
|
|
@ -1115,8 +1115,10 @@ static void cgroup_kill_sb(struct super_block *sb) {
|
|||
}
|
||||
write_unlock(&css_set_lock);
|
||||
|
||||
list_del(&root->root_list);
|
||||
root_count--;
|
||||
if (!list_empty(&root->root_list)) {
|
||||
list_del(&root->root_list);
|
||||
root_count--;
|
||||
}
|
||||
|
||||
mutex_unlock(&cgroup_mutex);
|
||||
|
||||
|
|
@ -2434,7 +2436,9 @@ static long cgroup_create(struct cgroup *parent, struct dentry *dentry,
|
|||
|
||||
err_remove:
|
||||
|
||||
cgroup_lock_hierarchy(root);
|
||||
list_del(&cgrp->sibling);
|
||||
cgroup_unlock_hierarchy(root);
|
||||
root->number_of_cgroups--;
|
||||
|
||||
err_destroy:
|
||||
|
|
@ -2507,7 +2511,7 @@ static int cgroup_clear_css_refs(struct cgroup *cgrp)
|
|||
for_each_subsys(cgrp->root, ss) {
|
||||
struct cgroup_subsys_state *css = cgrp->subsys[ss->subsys_id];
|
||||
int refcnt;
|
||||
do {
|
||||
while (1) {
|
||||
/* We can only remove a CSS with a refcnt==1 */
|
||||
refcnt = atomic_read(&css->refcnt);
|
||||
if (refcnt > 1) {
|
||||
|
|
@ -2521,7 +2525,10 @@ static int cgroup_clear_css_refs(struct cgroup *cgrp)
|
|||
* css_tryget() to spin until we set the
|
||||
* CSS_REMOVED bits or abort
|
||||
*/
|
||||
} while (atomic_cmpxchg(&css->refcnt, refcnt, 0) != refcnt);
|
||||
if (atomic_cmpxchg(&css->refcnt, refcnt, 0) == refcnt)
|
||||
break;
|
||||
cpu_relax();
|
||||
}
|
||||
}
|
||||
done:
|
||||
for_each_subsys(cgrp->root, ss) {
|
||||
|
|
@ -2991,20 +2998,21 @@ int cgroup_clone(struct task_struct *tsk, struct cgroup_subsys *subsys,
|
|||
mutex_unlock(&cgroup_mutex);
|
||||
return 0;
|
||||
}
|
||||
task_lock(tsk);
|
||||
cg = tsk->cgroups;
|
||||
parent = task_cgroup(tsk, subsys->subsys_id);
|
||||
|
||||
/* Pin the hierarchy */
|
||||
if (!atomic_inc_not_zero(&parent->root->sb->s_active)) {
|
||||
if (!atomic_inc_not_zero(&root->sb->s_active)) {
|
||||
/* We race with the final deactivate_super() */
|
||||
mutex_unlock(&cgroup_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Keep the cgroup alive */
|
||||
task_lock(tsk);
|
||||
parent = task_cgroup(tsk, subsys->subsys_id);
|
||||
cg = tsk->cgroups;
|
||||
get_css_set(cg);
|
||||
task_unlock(tsk);
|
||||
|
||||
mutex_unlock(&cgroup_mutex);
|
||||
|
||||
/* Now do the VFS work to create a cgroup */
|
||||
|
|
@ -3043,7 +3051,7 @@ int cgroup_clone(struct task_struct *tsk, struct cgroup_subsys *subsys,
|
|||
mutex_unlock(&inode->i_mutex);
|
||||
put_css_set(cg);
|
||||
|
||||
deactivate_super(parent->root->sb);
|
||||
deactivate_super(root->sb);
|
||||
/* The cgroup is still accessible in the VFS, but
|
||||
* we're not going to try to rmdir() it at this
|
||||
* point. */
|
||||
|
|
@ -3069,7 +3077,7 @@ int cgroup_clone(struct task_struct *tsk, struct cgroup_subsys *subsys,
|
|||
mutex_lock(&cgroup_mutex);
|
||||
put_css_set(cg);
|
||||
mutex_unlock(&cgroup_mutex);
|
||||
deactivate_super(parent->root->sb);
|
||||
deactivate_super(root->sb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -60,6 +60,14 @@
|
|||
#include <linux/workqueue.h>
|
||||
#include <linux/cgroup.h>
|
||||
|
||||
/*
|
||||
* Workqueue for cpuset related tasks.
|
||||
*
|
||||
* Using kevent workqueue may cause deadlock when memory_migrate
|
||||
* is set. So we create a separate workqueue thread for cpuset.
|
||||
*/
|
||||
static struct workqueue_struct *cpuset_wq;
|
||||
|
||||
/*
|
||||
* Tracks how many cpusets are currently defined in system.
|
||||
* When there is only one cpuset (the root cpuset) we can
|
||||
|
|
@ -831,7 +839,7 @@ static DECLARE_WORK(rebuild_sched_domains_work, do_rebuild_sched_domains);
|
|||
*/
|
||||
static void async_rebuild_sched_domains(void)
|
||||
{
|
||||
schedule_work(&rebuild_sched_domains_work);
|
||||
queue_work(cpuset_wq, &rebuild_sched_domains_work);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -2111,6 +2119,9 @@ void __init cpuset_init_smp(void)
|
|||
|
||||
hotcpu_notifier(cpuset_track_online_cpus, 0);
|
||||
hotplug_memory_notifier(cpuset_track_online_nodes, 10);
|
||||
|
||||
cpuset_wq = create_singlethread_workqueue("cpuset");
|
||||
BUG_ON(!cpuset_wq);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ EXPORT_SYMBOL(dma_mark_declared_memory_occupied);
|
|||
* @size: size of requested memory area
|
||||
* @dma_handle: This will be filled with the correct dma handle
|
||||
* @ret: This pointer will be filled with the virtual address
|
||||
* to allocated area.
|
||||
* to allocated area.
|
||||
*
|
||||
* This function should be only called from per-arch dma_alloc_coherent()
|
||||
* to support allocation from per-device coherent memory pools.
|
||||
|
|
@ -118,31 +118,32 @@ int dma_alloc_from_coherent(struct device *dev, ssize_t size,
|
|||
mem = dev->dma_mem;
|
||||
if (!mem)
|
||||
return 0;
|
||||
if (unlikely(size > mem->size))
|
||||
return 0;
|
||||
|
||||
*ret = NULL;
|
||||
|
||||
if (unlikely(size > (mem->size << PAGE_SHIFT)))
|
||||
goto err;
|
||||
|
||||
pageno = bitmap_find_free_region(mem->bitmap, mem->size, order);
|
||||
if (pageno >= 0) {
|
||||
/*
|
||||
* Memory was found in the per-device arena.
|
||||
*/
|
||||
*dma_handle = mem->device_base + (pageno << PAGE_SHIFT);
|
||||
*ret = mem->virt_base + (pageno << PAGE_SHIFT);
|
||||
memset(*ret, 0, size);
|
||||
} else if (mem->flags & DMA_MEMORY_EXCLUSIVE) {
|
||||
/*
|
||||
* The per-device arena is exhausted and we are not
|
||||
* permitted to fall back to generic memory.
|
||||
*/
|
||||
*ret = NULL;
|
||||
} else {
|
||||
/*
|
||||
* The per-device arena is exhausted and we are
|
||||
* permitted to fall back to generic memory.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
if (unlikely(pageno < 0))
|
||||
goto err;
|
||||
|
||||
/*
|
||||
* Memory was found in the per-device area.
|
||||
*/
|
||||
*dma_handle = mem->device_base + (pageno << PAGE_SHIFT);
|
||||
*ret = mem->virt_base + (pageno << PAGE_SHIFT);
|
||||
memset(*ret, 0, size);
|
||||
|
||||
return 1;
|
||||
|
||||
err:
|
||||
/*
|
||||
* In the case where the allocation can not be satisfied from the
|
||||
* per-device area, try to fall back to generic memory if the
|
||||
* constraints allow it.
|
||||
*/
|
||||
return mem->flags & DMA_MEMORY_EXCLUSIVE;
|
||||
}
|
||||
EXPORT_SYMBOL(dma_alloc_from_coherent);
|
||||
|
||||
|
|
|
|||
|
|
@ -817,17 +817,17 @@ static void posix_cpu_timers_init_group(struct signal_struct *sig)
|
|||
static int copy_signal(unsigned long clone_flags, struct task_struct *tsk)
|
||||
{
|
||||
struct signal_struct *sig;
|
||||
int ret;
|
||||
|
||||
if (clone_flags & CLONE_THREAD) {
|
||||
ret = thread_group_cputime_clone_thread(current);
|
||||
if (likely(!ret)) {
|
||||
atomic_inc(¤t->signal->count);
|
||||
atomic_inc(¤t->signal->live);
|
||||
}
|
||||
return ret;
|
||||
atomic_inc(¤t->signal->count);
|
||||
atomic_inc(¤t->signal->live);
|
||||
return 0;
|
||||
}
|
||||
sig = kmem_cache_alloc(signal_cachep, GFP_KERNEL);
|
||||
|
||||
if (sig)
|
||||
posix_cpu_timers_init_group(sig);
|
||||
|
||||
tsk->signal = sig;
|
||||
if (!sig)
|
||||
return -ENOMEM;
|
||||
|
|
@ -864,8 +864,6 @@ static int copy_signal(unsigned long clone_flags, struct task_struct *tsk)
|
|||
memcpy(sig->rlim, current->signal->rlim, sizeof sig->rlim);
|
||||
task_unlock(current->group_leader);
|
||||
|
||||
posix_cpu_timers_init_group(sig);
|
||||
|
||||
acct_init_pacct(&sig->pacct);
|
||||
|
||||
tty_audit_fork(sig);
|
||||
|
|
|
|||
|
|
@ -501,6 +501,13 @@ static void hrtimer_force_reprogram(struct hrtimer_cpu_base *cpu_base)
|
|||
continue;
|
||||
timer = rb_entry(base->first, struct hrtimer, node);
|
||||
expires = ktime_sub(hrtimer_get_expires(timer), base->offset);
|
||||
/*
|
||||
* clock_was_set() has changed base->offset so the
|
||||
* result might be negative. Fix it up to prevent a
|
||||
* false positive in clockevents_program_event()
|
||||
*/
|
||||
if (expires.tv64 < 0)
|
||||
expires.tv64 = 0;
|
||||
if (expires.tv64 < cpu_base->expires_next.tv64)
|
||||
cpu_base->expires_next = expires;
|
||||
}
|
||||
|
|
@ -614,7 +621,9 @@ void clock_was_set(void)
|
|||
*/
|
||||
void hres_timers_resume(void)
|
||||
{
|
||||
/* Retrigger the CPU local events: */
|
||||
WARN_ONCE(!irqs_disabled(),
|
||||
KERN_INFO "hres_timers_resume() called with IRQs enabled!");
|
||||
|
||||
retrigger_next_event(NULL);
|
||||
}
|
||||
|
||||
|
|
@ -1156,6 +1165,29 @@ static void __run_hrtimer(struct hrtimer *timer)
|
|||
|
||||
#ifdef CONFIG_HIGH_RES_TIMERS
|
||||
|
||||
static int force_clock_reprogram;
|
||||
|
||||
/*
|
||||
* After 5 iteration's attempts, we consider that hrtimer_interrupt()
|
||||
* is hanging, which could happen with something that slows the interrupt
|
||||
* such as the tracing. Then we force the clock reprogramming for each future
|
||||
* hrtimer interrupts to avoid infinite loops and use the min_delta_ns
|
||||
* threshold that we will overwrite.
|
||||
* The next tick event will be scheduled to 3 times we currently spend on
|
||||
* hrtimer_interrupt(). This gives a good compromise, the cpus will spend
|
||||
* 1/4 of their time to process the hrtimer interrupts. This is enough to
|
||||
* let it running without serious starvation.
|
||||
*/
|
||||
|
||||
static inline void
|
||||
hrtimer_interrupt_hanging(struct clock_event_device *dev,
|
||||
ktime_t try_time)
|
||||
{
|
||||
force_clock_reprogram = 1;
|
||||
dev->min_delta_ns = (unsigned long)try_time.tv64 * 3;
|
||||
printk(KERN_WARNING "hrtimer: interrupt too slow, "
|
||||
"forcing clock min delta to %lu ns\n", dev->min_delta_ns);
|
||||
}
|
||||
/*
|
||||
* High resolution timer interrupt
|
||||
* Called with interrupts disabled
|
||||
|
|
@ -1165,6 +1197,7 @@ void hrtimer_interrupt(struct clock_event_device *dev)
|
|||
struct hrtimer_cpu_base *cpu_base = &__get_cpu_var(hrtimer_bases);
|
||||
struct hrtimer_clock_base *base;
|
||||
ktime_t expires_next, now;
|
||||
int nr_retries = 0;
|
||||
int i;
|
||||
|
||||
BUG_ON(!cpu_base->hres_active);
|
||||
|
|
@ -1172,6 +1205,10 @@ void hrtimer_interrupt(struct clock_event_device *dev)
|
|||
dev->next_event.tv64 = KTIME_MAX;
|
||||
|
||||
retry:
|
||||
/* 5 retries is enough to notice a hang */
|
||||
if (!(++nr_retries % 5))
|
||||
hrtimer_interrupt_hanging(dev, ktime_sub(ktime_get(), now));
|
||||
|
||||
now = ktime_get();
|
||||
|
||||
expires_next.tv64 = KTIME_MAX;
|
||||
|
|
@ -1224,7 +1261,7 @@ void hrtimer_interrupt(struct clock_event_device *dev)
|
|||
|
||||
/* Reprogramming necessary ? */
|
||||
if (expires_next.tv64 != KTIME_MAX) {
|
||||
if (tick_program_event(expires_next, 0))
|
||||
if (tick_program_event(expires_next, force_clock_reprogram))
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
|
|
@ -1578,6 +1615,10 @@ static int __cpuinit hrtimer_cpu_notify(struct notifier_block *self,
|
|||
break;
|
||||
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
case CPU_DYING:
|
||||
case CPU_DYING_FROZEN:
|
||||
clockevents_notify(CLOCK_EVT_NOTIFY_CPU_DYING, &scpu);
|
||||
break;
|
||||
case CPU_DEAD:
|
||||
case CPU_DEAD_FROZEN:
|
||||
{
|
||||
|
|
|
|||
|
|
@ -383,6 +383,7 @@ handle_level_irq(unsigned int irq, struct irq_desc *desc)
|
|||
out_unlock:
|
||||
spin_unlock(&desc->lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(handle_level_irq);
|
||||
|
||||
/**
|
||||
* handle_fasteoi_irq - irq handler for transparent controllers
|
||||
|
|
@ -593,6 +594,7 @@ __set_irq_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
|
|||
}
|
||||
spin_unlock_irqrestore(&desc->lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__set_irq_handler);
|
||||
|
||||
void
|
||||
set_irq_chip_and_handler(unsigned int irq, struct irq_chip *chip,
|
||||
|
|
|
|||
|
|
@ -39,6 +39,18 @@ void handle_bad_irq(unsigned int irq, struct irq_desc *desc)
|
|||
ack_bad_irq(irq);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_SMP) && defined(CONFIG_GENERIC_HARDIRQS)
|
||||
static void __init init_irq_default_affinity(void)
|
||||
{
|
||||
alloc_bootmem_cpumask_var(&irq_default_affinity);
|
||||
cpumask_setall(irq_default_affinity);
|
||||
}
|
||||
#else
|
||||
static void __init init_irq_default_affinity(void)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Linux has a controller-independent interrupt architecture.
|
||||
* Every controller has a 'controller-template', that is used
|
||||
|
|
@ -134,6 +146,8 @@ int __init early_irq_init(void)
|
|||
int legacy_count;
|
||||
int i;
|
||||
|
||||
init_irq_default_affinity();
|
||||
|
||||
desc = irq_desc_legacy;
|
||||
legacy_count = ARRAY_SIZE(irq_desc_legacy);
|
||||
|
||||
|
|
@ -219,6 +233,8 @@ int __init early_irq_init(void)
|
|||
int count;
|
||||
int i;
|
||||
|
||||
init_irq_default_affinity();
|
||||
|
||||
desc = irq_desc;
|
||||
count = ARRAY_SIZE(irq_desc);
|
||||
|
||||
|
|
|
|||
|
|
@ -15,17 +15,9 @@
|
|||
|
||||
#include "internals.h"
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
#if defined(CONFIG_SMP) && defined(CONFIG_GENERIC_HARDIRQS)
|
||||
cpumask_var_t irq_default_affinity;
|
||||
|
||||
static int init_irq_default_affinity(void)
|
||||
{
|
||||
alloc_cpumask_var(&irq_default_affinity, GFP_KERNEL);
|
||||
cpumask_setall(irq_default_affinity);
|
||||
return 0;
|
||||
}
|
||||
core_initcall(init_irq_default_affinity);
|
||||
|
||||
/**
|
||||
* synchronize_irq - wait for pending IRQ handlers (on other CPUs)
|
||||
* @irq: interrupt number to wait for
|
||||
|
|
|
|||
|
|
@ -30,19 +30,20 @@
|
|||
#define all_var 0
|
||||
#endif
|
||||
|
||||
extern const unsigned long kallsyms_addresses[];
|
||||
extern const u8 kallsyms_names[];
|
||||
/* These will be re-linked against their real values during the second link stage */
|
||||
extern const unsigned long kallsyms_addresses[] __attribute__((weak));
|
||||
extern const u8 kallsyms_names[] __attribute__((weak));
|
||||
|
||||
/* tell the compiler that the count isn't in the small data section if the arch
|
||||
* has one (eg: FRV)
|
||||
*/
|
||||
extern const unsigned long kallsyms_num_syms
|
||||
__attribute__((__section__(".rodata")));
|
||||
__attribute__((weak, section(".rodata")));
|
||||
|
||||
extern const u8 kallsyms_token_table[];
|
||||
extern const u16 kallsyms_token_index[];
|
||||
extern const u8 kallsyms_token_table[] __attribute__((weak));
|
||||
extern const u16 kallsyms_token_index[] __attribute__((weak));
|
||||
|
||||
extern const unsigned long kallsyms_markers[];
|
||||
extern const unsigned long kallsyms_markers[] __attribute__((weak));
|
||||
|
||||
static inline int is_kernel_inittext(unsigned long addr)
|
||||
{
|
||||
|
|
@ -167,6 +168,9 @@ static unsigned long get_symbol_pos(unsigned long addr,
|
|||
unsigned long symbol_start = 0, symbol_end = 0;
|
||||
unsigned long i, low, high, mid;
|
||||
|
||||
/* This kernel should never had been booted. */
|
||||
BUG_ON(!kallsyms_addresses);
|
||||
|
||||
/* do a binary search on the sorted kallsyms_addresses array */
|
||||
low = 0;
|
||||
high = kallsyms_num_syms;
|
||||
|
|
|
|||
|
|
@ -573,13 +573,13 @@ static char last_unloaded_module[MODULE_NAME_LEN+1];
|
|||
/* Init the unload section of the module. */
|
||||
static void module_unload_init(struct module *mod)
|
||||
{
|
||||
unsigned int i;
|
||||
int cpu;
|
||||
|
||||
INIT_LIST_HEAD(&mod->modules_which_use_me);
|
||||
for (i = 0; i < NR_CPUS; i++)
|
||||
local_set(&mod->ref[i].count, 0);
|
||||
for_each_possible_cpu(cpu)
|
||||
local_set(__module_ref_addr(mod, cpu), 0);
|
||||
/* Hold reference count during initialization. */
|
||||
local_set(&mod->ref[raw_smp_processor_id()].count, 1);
|
||||
local_set(__module_ref_addr(mod, raw_smp_processor_id()), 1);
|
||||
/* Backwards compatibility macros put refcount during init. */
|
||||
mod->waiter = current;
|
||||
}
|
||||
|
|
@ -717,10 +717,11 @@ static int try_stop_module(struct module *mod, int flags, int *forced)
|
|||
|
||||
unsigned int module_refcount(struct module *mod)
|
||||
{
|
||||
unsigned int i, total = 0;
|
||||
unsigned int total = 0;
|
||||
int cpu;
|
||||
|
||||
for (i = 0; i < NR_CPUS; i++)
|
||||
total += local_read(&mod->ref[i].count);
|
||||
for_each_possible_cpu(cpu)
|
||||
total += local_read(__module_ref_addr(mod, cpu));
|
||||
return total;
|
||||
}
|
||||
EXPORT_SYMBOL(module_refcount);
|
||||
|
|
@ -894,7 +895,7 @@ void module_put(struct module *module)
|
|||
{
|
||||
if (module) {
|
||||
unsigned int cpu = get_cpu();
|
||||
local_dec(&module->ref[cpu].count);
|
||||
local_dec(__module_ref_addr(module, cpu));
|
||||
/* Maybe they're waiting for us to drop reference? */
|
||||
if (unlikely(!module_is_live(module)))
|
||||
wake_up_process(module->waiter);
|
||||
|
|
@ -1464,7 +1465,10 @@ static void free_module(struct module *mod)
|
|||
kfree(mod->args);
|
||||
if (mod->percpu)
|
||||
percpu_modfree(mod->percpu);
|
||||
|
||||
#if defined(CONFIG_MODULE_UNLOAD) && defined(CONFIG_SMP)
|
||||
if (mod->refptr)
|
||||
percpu_modfree(mod->refptr);
|
||||
#endif
|
||||
/* Free lock-classes: */
|
||||
lockdep_free_key_range(mod->module_core, mod->core_size);
|
||||
|
||||
|
|
@ -2011,6 +2015,14 @@ static noinline struct module *load_module(void __user *umod,
|
|||
if (err < 0)
|
||||
goto free_mod;
|
||||
|
||||
#if defined(CONFIG_MODULE_UNLOAD) && defined(CONFIG_SMP)
|
||||
mod->refptr = percpu_modalloc(sizeof(local_t), __alignof__(local_t),
|
||||
mod->name);
|
||||
if (!mod->refptr) {
|
||||
err = -ENOMEM;
|
||||
goto free_mod;
|
||||
}
|
||||
#endif
|
||||
if (pcpuindex) {
|
||||
/* We have a special allocation for this section. */
|
||||
percpu = percpu_modalloc(sechdrs[pcpuindex].sh_size,
|
||||
|
|
@ -2018,7 +2030,7 @@ static noinline struct module *load_module(void __user *umod,
|
|||
mod->name);
|
||||
if (!percpu) {
|
||||
err = -ENOMEM;
|
||||
goto free_mod;
|
||||
goto free_percpu;
|
||||
}
|
||||
sechdrs[pcpuindex].sh_flags &= ~(unsigned long)SHF_ALLOC;
|
||||
mod->percpu = percpu;
|
||||
|
|
@ -2282,6 +2294,9 @@ static noinline struct module *load_module(void __user *umod,
|
|||
free_percpu:
|
||||
if (percpu)
|
||||
percpu_modfree(percpu);
|
||||
#if defined(CONFIG_MODULE_UNLOAD) && defined(CONFIG_SMP)
|
||||
percpu_modfree(mod->refptr);
|
||||
#endif
|
||||
free_mod:
|
||||
kfree(args);
|
||||
free_hdr:
|
||||
|
|
|
|||
|
|
@ -9,76 +9,6 @@
|
|||
#include <asm/uaccess.h>
|
||||
#include <linux/kernel_stat.h>
|
||||
|
||||
/*
|
||||
* Allocate the thread_group_cputime structure appropriately and fill in the
|
||||
* current values of the fields. Called from copy_signal() via
|
||||
* thread_group_cputime_clone_thread() when adding a second or subsequent
|
||||
* thread to a thread group. Assumes interrupts are enabled when called.
|
||||
*/
|
||||
int thread_group_cputime_alloc(struct task_struct *tsk)
|
||||
{
|
||||
struct signal_struct *sig = tsk->signal;
|
||||
struct task_cputime *cputime;
|
||||
|
||||
/*
|
||||
* If we have multiple threads and we don't already have a
|
||||
* per-CPU task_cputime struct (checked in the caller), allocate
|
||||
* one and fill it in with the times accumulated so far. We may
|
||||
* race with another thread so recheck after we pick up the sighand
|
||||
* lock.
|
||||
*/
|
||||
cputime = alloc_percpu(struct task_cputime);
|
||||
if (cputime == NULL)
|
||||
return -ENOMEM;
|
||||
spin_lock_irq(&tsk->sighand->siglock);
|
||||
if (sig->cputime.totals) {
|
||||
spin_unlock_irq(&tsk->sighand->siglock);
|
||||
free_percpu(cputime);
|
||||
return 0;
|
||||
}
|
||||
sig->cputime.totals = cputime;
|
||||
cputime = per_cpu_ptr(sig->cputime.totals, smp_processor_id());
|
||||
cputime->utime = tsk->utime;
|
||||
cputime->stime = tsk->stime;
|
||||
cputime->sum_exec_runtime = tsk->se.sum_exec_runtime;
|
||||
spin_unlock_irq(&tsk->sighand->siglock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* thread_group_cputime - Sum the thread group time fields across all CPUs.
|
||||
*
|
||||
* @tsk: The task we use to identify the thread group.
|
||||
* @times: task_cputime structure in which we return the summed fields.
|
||||
*
|
||||
* Walk the list of CPUs to sum the per-CPU time fields in the thread group
|
||||
* time structure.
|
||||
*/
|
||||
void thread_group_cputime(
|
||||
struct task_struct *tsk,
|
||||
struct task_cputime *times)
|
||||
{
|
||||
struct task_cputime *totals, *tot;
|
||||
int i;
|
||||
|
||||
totals = tsk->signal->cputime.totals;
|
||||
if (!totals) {
|
||||
times->utime = tsk->utime;
|
||||
times->stime = tsk->stime;
|
||||
times->sum_exec_runtime = tsk->se.sum_exec_runtime;
|
||||
return;
|
||||
}
|
||||
|
||||
times->stime = times->utime = cputime_zero;
|
||||
times->sum_exec_runtime = 0;
|
||||
for_each_possible_cpu(i) {
|
||||
tot = per_cpu_ptr(totals, i);
|
||||
times->utime = cputime_add(times->utime, tot->utime);
|
||||
times->stime = cputime_add(times->stime, tot->stime);
|
||||
times->sum_exec_runtime += tot->sum_exec_runtime;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Called after updating RLIMIT_CPU to set timer expiration if necessary.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -71,6 +71,14 @@ void hibernation_set_ops(struct platform_hibernation_ops *ops)
|
|||
mutex_unlock(&pm_mutex);
|
||||
}
|
||||
|
||||
static bool entering_platform_hibernation;
|
||||
|
||||
bool system_entering_hibernation(void)
|
||||
{
|
||||
return entering_platform_hibernation;
|
||||
}
|
||||
EXPORT_SYMBOL(system_entering_hibernation);
|
||||
|
||||
#ifdef CONFIG_PM_DEBUG
|
||||
static void hibernation_debug_sleep(void)
|
||||
{
|
||||
|
|
@ -411,6 +419,7 @@ int hibernation_platform_enter(void)
|
|||
if (error)
|
||||
goto Close;
|
||||
|
||||
entering_platform_hibernation = true;
|
||||
suspend_console();
|
||||
error = device_suspend(PMSG_HIBERNATE);
|
||||
if (error) {
|
||||
|
|
@ -445,6 +454,7 @@ int hibernation_platform_enter(void)
|
|||
Finish:
|
||||
hibernation_ops->finish();
|
||||
Resume_devices:
|
||||
entering_platform_hibernation = false;
|
||||
device_resume(PMSG_RESTORE);
|
||||
resume_console();
|
||||
Close:
|
||||
|
|
|
|||
|
|
@ -716,7 +716,7 @@ void rcu_check_callbacks(int cpu, int user)
|
|||
raise_rcu_softirq();
|
||||
}
|
||||
|
||||
static void rcu_init_percpu_data(int cpu, struct rcu_ctrlblk *rcp,
|
||||
static void __cpuinit rcu_init_percpu_data(int cpu, struct rcu_ctrlblk *rcp,
|
||||
struct rcu_data *rdp)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
|
|
|||
|
|
@ -1314,7 +1314,7 @@ int rcu_needs_cpu(int cpu)
|
|||
* access due to the fact that this CPU cannot possibly have any RCU
|
||||
* callbacks in flight yet.
|
||||
*/
|
||||
static void
|
||||
static void __cpuinit
|
||||
rcu_init_percpu_data(int cpu, struct rcu_state *rsp)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
|
|
|||
|
|
@ -663,8 +663,10 @@ int relay_late_setup_files(struct rchan *chan,
|
|||
|
||||
mutex_lock(&relay_channels_mutex);
|
||||
/* Is chan already set up? */
|
||||
if (unlikely(chan->has_base_filename))
|
||||
if (unlikely(chan->has_base_filename)) {
|
||||
mutex_unlock(&relay_channels_mutex);
|
||||
return -EEXIST;
|
||||
}
|
||||
chan->has_base_filename = 1;
|
||||
chan->parent = parent;
|
||||
curr_cpu = get_cpu();
|
||||
|
|
|
|||
|
|
@ -2266,6 +2266,16 @@ static int try_to_wake_up(struct task_struct *p, unsigned int state, int sync)
|
|||
if (!sched_feat(SYNC_WAKEUPS))
|
||||
sync = 0;
|
||||
|
||||
if (!sync) {
|
||||
if (current->se.avg_overlap < sysctl_sched_migration_cost &&
|
||||
p->se.avg_overlap < sysctl_sched_migration_cost)
|
||||
sync = 1;
|
||||
} else {
|
||||
if (current->se.avg_overlap >= sysctl_sched_migration_cost ||
|
||||
p->se.avg_overlap >= sysctl_sched_migration_cost)
|
||||
sync = 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
if (sched_feat(LB_WAKEUP_UPDATE)) {
|
||||
struct sched_domain *sd;
|
||||
|
|
|
|||
|
|
@ -719,7 +719,7 @@ enqueue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int wakeup)
|
|||
__enqueue_entity(cfs_rq, se);
|
||||
}
|
||||
|
||||
static void clear_buddies(struct cfs_rq *cfs_rq, struct sched_entity *se)
|
||||
static void __clear_buddies(struct cfs_rq *cfs_rq, struct sched_entity *se)
|
||||
{
|
||||
if (cfs_rq->last == se)
|
||||
cfs_rq->last = NULL;
|
||||
|
|
@ -728,6 +728,12 @@ static void clear_buddies(struct cfs_rq *cfs_rq, struct sched_entity *se)
|
|||
cfs_rq->next = NULL;
|
||||
}
|
||||
|
||||
static void clear_buddies(struct cfs_rq *cfs_rq, struct sched_entity *se)
|
||||
{
|
||||
for_each_sched_entity(se)
|
||||
__clear_buddies(cfs_rq_of(se), se);
|
||||
}
|
||||
|
||||
static void
|
||||
dequeue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int sleep)
|
||||
{
|
||||
|
|
@ -768,8 +774,14 @@ check_preempt_tick(struct cfs_rq *cfs_rq, struct sched_entity *curr)
|
|||
|
||||
ideal_runtime = sched_slice(cfs_rq, curr);
|
||||
delta_exec = curr->sum_exec_runtime - curr->prev_sum_exec_runtime;
|
||||
if (delta_exec > ideal_runtime)
|
||||
if (delta_exec > ideal_runtime) {
|
||||
resched_task(rq_of(cfs_rq)->curr);
|
||||
/*
|
||||
* The current task ran long enough, ensure it doesn't get
|
||||
* re-elected due to buddy favours.
|
||||
*/
|
||||
clear_buddies(cfs_rq, curr);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -1179,20 +1191,15 @@ wake_affine(struct sched_domain *this_sd, struct rq *this_rq,
|
|||
int idx, unsigned long load, unsigned long this_load,
|
||||
unsigned int imbalance)
|
||||
{
|
||||
struct task_struct *curr = this_rq->curr;
|
||||
struct task_group *tg;
|
||||
unsigned long tl = this_load;
|
||||
unsigned long tl_per_task;
|
||||
struct task_group *tg;
|
||||
unsigned long weight;
|
||||
int balanced;
|
||||
|
||||
if (!(this_sd->flags & SD_WAKE_AFFINE) || !sched_feat(AFFINE_WAKEUPS))
|
||||
return 0;
|
||||
|
||||
if (sync && (curr->se.avg_overlap > sysctl_sched_migration_cost ||
|
||||
p->se.avg_overlap > sysctl_sched_migration_cost))
|
||||
sync = 0;
|
||||
|
||||
/*
|
||||
* If sync wakeup then subtract the (maximum possible)
|
||||
* effect of the currently running task from the load
|
||||
|
|
@ -1419,9 +1426,7 @@ static void check_preempt_wakeup(struct rq *rq, struct task_struct *p, int sync)
|
|||
if (!sched_feat(WAKEUP_PREEMPT))
|
||||
return;
|
||||
|
||||
if (sched_feat(WAKEUP_OVERLAP) && (sync ||
|
||||
(se->avg_overlap < sysctl_sched_migration_cost &&
|
||||
pse->avg_overlap < sysctl_sched_migration_cost))) {
|
||||
if (sched_feat(WAKEUP_OVERLAP) && sync) {
|
||||
resched_task(curr);
|
||||
return;
|
||||
}
|
||||
|
|
@ -1452,6 +1457,11 @@ static struct task_struct *pick_next_task_fair(struct rq *rq)
|
|||
|
||||
do {
|
||||
se = pick_next_entity(cfs_rq);
|
||||
/*
|
||||
* If se was a buddy, clear it so that it will have to earn
|
||||
* the favour again.
|
||||
*/
|
||||
__clear_buddies(cfs_rq, se);
|
||||
set_next_entity(cfs_rq, se);
|
||||
cfs_rq = group_cfs_rq(se);
|
||||
} while (cfs_rq);
|
||||
|
|
|
|||
|
|
@ -968,8 +968,8 @@ static inline int pick_optimal_cpu(int this_cpu, cpumask_t *mask)
|
|||
if ((this_cpu != -1) && cpu_isset(this_cpu, *mask))
|
||||
return this_cpu;
|
||||
|
||||
first = first_cpu(*mask);
|
||||
if (first != NR_CPUS)
|
||||
first = cpumask_first(mask);
|
||||
if (first < nr_cpu_ids)
|
||||
return first;
|
||||
|
||||
return -1;
|
||||
|
|
|
|||
|
|
@ -296,6 +296,7 @@ sched_info_switch(struct task_struct *prev, struct task_struct *next)
|
|||
static inline void account_group_user_time(struct task_struct *tsk,
|
||||
cputime_t cputime)
|
||||
{
|
||||
struct task_cputime *times;
|
||||
struct signal_struct *sig;
|
||||
|
||||
/* tsk == current, ensure it is safe to use ->signal */
|
||||
|
|
@ -303,13 +304,11 @@ static inline void account_group_user_time(struct task_struct *tsk,
|
|||
return;
|
||||
|
||||
sig = tsk->signal;
|
||||
if (sig->cputime.totals) {
|
||||
struct task_cputime *times;
|
||||
times = &sig->cputime.totals;
|
||||
|
||||
times = per_cpu_ptr(sig->cputime.totals, get_cpu());
|
||||
times->utime = cputime_add(times->utime, cputime);
|
||||
put_cpu_no_resched();
|
||||
}
|
||||
spin_lock(×->lock);
|
||||
times->utime = cputime_add(times->utime, cputime);
|
||||
spin_unlock(×->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -325,6 +324,7 @@ static inline void account_group_user_time(struct task_struct *tsk,
|
|||
static inline void account_group_system_time(struct task_struct *tsk,
|
||||
cputime_t cputime)
|
||||
{
|
||||
struct task_cputime *times;
|
||||
struct signal_struct *sig;
|
||||
|
||||
/* tsk == current, ensure it is safe to use ->signal */
|
||||
|
|
@ -332,13 +332,11 @@ static inline void account_group_system_time(struct task_struct *tsk,
|
|||
return;
|
||||
|
||||
sig = tsk->signal;
|
||||
if (sig->cputime.totals) {
|
||||
struct task_cputime *times;
|
||||
times = &sig->cputime.totals;
|
||||
|
||||
times = per_cpu_ptr(sig->cputime.totals, get_cpu());
|
||||
times->stime = cputime_add(times->stime, cputime);
|
||||
put_cpu_no_resched();
|
||||
}
|
||||
spin_lock(×->lock);
|
||||
times->stime = cputime_add(times->stime, cputime);
|
||||
spin_unlock(×->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -354,6 +352,7 @@ static inline void account_group_system_time(struct task_struct *tsk,
|
|||
static inline void account_group_exec_runtime(struct task_struct *tsk,
|
||||
unsigned long long ns)
|
||||
{
|
||||
struct task_cputime *times;
|
||||
struct signal_struct *sig;
|
||||
|
||||
sig = tsk->signal;
|
||||
|
|
@ -362,11 +361,9 @@ static inline void account_group_exec_runtime(struct task_struct *tsk,
|
|||
if (unlikely(!sig))
|
||||
return;
|
||||
|
||||
if (sig->cputime.totals) {
|
||||
struct task_cputime *times;
|
||||
times = &sig->cputime.totals;
|
||||
|
||||
times = per_cpu_ptr(sig->cputime.totals, get_cpu());
|
||||
times->sum_exec_runtime += ns;
|
||||
put_cpu_no_resched();
|
||||
}
|
||||
spin_lock(×->lock);
|
||||
times->sum_exec_runtime += ns;
|
||||
spin_unlock(×->lock);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -909,7 +909,9 @@ static void print_fatal_signal(struct pt_regs *regs, int signr)
|
|||
}
|
||||
#endif
|
||||
printk("\n");
|
||||
preempt_disable();
|
||||
show_regs(regs);
|
||||
preempt_enable();
|
||||
}
|
||||
|
||||
static int __init setup_print_fatal_signals(char *str)
|
||||
|
|
|
|||
36
kernel/smp.c
36
kernel/smp.c
|
|
@ -18,6 +18,7 @@ __cacheline_aligned_in_smp DEFINE_SPINLOCK(call_function_lock);
|
|||
enum {
|
||||
CSD_FLAG_WAIT = 0x01,
|
||||
CSD_FLAG_ALLOC = 0x02,
|
||||
CSD_FLAG_LOCK = 0x04,
|
||||
};
|
||||
|
||||
struct call_function_data {
|
||||
|
|
@ -186,6 +187,9 @@ void generic_smp_call_function_single_interrupt(void)
|
|||
if (data_flags & CSD_FLAG_WAIT) {
|
||||
smp_wmb();
|
||||
data->flags &= ~CSD_FLAG_WAIT;
|
||||
} else if (data_flags & CSD_FLAG_LOCK) {
|
||||
smp_wmb();
|
||||
data->flags &= ~CSD_FLAG_LOCK;
|
||||
} else if (data_flags & CSD_FLAG_ALLOC)
|
||||
kfree(data);
|
||||
}
|
||||
|
|
@ -196,6 +200,8 @@ void generic_smp_call_function_single_interrupt(void)
|
|||
}
|
||||
}
|
||||
|
||||
static DEFINE_PER_CPU(struct call_single_data, csd_data);
|
||||
|
||||
/*
|
||||
* smp_call_function_single - Run a function on a specific CPU
|
||||
* @func: The function to run. This must be fast and non-blocking.
|
||||
|
|
@ -224,14 +230,38 @@ int smp_call_function_single(int cpu, void (*func) (void *info), void *info,
|
|||
func(info);
|
||||
local_irq_restore(flags);
|
||||
} else if ((unsigned)cpu < nr_cpu_ids && cpu_online(cpu)) {
|
||||
struct call_single_data *data = NULL;
|
||||
struct call_single_data *data;
|
||||
|
||||
if (!wait) {
|
||||
/*
|
||||
* We are calling a function on a single CPU
|
||||
* and we are not going to wait for it to finish.
|
||||
* We first try to allocate the data, but if we
|
||||
* fail, we fall back to use a per cpu data to pass
|
||||
* the information to that CPU. Since all callers
|
||||
* of this code will use the same data, we must
|
||||
* synchronize the callers to prevent a new caller
|
||||
* from corrupting the data before the callee
|
||||
* can access it.
|
||||
*
|
||||
* The CSD_FLAG_LOCK is used to let us know when
|
||||
* the IPI handler is done with the data.
|
||||
* The first caller will set it, and the callee
|
||||
* will clear it. The next caller must wait for
|
||||
* it to clear before we set it again. This
|
||||
* will make sure the callee is done with the
|
||||
* data before a new caller will use it.
|
||||
*/
|
||||
data = kmalloc(sizeof(*data), GFP_ATOMIC);
|
||||
if (data)
|
||||
data->flags = CSD_FLAG_ALLOC;
|
||||
}
|
||||
if (!data) {
|
||||
else {
|
||||
data = &per_cpu(csd_data, me);
|
||||
while (data->flags & CSD_FLAG_LOCK)
|
||||
cpu_relax();
|
||||
data->flags = CSD_FLAG_LOCK;
|
||||
}
|
||||
} else {
|
||||
data = &d;
|
||||
data->flags = CSD_FLAG_WAIT;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
#include <linux/lockdep.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/sysctl.h>
|
||||
|
||||
#include <asm/irq_regs.h>
|
||||
|
||||
|
|
@ -88,6 +89,14 @@ void touch_all_softlockup_watchdogs(void)
|
|||
}
|
||||
EXPORT_SYMBOL(touch_all_softlockup_watchdogs);
|
||||
|
||||
int proc_dosoftlockup_thresh(struct ctl_table *table, int write,
|
||||
struct file *filp, void __user *buffer,
|
||||
size_t *lenp, loff_t *ppos)
|
||||
{
|
||||
touch_all_softlockup_watchdogs();
|
||||
return proc_dointvec_minmax(table, write, filp, buffer, lenp, ppos);
|
||||
}
|
||||
|
||||
/*
|
||||
* This callback runs from the timer interrupt, and checks
|
||||
* whether the watchdog thread has hung or not:
|
||||
|
|
|
|||
|
|
@ -809,7 +809,7 @@ static struct ctl_table kern_table[] = {
|
|||
.data = &softlockup_thresh,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_minmax,
|
||||
.proc_handler = &proc_dosoftlockup_thresh,
|
||||
.strategy = &sysctl_intvec,
|
||||
.extra1 = &neg_one,
|
||||
.extra2 = &sixty,
|
||||
|
|
|
|||
|
|
@ -273,6 +273,21 @@ out_bc:
|
|||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Transfer the do_timer job away from a dying cpu.
|
||||
*
|
||||
* Called with interrupts disabled.
|
||||
*/
|
||||
static void tick_handover_do_timer(int *cpup)
|
||||
{
|
||||
if (*cpup == tick_do_timer_cpu) {
|
||||
int cpu = cpumask_first(cpu_online_mask);
|
||||
|
||||
tick_do_timer_cpu = (cpu < nr_cpu_ids) ? cpu :
|
||||
TICK_DO_TIMER_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Shutdown an event device on a given cpu:
|
||||
*
|
||||
|
|
@ -297,13 +312,6 @@ static void tick_shutdown(unsigned int *cpup)
|
|||
clockevents_exchange_device(dev, NULL);
|
||||
td->evtdev = NULL;
|
||||
}
|
||||
/* Transfer the do_timer job away from this cpu */
|
||||
if (*cpup == tick_do_timer_cpu) {
|
||||
int cpu = cpumask_first(cpu_online_mask);
|
||||
|
||||
tick_do_timer_cpu = (cpu < nr_cpu_ids) ? cpu :
|
||||
TICK_DO_TIMER_NONE;
|
||||
}
|
||||
spin_unlock_irqrestore(&tick_device_lock, flags);
|
||||
}
|
||||
|
||||
|
|
@ -357,6 +365,10 @@ static int tick_notify(struct notifier_block *nb, unsigned long reason,
|
|||
tick_broadcast_oneshot_control(reason);
|
||||
break;
|
||||
|
||||
case CLOCK_EVT_NOTIFY_CPU_DYING:
|
||||
tick_handover_do_timer(dev);
|
||||
break;
|
||||
|
||||
case CLOCK_EVT_NOTIFY_CPU_DEAD:
|
||||
tick_shutdown_broadcast_oneshot(dev);
|
||||
tick_shutdown_broadcast(dev);
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ __setup("nohz=", setup_tick_nohz);
|
|||
* value. We do this unconditionally on any cpu, as we don't know whether the
|
||||
* cpu, which has the update task assigned is in a long sleep.
|
||||
*/
|
||||
void tick_nohz_update_jiffies(void)
|
||||
static void tick_nohz_update_jiffies(void)
|
||||
{
|
||||
int cpu = smp_processor_id();
|
||||
struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu);
|
||||
|
|
|
|||
|
|
@ -139,12 +139,12 @@ kmemtrace_print_alloc_compress(struct trace_iterator *iter,
|
|||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
/* Requested */
|
||||
ret = trace_seq_printf(s, "%4zd ", entry->bytes_req);
|
||||
ret = trace_seq_printf(s, "%4zu ", entry->bytes_req);
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
/* Allocated */
|
||||
ret = trace_seq_printf(s, "%4zd ", entry->bytes_alloc);
|
||||
ret = trace_seq_printf(s, "%4zu ", entry->bytes_alloc);
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
|
|
|
|||
|
|
@ -985,6 +985,8 @@ undo:
|
|||
}
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
static struct workqueue_struct *work_on_cpu_wq __read_mostly;
|
||||
|
||||
struct work_for_cpu {
|
||||
struct work_struct work;
|
||||
long (*fn)(void *);
|
||||
|
|
@ -1005,8 +1007,8 @@ static void do_work_for_cpu(struct work_struct *w)
|
|||
* @fn: the function to run
|
||||
* @arg: the function arg
|
||||
*
|
||||
* This will return -EINVAL in the cpu is not online, or the return value
|
||||
* of @fn otherwise.
|
||||
* This will return the value @fn returns.
|
||||
* It is up to the caller to ensure that the cpu doesn't go offline.
|
||||
*/
|
||||
long work_on_cpu(unsigned int cpu, long (*fn)(void *), void *arg)
|
||||
{
|
||||
|
|
@ -1015,14 +1017,8 @@ long work_on_cpu(unsigned int cpu, long (*fn)(void *), void *arg)
|
|||
INIT_WORK(&wfc.work, do_work_for_cpu);
|
||||
wfc.fn = fn;
|
||||
wfc.arg = arg;
|
||||
get_online_cpus();
|
||||
if (unlikely(!cpu_online(cpu)))
|
||||
wfc.ret = -EINVAL;
|
||||
else {
|
||||
schedule_work_on(cpu, &wfc.work);
|
||||
flush_work(&wfc.work);
|
||||
}
|
||||
put_online_cpus();
|
||||
queue_work_on(cpu, work_on_cpu_wq, &wfc.work);
|
||||
flush_work(&wfc.work);
|
||||
|
||||
return wfc.ret;
|
||||
}
|
||||
|
|
@ -1039,4 +1035,8 @@ void __init init_workqueues(void)
|
|||
hotcpu_notifier(workqueue_cpu_callback, 0);
|
||||
keventd_wq = create_workqueue("events");
|
||||
BUG_ON(!keventd_wq);
|
||||
#ifdef CONFIG_SMP
|
||||
work_on_cpu_wq = create_workqueue("work_on_cpu");
|
||||
BUG_ON(!work_on_cpu_wq);
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue