Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
e7858f52
Commit
e7858f52
authored
May 08, 2010
by
Ingo Molnar
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'cpu_stop' of
git://git.kernel.org/pub/scm/linux/kernel/git/tj/misc
into sched/core
parents
27a9da65
bbf1bb3e
Changes
13
Show whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
604 additions
and
439 deletions
+604
-439
Documentation/RCU/torture.txt
Documentation/RCU/torture.txt
+0
-10
arch/s390/kernel/time.c
arch/s390/kernel/time.c
+0
-1
drivers/xen/manage.c
drivers/xen/manage.c
+2
-12
include/linux/rcutiny.h
include/linux/rcutiny.h
+0
-2
include/linux/rcutree.h
include/linux/rcutree.h
+0
-1
include/linux/stop_machine.h
include/linux/stop_machine.h
+95
-27
kernel/Makefile
kernel/Makefile
+1
-1
kernel/cpu.c
kernel/cpu.c
+0
-8
kernel/module.c
kernel/module.c
+2
-12
kernel/rcutorture.c
kernel/rcutorture.c
+1
-1
kernel/sched.c
kernel/sched.c
+60
-225
kernel/sched_fair.c
kernel/sched_fair.c
+34
-14
kernel/stop_machine.c
kernel/stop_machine.c
+409
-125
No files found.
Documentation/RCU/torture.txt
View file @
e7858f52
...
@@ -182,16 +182,6 @@ Similarly, sched_expedited RCU provides the following:
...
@@ -182,16 +182,6 @@ Similarly, sched_expedited RCU provides the following:
sched_expedited-torture: Reader Pipe: 12660320201 95875 0 0 0 0 0 0 0 0 0
sched_expedited-torture: Reader Pipe: 12660320201 95875 0 0 0 0 0 0 0 0 0
sched_expedited-torture: Reader Batch: 12660424885 0 0 0 0 0 0 0 0 0 0
sched_expedited-torture: Reader Batch: 12660424885 0 0 0 0 0 0 0 0 0 0
sched_expedited-torture: Free-Block Circulation: 1090795 1090795 1090794 1090793 1090792 1090791 1090790 1090789 1090788 1090787 0
sched_expedited-torture: Free-Block Circulation: 1090795 1090795 1090794 1090793 1090792 1090791 1090790 1090789 1090788 1090787 0
state: -1 / 0:0 3:0 4:0
As before, the first four lines are similar to those for RCU.
The last line shows the task-migration state. The first number is
-1 if synchronize_sched_expedited() is idle, -2 if in the process of
posting wakeups to the migration kthreads, and N when waiting on CPU N.
Each of the colon-separated fields following the "/" is a CPU:state pair.
Valid states are "0" for idle, "1" for waiting for quiescent state,
"2" for passed through quiescent state, and "3" when a race with a
CPU-hotplug event forces use of the synchronize_sched() primitive.
USAGE
USAGE
...
...
arch/s390/kernel/time.c
View file @
e7858f52
...
@@ -391,7 +391,6 @@ static void __init time_init_wq(void)
...
@@ -391,7 +391,6 @@ static void __init time_init_wq(void)
if
(
time_sync_wq
)
if
(
time_sync_wq
)
return
;
return
;
time_sync_wq
=
create_singlethread_workqueue
(
"timesync"
);
time_sync_wq
=
create_singlethread_workqueue
(
"timesync"
);
stop_machine_create
();
}
}
/*
/*
...
...
drivers/xen/manage.c
View file @
e7858f52
...
@@ -80,12 +80,6 @@ static void do_suspend(void)
...
@@ -80,12 +80,6 @@ static void do_suspend(void)
shutting_down
=
SHUTDOWN_SUSPEND
;
shutting_down
=
SHUTDOWN_SUSPEND
;
err
=
stop_machine_create
();
if
(
err
)
{
printk
(
KERN_ERR
"xen suspend: failed to setup stop_machine %d
\n
"
,
err
);
goto
out
;
}
#ifdef CONFIG_PREEMPT
#ifdef CONFIG_PREEMPT
/* If the kernel is preemptible, we need to freeze all the processes
/* If the kernel is preemptible, we need to freeze all the processes
to prevent them from being in the middle of a pagetable update
to prevent them from being in the middle of a pagetable update
...
@@ -93,7 +87,7 @@ static void do_suspend(void)
...
@@ -93,7 +87,7 @@ static void do_suspend(void)
err
=
freeze_processes
();
err
=
freeze_processes
();
if
(
err
)
{
if
(
err
)
{
printk
(
KERN_ERR
"xen suspend: freeze failed %d
\n
"
,
err
);
printk
(
KERN_ERR
"xen suspend: freeze failed %d
\n
"
,
err
);
goto
out
_destroy_sm
;
goto
out
;
}
}
#endif
#endif
...
@@ -136,12 +130,8 @@ static void do_suspend(void)
...
@@ -136,12 +130,8 @@ static void do_suspend(void)
out_thaw:
out_thaw:
#ifdef CONFIG_PREEMPT
#ifdef CONFIG_PREEMPT
thaw_processes
();
thaw_processes
();
out_destroy_sm:
#endif
stop_machine_destroy
();
out:
out:
#endif
shutting_down
=
SHUTDOWN_INVALID
;
shutting_down
=
SHUTDOWN_INVALID
;
}
}
#endif
/* CONFIG_PM_SLEEP */
#endif
/* CONFIG_PM_SLEEP */
...
...
include/linux/rcutiny.h
View file @
e7858f52
...
@@ -60,8 +60,6 @@ static inline long rcu_batches_completed_bh(void)
...
@@ -60,8 +60,6 @@ static inline long rcu_batches_completed_bh(void)
return
0
;
return
0
;
}
}
extern
int
rcu_expedited_torture_stats
(
char
*
page
);
static
inline
void
rcu_force_quiescent_state
(
void
)
static
inline
void
rcu_force_quiescent_state
(
void
)
{
{
}
}
...
...
include/linux/rcutree.h
View file @
e7858f52
...
@@ -35,7 +35,6 @@ struct notifier_block;
...
@@ -35,7 +35,6 @@ struct notifier_block;
extern
void
rcu_sched_qs
(
int
cpu
);
extern
void
rcu_sched_qs
(
int
cpu
);
extern
void
rcu_bh_qs
(
int
cpu
);
extern
void
rcu_bh_qs
(
int
cpu
);
extern
int
rcu_needs_cpu
(
int
cpu
);
extern
int
rcu_needs_cpu
(
int
cpu
);
extern
int
rcu_expedited_torture_stats
(
char
*
page
);
#ifdef CONFIG_TREE_PREEMPT_RCU
#ifdef CONFIG_TREE_PREEMPT_RCU
...
...
include/linux/stop_machine.h
View file @
e7858f52
#ifndef _LINUX_STOP_MACHINE
#ifndef _LINUX_STOP_MACHINE
#define _LINUX_STOP_MACHINE
#define _LINUX_STOP_MACHINE
/* "Bogolock": stop the entire machine, disable interrupts. This is a
very heavy lock, which is equivalent to grabbing every spinlock
(and more). So the "read" side to such a lock is anything which
disables preeempt. */
#include <linux/cpu.h>
#include <linux/cpu.h>
#include <linux/cpumask.h>
#include <linux/cpumask.h>
#include <linux/list.h>
#include <asm/system.h>
#include <asm/system.h>
/*
* stop_cpu[s]() is simplistic per-cpu maximum priority cpu
* monopolization mechanism. The caller can specify a non-sleeping
* function to be executed on a single or multiple cpus preempting all
* other processes and monopolizing those cpus until it finishes.
*
* Resources for this mechanism are preallocated when a cpu is brought
* up and requests are guaranteed to be served as long as the target
* cpus are online.
*/
typedef
int
(
*
cpu_stop_fn_t
)(
void
*
arg
);
#ifdef CONFIG_SMP
struct
cpu_stop_work
{
struct
list_head
list
;
/* cpu_stopper->works */
cpu_stop_fn_t
fn
;
void
*
arg
;
struct
cpu_stop_done
*
done
;
};
int
stop_one_cpu
(
unsigned
int
cpu
,
cpu_stop_fn_t
fn
,
void
*
arg
);
void
stop_one_cpu_nowait
(
unsigned
int
cpu
,
cpu_stop_fn_t
fn
,
void
*
arg
,
struct
cpu_stop_work
*
work_buf
);
int
stop_cpus
(
const
struct
cpumask
*
cpumask
,
cpu_stop_fn_t
fn
,
void
*
arg
);
int
try_stop_cpus
(
const
struct
cpumask
*
cpumask
,
cpu_stop_fn_t
fn
,
void
*
arg
);
#else
/* CONFIG_SMP */
#include <linux/workqueue.h>
struct
cpu_stop_work
{
struct
work_struct
work
;
cpu_stop_fn_t
fn
;
void
*
arg
;
};
static
inline
int
stop_one_cpu
(
unsigned
int
cpu
,
cpu_stop_fn_t
fn
,
void
*
arg
)
{
int
ret
=
-
ENOENT
;
preempt_disable
();
if
(
cpu
==
smp_processor_id
())
ret
=
fn
(
arg
);
preempt_enable
();
return
ret
;
}
static
void
stop_one_cpu_nowait_workfn
(
struct
work_struct
*
work
)
{
struct
cpu_stop_work
*
stwork
=
container_of
(
work
,
struct
cpu_stop_work
,
work
);
preempt_disable
();
stwork
->
fn
(
stwork
->
arg
);
preempt_enable
();
}
static
inline
void
stop_one_cpu_nowait
(
unsigned
int
cpu
,
cpu_stop_fn_t
fn
,
void
*
arg
,
struct
cpu_stop_work
*
work_buf
)
{
if
(
cpu
==
smp_processor_id
())
{
INIT_WORK
(
&
work_buf
->
work
,
stop_one_cpu_nowait_workfn
);
work_buf
->
fn
=
fn
;
work_buf
->
arg
=
arg
;
schedule_work
(
&
work_buf
->
work
);
}
}
static
inline
int
stop_cpus
(
const
struct
cpumask
*
cpumask
,
cpu_stop_fn_t
fn
,
void
*
arg
)
{
if
(
cpumask_test_cpu
(
raw_smp_processor_id
(),
cpumask
))
return
stop_one_cpu
(
raw_smp_processor_id
(),
fn
,
arg
);
return
-
ENOENT
;
}
static
inline
int
try_stop_cpus
(
const
struct
cpumask
*
cpumask
,
cpu_stop_fn_t
fn
,
void
*
arg
)
{
return
stop_cpus
(
cpumask
,
fn
,
arg
);
}
#endif
/* CONFIG_SMP */
/*
* stop_machine "Bogolock": stop the entire machine, disable
* interrupts. This is a very heavy lock, which is equivalent to
* grabbing every spinlock (and more). So the "read" side to such a
* lock is anything which disables preeempt.
*/
#if defined(CONFIG_STOP_MACHINE) && defined(CONFIG_SMP)
#if defined(CONFIG_STOP_MACHINE) && defined(CONFIG_SMP)
/**
/**
...
@@ -36,24 +124,7 @@ int stop_machine(int (*fn)(void *), void *data, const struct cpumask *cpus);
...
@@ -36,24 +124,7 @@ int stop_machine(int (*fn)(void *), void *data, const struct cpumask *cpus);
*/
*/
int
__stop_machine
(
int
(
*
fn
)(
void
*
),
void
*
data
,
const
struct
cpumask
*
cpus
);
int
__stop_machine
(
int
(
*
fn
)(
void
*
),
void
*
data
,
const
struct
cpumask
*
cpus
);
/**
#else
/* CONFIG_STOP_MACHINE && CONFIG_SMP */
* stop_machine_create: create all stop_machine threads
*
* Description: This causes all stop_machine threads to be created before
* stop_machine actually gets called. This can be used by subsystems that
* need a non failing stop_machine infrastructure.
*/
int
stop_machine_create
(
void
);
/**
* stop_machine_destroy: destroy all stop_machine threads
*
* Description: This causes all stop_machine threads which were created with
* stop_machine_create to be destroyed again.
*/
void
stop_machine_destroy
(
void
);
#else
static
inline
int
stop_machine
(
int
(
*
fn
)(
void
*
),
void
*
data
,
static
inline
int
stop_machine
(
int
(
*
fn
)(
void
*
),
void
*
data
,
const
struct
cpumask
*
cpus
)
const
struct
cpumask
*
cpus
)
...
@@ -65,8 +136,5 @@ static inline int stop_machine(int (*fn)(void *), void *data,
...
@@ -65,8 +136,5 @@ static inline int stop_machine(int (*fn)(void *), void *data,
return
ret
;
return
ret
;
}
}
static
inline
int
stop_machine_create
(
void
)
{
return
0
;
}
#endif
/* CONFIG_STOP_MACHINE && CONFIG_SMP */
static
inline
void
stop_machine_destroy
(
void
)
{
}
#endif
/* CONFIG_SMP */
#endif
/* _LINUX_STOP_MACHINE */
#endif
/* _LINUX_STOP_MACHINE */
kernel/Makefile
View file @
e7858f52
...
@@ -68,7 +68,7 @@ obj-$(CONFIG_USER_NS) += user_namespace.o
...
@@ -68,7 +68,7 @@ obj-$(CONFIG_USER_NS) += user_namespace.o
obj-$(CONFIG_PID_NS)
+=
pid_namespace.o
obj-$(CONFIG_PID_NS)
+=
pid_namespace.o
obj-$(CONFIG_IKCONFIG)
+=
configs.o
obj-$(CONFIG_IKCONFIG)
+=
configs.o
obj-$(CONFIG_RESOURCE_COUNTERS)
+=
res_counter.o
obj-$(CONFIG_RESOURCE_COUNTERS)
+=
res_counter.o
obj-$(CONFIG_S
TOP_MACHINE
)
+=
stop_machine.o
obj-$(CONFIG_S
MP
)
+=
stop_machine.o
obj-$(CONFIG_KPROBES_SANITY_TEST)
+=
test_kprobes.o
obj-$(CONFIG_KPROBES_SANITY_TEST)
+=
test_kprobes.o
obj-$(CONFIG_AUDIT)
+=
audit.o auditfilter.o audit_watch.o
obj-$(CONFIG_AUDIT)
+=
audit.o auditfilter.o audit_watch.o
obj-$(CONFIG_AUDITSYSCALL)
+=
auditsc.o
obj-$(CONFIG_AUDITSYSCALL)
+=
auditsc.o
...
...
kernel/cpu.c
View file @
e7858f52
...
@@ -266,9 +266,6 @@ int __ref cpu_down(unsigned int cpu)
...
@@ -266,9 +266,6 @@ int __ref cpu_down(unsigned int cpu)
{
{
int
err
;
int
err
;
err
=
stop_machine_create
();
if
(
err
)
return
err
;
cpu_maps_update_begin
();
cpu_maps_update_begin
();
if
(
cpu_hotplug_disabled
)
{
if
(
cpu_hotplug_disabled
)
{
...
@@ -280,7 +277,6 @@ int __ref cpu_down(unsigned int cpu)
...
@@ -280,7 +277,6 @@ int __ref cpu_down(unsigned int cpu)
out:
out:
cpu_maps_update_done
();
cpu_maps_update_done
();
stop_machine_destroy
();
return
err
;
return
err
;
}
}
EXPORT_SYMBOL
(
cpu_down
);
EXPORT_SYMBOL
(
cpu_down
);
...
@@ -361,9 +357,6 @@ int disable_nonboot_cpus(void)
...
@@ -361,9 +357,6 @@ int disable_nonboot_cpus(void)
{
{
int
cpu
,
first_cpu
,
error
;
int
cpu
,
first_cpu
,
error
;
error
=
stop_machine_create
();
if
(
error
)
return
error
;
cpu_maps_update_begin
();
cpu_maps_update_begin
();
first_cpu
=
cpumask_first
(
cpu_online_mask
);
first_cpu
=
cpumask_first
(
cpu_online_mask
);
/*
/*
...
@@ -394,7 +387,6 @@ int disable_nonboot_cpus(void)
...
@@ -394,7 +387,6 @@ int disable_nonboot_cpus(void)
printk
(
KERN_ERR
"Non-boot CPUs are not disabled
\n
"
);
printk
(
KERN_ERR
"Non-boot CPUs are not disabled
\n
"
);
}
}
cpu_maps_update_done
();
cpu_maps_update_done
();
stop_machine_destroy
();
return
error
;
return
error
;
}
}
...
...
kernel/module.c
View file @
e7858f52
...
@@ -723,16 +723,8 @@ SYSCALL_DEFINE2(delete_module, const char __user *, name_user,
...
@@ -723,16 +723,8 @@ SYSCALL_DEFINE2(delete_module, const char __user *, name_user,
return
-
EFAULT
;
return
-
EFAULT
;
name
[
MODULE_NAME_LEN
-
1
]
=
'\0'
;
name
[
MODULE_NAME_LEN
-
1
]
=
'\0'
;
/* Create stop_machine threads since free_module relies on
if
(
mutex_lock_interruptible
(
&
module_mutex
)
!=
0
)
* a non-failing stop_machine call. */
return
-
EINTR
;
ret
=
stop_machine_create
();
if
(
ret
)
return
ret
;
if
(
mutex_lock_interruptible
(
&
module_mutex
)
!=
0
)
{
ret
=
-
EINTR
;
goto
out_stop
;
}
mod
=
find_module
(
name
);
mod
=
find_module
(
name
);
if
(
!
mod
)
{
if
(
!
mod
)
{
...
@@ -792,8 +784,6 @@ SYSCALL_DEFINE2(delete_module, const char __user *, name_user,
...
@@ -792,8 +784,6 @@ SYSCALL_DEFINE2(delete_module, const char __user *, name_user,
out:
out:
mutex_unlock
(
&
module_mutex
);
mutex_unlock
(
&
module_mutex
);
out_stop:
stop_machine_destroy
();
return
ret
;
return
ret
;
}
}
...
...
kernel/rcutorture.c
View file @
e7858f52
...
@@ -669,7 +669,7 @@ static struct rcu_torture_ops sched_expedited_ops = {
...
@@ -669,7 +669,7 @@ static struct rcu_torture_ops sched_expedited_ops = {
.
sync
=
synchronize_sched_expedited
,
.
sync
=
synchronize_sched_expedited
,
.
cb_barrier
=
NULL
,
.
cb_barrier
=
NULL
,
.
fqs
=
rcu_sched_force_quiescent_state
,
.
fqs
=
rcu_sched_force_quiescent_state
,
.
stats
=
rcu_expedited_torture_stats
,
.
stats
=
NULL
,
.
irq_capable
=
1
,
.
irq_capable
=
1
,
.
name
=
"sched_expedited"
.
name
=
"sched_expedited"
};
};
...
...
kernel/sched.c
View file @
e7858f52
...
@@ -55,9 +55,9 @@
...
@@ -55,9 +55,9 @@
#include <linux/cpu.h>
#include <linux/cpu.h>
#include <linux/cpuset.h>
#include <linux/cpuset.h>
#include <linux/percpu.h>
#include <linux/percpu.h>
#include <linux/kthread.h>
#include <linux/proc_fs.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/seq_file.h>
#include <linux/stop_machine.h>
#include <linux/sysctl.h>
#include <linux/sysctl.h>
#include <linux/syscalls.h>
#include <linux/syscalls.h>
#include <linux/times.h>
#include <linux/times.h>
...
@@ -539,15 +539,13 @@ struct rq {
...
@@ -539,15 +539,13 @@ struct rq {
int
post_schedule
;
int
post_schedule
;
int
active_balance
;
int
active_balance
;
int
push_cpu
;
int
push_cpu
;
struct
cpu_stop_work
active_balance_work
;
/* cpu of this runqueue: */
/* cpu of this runqueue: */
int
cpu
;
int
cpu
;
int
online
;
int
online
;
unsigned
long
avg_load_per_task
;
unsigned
long
avg_load_per_task
;
struct
task_struct
*
migration_thread
;
struct
list_head
migration_queue
;
u64
rt_avg
;
u64
rt_avg
;
u64
age_stamp
;
u64
age_stamp
;
u64
idle_stamp
;
u64
idle_stamp
;
...
@@ -2037,21 +2035,18 @@ void set_task_cpu(struct task_struct *p, unsigned int new_cpu)
...
@@ -2037,21 +2035,18 @@ void set_task_cpu(struct task_struct *p, unsigned int new_cpu)
__set_task_cpu
(
p
,
new_cpu
);
__set_task_cpu
(
p
,
new_cpu
);
}
}
struct
migration_req
{
struct
migration_arg
{
struct
list_head
list
;
struct
task_struct
*
task
;
struct
task_struct
*
task
;
int
dest_cpu
;
int
dest_cpu
;
struct
completion
done
;
};
};
static
int
migration_cpu_stop
(
void
*
data
);
/*
/*
* The task's runqueue lock must be held.
* The task's runqueue lock must be held.
* Returns true if you have to wait for migration thread.
* Returns true if you have to wait for migration thread.
*/
*/
static
int
static
bool
migrate_task
(
struct
task_struct
*
p
,
int
dest_cpu
)
migrate_task
(
struct
task_struct
*
p
,
int
dest_cpu
,
struct
migration_req
*
req
)
{
{
struct
rq
*
rq
=
task_rq
(
p
);
struct
rq
*
rq
=
task_rq
(
p
);
...
@@ -2059,15 +2054,7 @@ migrate_task(struct task_struct *p, int dest_cpu, struct migration_req *req)
...
@@ -2059,15 +2054,7 @@ migrate_task(struct task_struct *p, int dest_cpu, struct migration_req *req)
* If the task is not on a runqueue (and not running), then
* If the task is not on a runqueue (and not running), then
* the next wake-up will properly place the task.
* the next wake-up will properly place the task.
*/
*/
if
(
!
p
->
se
.
on_rq
&&
!
task_running
(
rq
,
p
))
return
p
->
se
.
on_rq
||
task_running
(
rq
,
p
);
return
0
;
init_completion
(
&
req
->
done
);
req
->
task
=
p
;
req
->
dest_cpu
=
dest_cpu
;
list_add
(
&
req
->
list
,
&
rq
->
migration_queue
);
return
1
;
}
}
/*
/*
...
@@ -3110,7 +3097,6 @@ static void update_cpu_load(struct rq *this_rq)
...
@@ -3110,7 +3097,6 @@ static void update_cpu_load(struct rq *this_rq)
void
sched_exec
(
void
)
void
sched_exec
(
void
)
{
{
struct
task_struct
*
p
=
current
;
struct
task_struct
*
p
=
current
;
struct
migration_req
req
;
unsigned
long
flags
;
unsigned
long
flags
;
struct
rq
*
rq
;
struct
rq
*
rq
;
int
dest_cpu
;
int
dest_cpu
;
...
@@ -3124,17 +3110,11 @@ void sched_exec(void)
...
@@ -3124,17 +3110,11 @@ void sched_exec(void)
* select_task_rq() can race against ->cpus_allowed
* select_task_rq() can race against ->cpus_allowed
*/
*/
if
(
cpumask_test_cpu
(
dest_cpu
,
&
p
->
cpus_allowed
)
&&
if
(
cpumask_test_cpu
(
dest_cpu
,
&
p
->
cpus_allowed
)
&&
likely
(
cpu_active
(
dest_cpu
))
&&
likely
(
cpu_active
(
dest_cpu
))
&&
migrate_task
(
p
,
dest_cpu
))
{
migrate_task
(
p
,
dest_cpu
,
&
req
))
{
struct
migration_arg
arg
=
{
p
,
dest_cpu
};
/* Need to wait for migration thread (might exit: take ref). */
struct
task_struct
*
mt
=
rq
->
migration_thread
;
get_task_struct
(
mt
);
task_rq_unlock
(
rq
,
&
flags
);
task_rq_unlock
(
rq
,
&
flags
);
wake_up_process
(
mt
);
stop_one_cpu
(
cpu_of
(
rq
),
migration_cpu_stop
,
&
arg
);
put_task_struct
(
mt
);
wait_for_completion
(
&
req
.
done
);
return
;
return
;
}
}
unlock:
unlock:
...
@@ -5290,17 +5270,15 @@ static inline void sched_init_granularity(void)
...
@@ -5290,17 +5270,15 @@ static inline void sched_init_granularity(void)
/*
/*
* This is how migration works:
* This is how migration works:
*
*
* 1) we queue a struct migration_req structure in the source CPU's
* 1) we invoke migration_cpu_stop() on the target CPU using
* runqueue and wake up that CPU's migration thread.
* stop_one_cpu().
* 2) we down() the locked semaphore => thread blocks.
* 2) stopper starts to run (implicitly forcing the migrated thread
* 3) migration thread wakes up (implicitly it forces the migrated
* off the CPU)
* thread off the CPU)
* 3) it checks whether the migrated task is still in the wrong runqueue.
* 4) it gets the migration request and checks whether the migrated
* 4) if it's in the wrong runqueue then the migration thread removes
* task is still in the wrong runqueue.
* 5) if it's in the wrong runqueue then the migration thread removes
* it and puts it into the right queue.
* it and puts it into the right queue.
*
6) migration thread up()s the semaphore.
*
5) stopper completes and stop_one_cpu() returns and the migration
*
7) we wake up and the migration
is done.
*
is done.
*/
*/
/*
/*
...
@@ -5314,9 +5292,9 @@ static inline void sched_init_granularity(void)
...
@@ -5314,9 +5292,9 @@ static inline void sched_init_granularity(void)
*/
*/
int
set_cpus_allowed_ptr
(
struct
task_struct
*
p
,
const
struct
cpumask
*
new_mask
)
int
set_cpus_allowed_ptr
(
struct
task_struct
*
p
,
const
struct
cpumask
*
new_mask
)
{
{
struct
migration_req
req
;
unsigned
long
flags
;
unsigned
long
flags
;
struct
rq
*
rq
;
struct
rq
*
rq
;
unsigned
int
dest_cpu
;
int
ret
=
0
;
int
ret
=
0
;
/*
/*
...
@@ -5354,15 +5332,12 @@ int set_cpus_allowed_ptr(struct task_struct *p, const struct cpumask *new_mask)
...
@@ -5354,15 +5332,12 @@ int set_cpus_allowed_ptr(struct task_struct *p, const struct cpumask *new_mask)
if
(
cpumask_test_cpu
(
task_cpu
(
p
),
new_mask
))
if
(
cpumask_test_cpu
(
task_cpu
(
p
),
new_mask
))
goto
out
;
goto
out
;
if
(
migrate_task
(
p
,
cpumask_any_and
(
cpu_active_mask
,
new_mask
),
&
req
))
{
dest_cpu
=
cpumask_any_and
(
cpu_active_mask
,
new_mask
);
if
(
migrate_task
(
p
,
dest_cpu
))
{
struct
migration_arg
arg
=
{
p
,
dest_cpu
};
/* Need help from migration thread: drop lock and wait. */
/* Need help from migration thread: drop lock and wait. */
struct
task_struct
*
mt
=
rq
->
migration_thread
;
get_task_struct
(
mt
);
task_rq_unlock
(
rq
,
&
flags
);
task_rq_unlock
(
rq
,
&
flags
);
wake_up_process
(
mt
);
stop_one_cpu
(
cpu_of
(
rq
),
migration_cpu_stop
,
&
arg
);
put_task_struct
(
mt
);
wait_for_completion
(
&
req
.
done
);
tlb_migrate_finish
(
p
->
mm
);
tlb_migrate_finish
(
p
->
mm
);
return
0
;
return
0
;
}
}
...
@@ -5420,70 +5395,22 @@ static int __migrate_task(struct task_struct *p, int src_cpu, int dest_cpu)
...
@@ -5420,70 +5395,22 @@ static int __migrate_task(struct task_struct *p, int src_cpu, int dest_cpu)
return
ret
;
return
ret
;
}
}
#define RCU_MIGRATION_IDLE 0
#define RCU_MIGRATION_NEED_QS 1
#define RCU_MIGRATION_GOT_QS 2
#define RCU_MIGRATION_MUST_SYNC 3
/*
/*
* migration_
thread - this is a highprio system thread that performs
* migration_
cpu_stop - this will be executed by a highprio stopper thread
*
thread migration by bumping thread off CPU then 'pushing' onto
*
and performs thread migration by bumping thread off CPU then
* another runqueue.
*
'pushing' onto
another runqueue.
*/
*/
static
int
migration_
thread
(
void
*
data
)
static
int
migration_
cpu_stop
(
void
*
data
)
{
{
int
badcpu
;
struct
migration_arg
*
arg
=
data
;
int
cpu
=
(
long
)
data
;
struct
rq
*
rq
;
rq
=
cpu_rq
(
cpu
);
BUG_ON
(
rq
->
migration_thread
!=
current
);
set_current_state
(
TASK_INTERRUPTIBLE
);
while
(
!
kthread_should_stop
())
{
struct
migration_req
*
req
;
struct
list_head
*
head
;
raw_spin_lock_irq
(
&
rq
->
lock
);
if
(
cpu_is_offline
(
cpu
))
{
raw_spin_unlock_irq
(
&
rq
->
lock
);
break
;
}
if
(
rq
->
active_balance
)
{
active_load_balance
(
rq
,
cpu
);
rq
->
active_balance
=
0
;
}
head
=
&
rq
->
migration_queue
;
if
(
list_empty
(
head
))
{
raw_spin_unlock_irq
(
&
rq
->
lock
);
schedule
();
set_current_state
(
TASK_INTERRUPTIBLE
);
continue
;
}
req
=
list_entry
(
head
->
next
,
struct
migration_req
,
list
);
list_del_init
(
head
->
next
);
if
(
req
->
task
!=
NULL
)
{
/*
raw_spin_unlock
(
&
rq
->
lock
);
* The original target cpu might have gone down and we might
__migrate_task
(
req
->
task
,
cpu
,
req
->
dest_cpu
);
* be on another cpu but it doesn't matter.
}
else
if
(
likely
(
cpu
==
(
badcpu
=
smp_processor_id
())))
{
*/
req
->
dest_cpu
=
RCU_MIGRATION_GOT_QS
;
local_irq_disable
();
raw_spin_unlock
(
&
rq
->
lock
);
__migrate_task
(
arg
->
task
,
raw_smp_processor_id
(),
arg
->
dest_cpu
);
}
else
{
req
->
dest_cpu
=
RCU_MIGRATION_MUST_SYNC
;
raw_spin_unlock
(
&
rq
->
lock
);
WARN_ONCE
(
1
,
"migration_thread() on CPU %d, expected %d
\n
"
,
badcpu
,
cpu
);
}
local_irq_enable
();
local_irq_enable
();
complete
(
&
req
->
done
);
}
__set_current_state
(
TASK_RUNNING
);
return
0
;
return
0
;
}
}
...
@@ -5850,35 +5777,20 @@ static void set_rq_offline(struct rq *rq)
...
@@ -5850,35 +5777,20 @@ static void set_rq_offline(struct rq *rq)
static
int
__cpuinit
static
int
__cpuinit
migration_call
(
struct
notifier_block
*
nfb
,
unsigned
long
action
,
void
*
hcpu
)
migration_call
(
struct
notifier_block
*
nfb
,
unsigned
long
action
,
void
*
hcpu
)
{
{
struct
task_struct
*
p
;
int
cpu
=
(
long
)
hcpu
;
int
cpu
=
(
long
)
hcpu
;
unsigned
long
flags
;
unsigned
long
flags
;
struct
rq
*
rq
;
struct
rq
*
rq
=
cpu_rq
(
cpu
)
;
switch
(
action
)
{
switch
(
action
)
{
case
CPU_UP_PREPARE
:
case
CPU_UP_PREPARE
:
case
CPU_UP_PREPARE_FROZEN
:
case
CPU_UP_PREPARE_FROZEN
:
p
=
kthread_create
(
migration_thread
,
hcpu
,
"migration/%d"
,
cpu
);
if
(
IS_ERR
(
p
))
return
NOTIFY_BAD
;
kthread_bind
(
p
,
cpu
);
/* Must be high prio: stop_machine expects to yield to it. */
rq
=
task_rq_lock
(
p
,
&
flags
);
__setscheduler
(
rq
,
p
,
SCHED_FIFO
,
MAX_RT_PRIO
-
1
);
task_rq_unlock
(
rq
,
&
flags
);
get_task_struct
(
p
);
cpu_rq
(
cpu
)
->
migration_thread
=
p
;
rq
->
calc_load_update
=
calc_load_update
;
rq
->
calc_load_update
=
calc_load_update
;
break
;
break
;
case
CPU_ONLINE
:
case
CPU_ONLINE
:
case
CPU_ONLINE_FROZEN
:
case
CPU_ONLINE_FROZEN
:
/* Strictly unnecessary, as first user will wake it. */
wake_up_process
(
cpu_rq
(
cpu
)
->
migration_thread
);
/* Update our root-domain */
/* Update our root-domain */
rq
=
cpu_rq
(
cpu
);
raw_spin_lock_irqsave
(
&
rq
->
lock
,
flags
);
raw_spin_lock_irqsave
(
&
rq
->
lock
,
flags
);
if
(
rq
->
rd
)
{
if
(
rq
->
rd
)
{
BUG_ON
(
!
cpumask_test_cpu
(
cpu
,
rq
->
rd
->
span
));
BUG_ON
(
!
cpumask_test_cpu
(
cpu
,
rq
->
rd
->
span
));
...
@@ -5889,25 +5801,9 @@ migration_call(struct notifier_block *nfb, unsigned long action, void *hcpu)
...
@@ -5889,25 +5801,9 @@ migration_call(struct notifier_block *nfb, unsigned long action, void *hcpu)
break
;
break
;
#ifdef CONFIG_HOTPLUG_CPU
#ifdef CONFIG_HOTPLUG_CPU
case
CPU_UP_CANCELED
:
case
CPU_UP_CANCELED_FROZEN
:
if
(
!
cpu_rq
(
cpu
)
->
migration_thread
)
break
;
/* Unbind it from offline cpu so it can run. Fall thru. */
kthread_bind
(
cpu_rq
(
cpu
)
->
migration_thread
,
cpumask_any
(
cpu_online_mask
));
kthread_stop
(
cpu_rq
(
cpu
)
->
migration_thread
);
put_task_struct
(
cpu_rq
(
cpu
)
->
migration_thread
);
cpu_rq
(
cpu
)
->
migration_thread
=
NULL
;
break
;
case
CPU_DEAD
:
case
CPU_DEAD
:
case
CPU_DEAD_FROZEN
:
case
CPU_DEAD_FROZEN
:
migrate_live_tasks
(
cpu
);
migrate_live_tasks
(
cpu
);
rq
=
cpu_rq
(
cpu
);
kthread_stop
(
rq
->
migration_thread
);
put_task_struct
(
rq
->
migration_thread
);
rq
->
migration_thread
=
NULL
;
/* Idle task back to normal (off runqueue, low prio) */
/* Idle task back to normal (off runqueue, low prio) */
raw_spin_lock_irq
(
&
rq
->
lock
);
raw_spin_lock_irq
(
&
rq
->
lock
);
deactivate_task
(
rq
,
rq
->
idle
,
0
);
deactivate_task
(
rq
,
rq
->
idle
,
0
);
...
@@ -5918,29 +5814,11 @@ migration_call(struct notifier_block *nfb, unsigned long action, void *hcpu)
...
@@ -5918,29 +5814,11 @@ migration_call(struct notifier_block *nfb, unsigned long action, void *hcpu)
migrate_nr_uninterruptible
(
rq
);
migrate_nr_uninterruptible
(
rq
);
BUG_ON
(
rq
->
nr_running
!=
0
);
BUG_ON
(
rq
->
nr_running
!=
0
);
calc_global_load_remove
(
rq
);
calc_global_load_remove
(
rq
);
/*
* No need to migrate the tasks: it was best-effort if
* they didn't take sched_hotcpu_mutex. Just wake up
* the requestors.
*/
raw_spin_lock_irq
(
&
rq
->
lock
);
while
(
!
list_empty
(
&
rq
->
migration_queue
))
{
struct
migration_req
*
req
;
req
=
list_entry
(
rq
->
migration_queue
.
next
,
struct
migration_req
,
list
);
list_del_init
(
&
req
->
list
);
raw_spin_unlock_irq
(
&
rq
->
lock
);
complete
(
&
req
->
done
);
raw_spin_lock_irq
(
&
rq
->
lock
);
}
raw_spin_unlock_irq
(
&
rq
->
lock
);
break
;
break
;
case
CPU_DYING
:
case
CPU_DYING
:
case
CPU_DYING_FROZEN
:
case
CPU_DYING_FROZEN
:
/* Update our root-domain */
/* Update our root-domain */
rq
=
cpu_rq
(
cpu
);
raw_spin_lock_irqsave
(
&
rq
->
lock
,
flags
);
raw_spin_lock_irqsave
(
&
rq
->
lock
,
flags
);
if
(
rq
->
rd
)
{
if
(
rq
->
rd
)
{
BUG_ON
(
!
cpumask_test_cpu
(
cpu
,
rq
->
rd
->
span
));
BUG_ON
(
!
cpumask_test_cpu
(
cpu
,
rq
->
rd
->
span
));
...
@@ -7757,10 +7635,8 @@ void __init sched_init(void)
...
@@ -7757,10 +7635,8 @@ void __init sched_init(void)
rq
->
push_cpu
=
0
;
rq
->
push_cpu
=
0
;
rq
->
cpu
=
i
;
rq
->
cpu
=
i
;
rq
->
online
=
0
;
rq
->
online
=
0
;
rq
->
migration_thread
=
NULL
;
rq
->
idle_stamp
=
0
;
rq
->
idle_stamp
=
0
;
rq
->
avg_idle
=
2
*
sysctl_sched_migration_cost
;
rq
->
avg_idle
=
2
*
sysctl_sched_migration_cost
;
INIT_LIST_HEAD
(
&
rq
->
migration_queue
);
rq_attach_root
(
rq
,
&
def_root_domain
);
rq_attach_root
(
rq
,
&
def_root_domain
);
#endif
#endif
init_rq_hrtick
(
rq
);
init_rq_hrtick
(
rq
);
...
@@ -9054,43 +8930,32 @@ struct cgroup_subsys cpuacct_subsys = {
...
@@ -9054,43 +8930,32 @@ struct cgroup_subsys cpuacct_subsys = {
#ifndef CONFIG_SMP
#ifndef CONFIG_SMP
int
rcu_expedited_torture_stats
(
char
*
page
)
{
return
0
;
}
EXPORT_SYMBOL_GPL
(
rcu_expedited_torture_stats
);
void
synchronize_sched_expedited
(
void
)
void
synchronize_sched_expedited
(
void
)
{
{
barrier
();
}
}
EXPORT_SYMBOL_GPL
(
synchronize_sched_expedited
);
EXPORT_SYMBOL_GPL
(
synchronize_sched_expedited
);
#else
/* #ifndef CONFIG_SMP */
#else
/* #ifndef CONFIG_SMP */
static
DEFINE_PER_CPU
(
struct
migration_req
,
rcu_migration_req
);
static
atomic_t
synchronize_sched_expedited_count
=
ATOMIC_INIT
(
0
);
static
DEFINE_MUTEX
(
rcu_sched_expedited_mutex
);
#define RCU_EXPEDITED_STATE_POST -2
static
int
synchronize_sched_expedited_cpu_stop
(
void
*
data
)
#define RCU_EXPEDITED_STATE_IDLE -1
static
int
rcu_expedited_state
=
RCU_EXPEDITED_STATE_IDLE
;
int
rcu_expedited_torture_stats
(
char
*
page
)
{
{
int
cnt
=
0
;
/*
int
cpu
;
* There must be a full memory barrier on each affected CPU
* between the time that try_stop_cpus() is called and the
cnt
+=
sprintf
(
&
page
[
cnt
],
"state: %d /"
,
rcu_expedited_state
);
* time that it returns.
for_each_online_cpu
(
cpu
)
{
*
cnt
+=
sprintf
(
&
page
[
cnt
],
" %d:%d"
,
* In the current initial implementation of cpu_stop, the
cpu
,
per_cpu
(
rcu_migration_req
,
cpu
).
dest_cpu
);
* above condition is already met when the control reaches
}
* this point and the following smp_mb() is not strictly
cnt
+=
sprintf
(
&
page
[
cnt
],
"
\n
"
);
* necessary. Do smp_mb() anyway for documentation and
return
cnt
;
* robustness against future implementation changes.
*/
smp_mb
();
/* See above comment block. */
return
0
;
}
}
EXPORT_SYMBOL_GPL
(
rcu_expedited_torture_stats
);
static
long
synchronize_sched_expedited_count
;
/*
/*
* Wait for an rcu-sched grace period to elapse, but use "big hammer"
* Wait for an rcu-sched grace period to elapse, but use "big hammer"
...
@@ -9104,18 +8969,14 @@ static long synchronize_sched_expedited_count;
...
@@ -9104,18 +8969,14 @@ static long synchronize_sched_expedited_count;
*/
*/
void
synchronize_sched_expedited
(
void
)
void
synchronize_sched_expedited
(
void
)
{
{
int
cpu
;
int
snap
,
trycount
=
0
;
unsigned
long
flags
;
bool
need_full_sync
=
0
;
struct
rq
*
rq
;
struct
migration_req
*
req
;
long
snap
;
int
trycount
=
0
;
smp_mb
();
/* ensure prior mod happens before capturing snap. */
smp_mb
();
/* ensure prior mod happens before capturing snap. */
snap
=
ACCESS_ONCE
(
synchronize_sched_expedited_count
)
+
1
;
snap
=
atomic_read
(
&
synchronize_sched_expedited_count
)
+
1
;
get_online_cpus
();
get_online_cpus
();
while
(
!
mutex_trylock
(
&
rcu_sched_expedited_mutex
))
{
while
(
try_stop_cpus
(
cpu_online_mask
,
synchronize_sched_expedited_cpu_stop
,
NULL
)
==
-
EAGAIN
)
{
put_online_cpus
();
put_online_cpus
();
if
(
trycount
++
<
10
)
if
(
trycount
++
<
10
)
udelay
(
trycount
*
num_online_cpus
());
udelay
(
trycount
*
num_online_cpus
());
...
@@ -9123,41 +8984,15 @@ void synchronize_sched_expedited(void)
...
@@ -9123,41 +8984,15 @@ void synchronize_sched_expedited(void)
synchronize_sched
();
synchronize_sched
();
return
;
return
;
}
}
if
(
ACCESS_ONCE
(
synchronize_sched_expedited_count
)
-
snap
>
0
)
{
if
(
atomic_read
(
&
synchronize_sched_expedited_count
)
-
snap
>
0
)
{
smp_mb
();
/* ensure test happens before caller kfree */
smp_mb
();
/* ensure test happens before caller kfree */
return
;
return
;
}
}
get_online_cpus
();
get_online_cpus
();
}
}
rcu_expedited_state
=
RCU_EXPEDITED_STATE_POST
;
atomic_inc
(
&
synchronize_sched_expedited_count
);
for_each_online_cpu
(
cpu
)
{
smp_mb__after_atomic_inc
();
/* ensure post-GP actions seen after GP. */
rq
=
cpu_rq
(
cpu
);
req
=
&
per_cpu
(
rcu_migration_req
,
cpu
);
init_completion
(
&
req
->
done
);
req
->
task
=
NULL
;
req
->
dest_cpu
=
RCU_MIGRATION_NEED_QS
;
raw_spin_lock_irqsave
(
&
rq
->
lock
,
flags
);
list_add
(
&
req
->
list
,
&
rq
->
migration_queue
);
raw_spin_unlock_irqrestore
(
&
rq
->
lock
,
flags
);
wake_up_process
(
rq
->
migration_thread
);
}
for_each_online_cpu
(
cpu
)
{
rcu_expedited_state
=
cpu
;
req
=
&
per_cpu
(
rcu_migration_req
,
cpu
);
rq
=
cpu_rq
(
cpu
);
wait_for_completion
(
&
req
->
done
);
raw_spin_lock_irqsave
(
&
rq
->
lock
,
flags
);
if
(
unlikely
(
req
->
dest_cpu
==
RCU_MIGRATION_MUST_SYNC
))
need_full_sync
=
1
;
req
->
dest_cpu
=
RCU_MIGRATION_IDLE
;
raw_spin_unlock_irqrestore
(
&
rq
->
lock
,
flags
);
}
rcu_expedited_state
=
RCU_EXPEDITED_STATE_IDLE
;
synchronize_sched_expedited_count
++
;
mutex_unlock
(
&
rcu_sched_expedited_mutex
);
put_online_cpus
();
put_online_cpus
();
if
(
need_full_sync
)
synchronize_sched
();
}
}
EXPORT_SYMBOL_GPL
(
synchronize_sched_expedited
);
EXPORT_SYMBOL_GPL
(
synchronize_sched_expedited
);
...
...
kernel/sched_fair.c
View file @
e7858f52
...
@@ -2798,6 +2798,8 @@ static int need_active_balance(struct sched_domain *sd, int sd_idle, int idle)
...
@@ -2798,6 +2798,8 @@ static int need_active_balance(struct sched_domain *sd, int sd_idle, int idle)
return
unlikely
(
sd
->
nr_balance_failed
>
sd
->
cache_nice_tries
+
2
);
return
unlikely
(
sd
->
nr_balance_failed
>
sd
->
cache_nice_tries
+
2
);
}
}
static
int
active_load_balance_cpu_stop
(
void
*
data
);
/*
/*
* Check this_cpu to ensure it is balanced within domain. Attempt to move
* Check this_cpu to ensure it is balanced within domain. Attempt to move
* tasks if there is an imbalance.
* tasks if there is an imbalance.
...
@@ -2887,8 +2889,9 @@ static int load_balance(int this_cpu, struct rq *this_rq,
...
@@ -2887,8 +2889,9 @@ static int load_balance(int this_cpu, struct rq *this_rq,
if
(
need_active_balance
(
sd
,
sd_idle
,
idle
))
{
if
(
need_active_balance
(
sd
,
sd_idle
,
idle
))
{
raw_spin_lock_irqsave
(
&
busiest
->
lock
,
flags
);
raw_spin_lock_irqsave
(
&
busiest
->
lock
,
flags
);
/* don't kick the migration_thread, if the curr
/* don't kick the active_load_balance_cpu_stop,
* task on busiest cpu can't be moved to this_cpu
* if the curr task on busiest cpu can't be
* moved to this_cpu
*/
*/
if
(
!
cpumask_test_cpu
(
this_cpu
,
if
(
!
cpumask_test_cpu
(
this_cpu
,
&
busiest
->
curr
->
cpus_allowed
))
{
&
busiest
->
curr
->
cpus_allowed
))
{
...
@@ -2898,14 +2901,22 @@ static int load_balance(int this_cpu, struct rq *this_rq,
...
@@ -2898,14 +2901,22 @@ static int load_balance(int this_cpu, struct rq *this_rq,
goto
out_one_pinned
;
goto
out_one_pinned
;
}
}
/*
* ->active_balance synchronizes accesses to
* ->active_balance_work. Once set, it's cleared
* only after active load balance is finished.
*/
if
(
!
busiest
->
active_balance
)
{
if
(
!
busiest
->
active_balance
)
{
busiest
->
active_balance
=
1
;
busiest
->
active_balance
=
1
;
busiest
->
push_cpu
=
this_cpu
;
busiest
->
push_cpu
=
this_cpu
;
active_balance
=
1
;
active_balance
=
1
;
}
}
raw_spin_unlock_irqrestore
(
&
busiest
->
lock
,
flags
);
raw_spin_unlock_irqrestore
(
&
busiest
->
lock
,
flags
);
if
(
active_balance
)
if
(
active_balance
)
wake_up_process
(
busiest
->
migration_thread
);
stop_one_cpu_nowait
(
cpu_of
(
busiest
),
active_load_balance_cpu_stop
,
busiest
,
&
busiest
->
active_balance_work
);
/*
/*
* We've kicked active balancing, reset the failure
* We've kicked active balancing, reset the failure
...
@@ -3012,24 +3023,29 @@ static void idle_balance(int this_cpu, struct rq *this_rq)
...
@@ -3012,24 +3023,29 @@ static void idle_balance(int this_cpu, struct rq *this_rq)
}
}
/*
/*
* active_load_balance is run by migration threads. It pushes running tasks
* active_load_balance_cpu_stop is run by cpu stopper. It pushes
* off the busiest CPU onto idle CPUs. It requires at least 1 task to be
* running tasks off the busiest CPU onto idle CPUs. It requires at
* running on each physical CPU where possible, and avoids physical /
* least 1 task to be running on each physical CPU where possible, and
* logical imbalances.
* avoids physical / logical imbalances.
*
* Called with busiest_rq locked.
*/
*/
static
void
active_load_balance
(
struct
rq
*
busiest_rq
,
int
busiest_cpu
)
static
int
active_load_balance_cpu_stop
(
void
*
data
)
{
{
struct
rq
*
busiest_rq
=
data
;
int
busiest_cpu
=
cpu_of
(
busiest_rq
);
int
target_cpu
=
busiest_rq
->
push_cpu
;
int
target_cpu
=
busiest_rq
->
push_cpu
;
struct
rq
*
target_rq
=
cpu_rq
(
target_cpu
);
struct
sched_domain
*
sd
;
struct
sched_domain
*
sd
;
struct
rq
*
target_rq
;
raw_spin_lock_irq
(
&
busiest_rq
->
lock
);
/* make sure the requested cpu hasn't gone down in the meantime */
if
(
unlikely
(
busiest_cpu
!=
smp_processor_id
()
||
!
busiest_rq
->
active_balance
))
goto
out_unlock
;
/* Is there any task to move? */
/* Is there any task to move? */
if
(
busiest_rq
->
nr_running
<=
1
)
if
(
busiest_rq
->
nr_running
<=
1
)
return
;
goto
out_unlock
;
target_rq
=
cpu_rq
(
target_cpu
);
/*
/*
* This condition is "impossible", if it occurs
* This condition is "impossible", if it occurs
...
@@ -3058,6 +3074,10 @@ static void active_load_balance(struct rq *busiest_rq, int busiest_cpu)
...
@@ -3058,6 +3074,10 @@ static void active_load_balance(struct rq *busiest_rq, int busiest_cpu)
schedstat_inc
(
sd
,
alb_failed
);
schedstat_inc
(
sd
,
alb_failed
);
}
}
double_unlock_balance
(
busiest_rq
,
target_rq
);
double_unlock_balance
(
busiest_rq
,
target_rq
);
out_unlock:
busiest_rq
->
active_balance
=
0
;
raw_spin_unlock_irq
(
&
busiest_rq
->
lock
);
return
0
;
}
}
#ifdef CONFIG_NO_HZ
#ifdef CONFIG_NO_HZ
...
...
kernel/stop_machine.c
View file @
e7858f52
/* Copyright 2008, 2005 Rusty Russell rusty@rustcorp.com.au IBM Corporation.
/*
* GPL v2 and any later version.
* kernel/stop_machine.c
*
* Copyright (C) 2008, 2005 IBM Corporation.
* Copyright (C) 2008, 2005 Rusty Russell rusty@rustcorp.com.au
* Copyright (C) 2010 SUSE Linux Products GmbH
* Copyright (C) 2010 Tejun Heo <tj@kernel.org>
*
* This file is released under the GPLv2 and any later version.
*/
*/
#include <linux/completion.h>
#include <linux/cpu.h>
#include <linux/cpu.h>
#include <linux/
err
.h>
#include <linux/
init
.h>
#include <linux/kthread.h>
#include <linux/kthread.h>
#include <linux/module.h>
#include <linux/module.h>
#include <linux/percpu.h>
#include <linux/sched.h>
#include <linux/sched.h>
#include <linux/stop_machine.h>
#include <linux/stop_machine.h>
#include <linux/syscalls.h>
#include <linux/interrupt.h>
#include <linux/interrupt.h>
#include <linux/kallsyms.h>
#include <asm/atomic.h>
#include <asm/atomic.h>
#include <asm/uaccess.h>
/*
* Structure to determine completion condition and record errors. May
* be shared by works on different cpus.
*/
struct
cpu_stop_done
{
atomic_t
nr_todo
;
/* nr left to execute */
bool
executed
;
/* actually executed? */
int
ret
;
/* collected return value */
struct
completion
completion
;
/* fired if nr_todo reaches 0 */
};
/* the actual stopper, one per every possible cpu, enabled on online cpus */
struct
cpu_stopper
{
spinlock_t
lock
;
struct
list_head
works
;
/* list of pending works */
struct
task_struct
*
thread
;
/* stopper thread */
bool
enabled
;
/* is this stopper enabled? */
};
static
DEFINE_PER_CPU
(
struct
cpu_stopper
,
cpu_stopper
);
static
void
cpu_stop_init_done
(
struct
cpu_stop_done
*
done
,
unsigned
int
nr_todo
)
{
memset
(
done
,
0
,
sizeof
(
*
done
));
atomic_set
(
&
done
->
nr_todo
,
nr_todo
);
init_completion
(
&
done
->
completion
);
}
/* signal completion unless @done is NULL */
static
void
cpu_stop_signal_done
(
struct
cpu_stop_done
*
done
,
bool
executed
)
{
if
(
done
)
{
if
(
executed
)
done
->
executed
=
true
;
if
(
atomic_dec_and_test
(
&
done
->
nr_todo
))
complete
(
&
done
->
completion
);
}
}
/* queue @work to @stopper. if offline, @work is completed immediately */
static
void
cpu_stop_queue_work
(
struct
cpu_stopper
*
stopper
,
struct
cpu_stop_work
*
work
)
{
unsigned
long
flags
;
spin_lock_irqsave
(
&
stopper
->
lock
,
flags
);
if
(
stopper
->
enabled
)
{
list_add_tail
(
&
work
->
list
,
&
stopper
->
works
);
wake_up_process
(
stopper
->
thread
);
}
else
cpu_stop_signal_done
(
work
->
done
,
false
);
spin_unlock_irqrestore
(
&
stopper
->
lock
,
flags
);
}
/**
* stop_one_cpu - stop a cpu
* @cpu: cpu to stop
* @fn: function to execute
* @arg: argument to @fn
*
* Execute @fn(@arg) on @cpu. @fn is run in a process context with
* the highest priority preempting any task on the cpu and
* monopolizing it. This function returns after the execution is
* complete.
*
* This function doesn't guarantee @cpu stays online till @fn
* completes. If @cpu goes down in the middle, execution may happen
* partially or fully on different cpus. @fn should either be ready
* for that or the caller should ensure that @cpu stays online until
* this function completes.
*
* CONTEXT:
* Might sleep.
*
* RETURNS:
* -ENOENT if @fn(@arg) was not executed because @cpu was offline;
* otherwise, the return value of @fn.
*/
int
stop_one_cpu
(
unsigned
int
cpu
,
cpu_stop_fn_t
fn
,
void
*
arg
)
{
struct
cpu_stop_done
done
;
struct
cpu_stop_work
work
=
{
.
fn
=
fn
,
.
arg
=
arg
,
.
done
=
&
done
};
cpu_stop_init_done
(
&
done
,
1
);
cpu_stop_queue_work
(
&
per_cpu
(
cpu_stopper
,
cpu
),
&
work
);
wait_for_completion
(
&
done
.
completion
);
return
done
.
executed
?
done
.
ret
:
-
ENOENT
;
}
/**
* stop_one_cpu_nowait - stop a cpu but don't wait for completion
* @cpu: cpu to stop
* @fn: function to execute
* @arg: argument to @fn
*
* Similar to stop_one_cpu() but doesn't wait for completion. The
* caller is responsible for ensuring @work_buf is currently unused
* and will remain untouched until stopper starts executing @fn.
*
* CONTEXT:
* Don't care.
*/
void
stop_one_cpu_nowait
(
unsigned
int
cpu
,
cpu_stop_fn_t
fn
,
void
*
arg
,
struct
cpu_stop_work
*
work_buf
)
{
*
work_buf
=
(
struct
cpu_stop_work
){
.
fn
=
fn
,
.
arg
=
arg
,
};
cpu_stop_queue_work
(
&
per_cpu
(
cpu_stopper
,
cpu
),
work_buf
);
}
/* static data for stop_cpus */
static
DEFINE_MUTEX
(
stop_cpus_mutex
);
static
DEFINE_PER_CPU
(
struct
cpu_stop_work
,
stop_cpus_work
);
int
__stop_cpus
(
const
struct
cpumask
*
cpumask
,
cpu_stop_fn_t
fn
,
void
*
arg
)
{
struct
cpu_stop_work
*
work
;
struct
cpu_stop_done
done
;
unsigned
int
cpu
;
/* initialize works and done */
for_each_cpu
(
cpu
,
cpumask
)
{
work
=
&
per_cpu
(
stop_cpus_work
,
cpu
);
work
->
fn
=
fn
;
work
->
arg
=
arg
;
work
->
done
=
&
done
;
}
cpu_stop_init_done
(
&
done
,
cpumask_weight
(
cpumask
));
/*
* Disable preemption while queueing to avoid getting
* preempted by a stopper which might wait for other stoppers
* to enter @fn which can lead to deadlock.
*/
preempt_disable
();
for_each_cpu
(
cpu
,
cpumask
)
cpu_stop_queue_work
(
&
per_cpu
(
cpu_stopper
,
cpu
),
&
per_cpu
(
stop_cpus_work
,
cpu
));
preempt_enable
();
wait_for_completion
(
&
done
.
completion
);
return
done
.
executed
?
done
.
ret
:
-
ENOENT
;
}
/**
* stop_cpus - stop multiple cpus
* @cpumask: cpus to stop
* @fn: function to execute
* @arg: argument to @fn
*
* Execute @fn(@arg) on online cpus in @cpumask. On each target cpu,
* @fn is run in a process context with the highest priority
* preempting any task on the cpu and monopolizing it. This function
* returns after all executions are complete.
*
* This function doesn't guarantee the cpus in @cpumask stay online
* till @fn completes. If some cpus go down in the middle, execution
* on the cpu may happen partially or fully on different cpus. @fn
* should either be ready for that or the caller should ensure that
* the cpus stay online until this function completes.
*
* All stop_cpus() calls are serialized making it safe for @fn to wait
* for all cpus to start executing it.
*
* CONTEXT:
* Might sleep.
*
* RETURNS:
* -ENOENT if @fn(@arg) was not executed at all because all cpus in
* @cpumask were offline; otherwise, 0 if all executions of @fn
* returned 0, any non zero return value if any returned non zero.
*/
int
stop_cpus
(
const
struct
cpumask
*
cpumask
,
cpu_stop_fn_t
fn
,
void
*
arg
)
{
int
ret
;
/* static works are used, process one request at a time */
mutex_lock
(
&
stop_cpus_mutex
);
ret
=
__stop_cpus
(
cpumask
,
fn
,
arg
);
mutex_unlock
(
&
stop_cpus_mutex
);
return
ret
;
}
/**
* try_stop_cpus - try to stop multiple cpus
* @cpumask: cpus to stop
* @fn: function to execute
* @arg: argument to @fn
*
* Identical to stop_cpus() except that it fails with -EAGAIN if
* someone else is already using the facility.
*
* CONTEXT:
* Might sleep.
*
* RETURNS:
* -EAGAIN if someone else is already stopping cpus, -ENOENT if
* @fn(@arg) was not executed at all because all cpus in @cpumask were
* offline; otherwise, 0 if all executions of @fn returned 0, any non
* zero return value if any returned non zero.
*/
int
try_stop_cpus
(
const
struct
cpumask
*
cpumask
,
cpu_stop_fn_t
fn
,
void
*
arg
)
{
int
ret
;
/* static works are used, process one request at a time */
if
(
!
mutex_trylock
(
&
stop_cpus_mutex
))
return
-
EAGAIN
;
ret
=
__stop_cpus
(
cpumask
,
fn
,
arg
);
mutex_unlock
(
&
stop_cpus_mutex
);
return
ret
;
}
static
int
cpu_stopper_thread
(
void
*
data
)
{
struct
cpu_stopper
*
stopper
=
data
;
struct
cpu_stop_work
*
work
;
int
ret
;
repeat:
set_current_state
(
TASK_INTERRUPTIBLE
);
/* mb paired w/ kthread_stop */
if
(
kthread_should_stop
())
{
__set_current_state
(
TASK_RUNNING
);
return
0
;
}
work
=
NULL
;
spin_lock_irq
(
&
stopper
->
lock
);
if
(
!
list_empty
(
&
stopper
->
works
))
{
work
=
list_first_entry
(
&
stopper
->
works
,
struct
cpu_stop_work
,
list
);
list_del_init
(
&
work
->
list
);
}
spin_unlock_irq
(
&
stopper
->
lock
);
if
(
work
)
{
cpu_stop_fn_t
fn
=
work
->
fn
;
void
*
arg
=
work
->
arg
;
struct
cpu_stop_done
*
done
=
work
->
done
;
char
ksym_buf
[
KSYM_NAME_LEN
];
__set_current_state
(
TASK_RUNNING
);
/* cpu stop callbacks are not allowed to sleep */
preempt_disable
();
ret
=
fn
(
arg
);
if
(
ret
)
done
->
ret
=
ret
;
/* restore preemption and check it's still balanced */
preempt_enable
();
WARN_ONCE
(
preempt_count
(),
"cpu_stop: %s(%p) leaked preempt count
\n
"
,
kallsyms_lookup
((
unsigned
long
)
fn
,
NULL
,
NULL
,
NULL
,
ksym_buf
),
arg
);
cpu_stop_signal_done
(
done
,
true
);
}
else
schedule
();
goto
repeat
;
}
/* manage stopper for a cpu, mostly lifted from sched migration thread mgmt */
static
int
__cpuinit
cpu_stop_cpu_callback
(
struct
notifier_block
*
nfb
,
unsigned
long
action
,
void
*
hcpu
)
{
struct
sched_param
param
=
{
.
sched_priority
=
MAX_RT_PRIO
-
1
};
unsigned
int
cpu
=
(
unsigned
long
)
hcpu
;
struct
cpu_stopper
*
stopper
=
&
per_cpu
(
cpu_stopper
,
cpu
);
struct
cpu_stop_work
*
work
;
struct
task_struct
*
p
;
switch
(
action
&
~
CPU_TASKS_FROZEN
)
{
case
CPU_UP_PREPARE
:
BUG_ON
(
stopper
->
thread
||
stopper
->
enabled
||
!
list_empty
(
&
stopper
->
works
));
p
=
kthread_create
(
cpu_stopper_thread
,
stopper
,
"migration/%d"
,
cpu
);
if
(
IS_ERR
(
p
))
return
NOTIFY_BAD
;
sched_setscheduler_nocheck
(
p
,
SCHED_FIFO
,
&
param
);
get_task_struct
(
p
);
stopper
->
thread
=
p
;
break
;
case
CPU_ONLINE
:
kthread_bind
(
stopper
->
thread
,
cpu
);
/* strictly unnecessary, as first user will wake it */
wake_up_process
(
stopper
->
thread
);
/* mark enabled */
spin_lock_irq
(
&
stopper
->
lock
);
stopper
->
enabled
=
true
;
spin_unlock_irq
(
&
stopper
->
lock
);
break
;
#ifdef CONFIG_HOTPLUG_CPU
case
CPU_UP_CANCELED
:
case
CPU_DEAD
:
/* kill the stopper */
kthread_stop
(
stopper
->
thread
);
/* drain remaining works */
spin_lock_irq
(
&
stopper
->
lock
);
list_for_each_entry
(
work
,
&
stopper
->
works
,
list
)
cpu_stop_signal_done
(
work
->
done
,
false
);
stopper
->
enabled
=
false
;
spin_unlock_irq
(
&
stopper
->
lock
);
/* release the stopper */
put_task_struct
(
stopper
->
thread
);
stopper
->
thread
=
NULL
;
break
;
#endif
}
return
NOTIFY_OK
;
}
/*
* Give it a higher priority so that cpu stopper is available to other
* cpu notifiers. It currently shares the same priority as sched
* migration_notifier.
*/
static
struct
notifier_block
__cpuinitdata
cpu_stop_cpu_notifier
=
{
.
notifier_call
=
cpu_stop_cpu_callback
,
.
priority
=
10
,
};
static
int
__init
cpu_stop_init
(
void
)
{
void
*
bcpu
=
(
void
*
)(
long
)
smp_processor_id
();
unsigned
int
cpu
;
int
err
;
for_each_possible_cpu
(
cpu
)
{
struct
cpu_stopper
*
stopper
=
&
per_cpu
(
cpu_stopper
,
cpu
);
spin_lock_init
(
&
stopper
->
lock
);
INIT_LIST_HEAD
(
&
stopper
->
works
);
}
/* start one for the boot cpu */
err
=
cpu_stop_cpu_callback
(
&
cpu_stop_cpu_notifier
,
CPU_UP_PREPARE
,
bcpu
);
BUG_ON
(
err
==
NOTIFY_BAD
);
cpu_stop_cpu_callback
(
&
cpu_stop_cpu_notifier
,
CPU_ONLINE
,
bcpu
);
register_cpu_notifier
(
&
cpu_stop_cpu_notifier
);
return
0
;
}
early_initcall
(
cpu_stop_init
);
#ifdef CONFIG_STOP_MACHINE
/* This controls the threads on each CPU. */
/* This controls the threads on each CPU. */
enum
stopmachine_state
{
enum
stopmachine_state
{
...
@@ -26,174 +390,94 @@ enum stopmachine_state {
...
@@ -26,174 +390,94 @@ enum stopmachine_state {
/* Exit */
/* Exit */
STOPMACHINE_EXIT
,
STOPMACHINE_EXIT
,
};
};
static
enum
stopmachine_state
state
;
struct
stop_machine_data
{
struct
stop_machine_data
{
int
(
*
fn
)(
void
*
);
int
(
*
fn
)(
void
*
);
void
*
data
;
void
*
data
;
int
fnret
;
/* Like num_online_cpus(), but hotplug cpu uses us, so we need this. */
unsigned
int
num_threads
;
const
struct
cpumask
*
active_cpus
;
enum
stopmachine_state
state
;
atomic_t
thread_ack
;
};
};
/* Like num_online_cpus(), but hotplug cpu uses us, so we need this. */
static
void
set_state
(
struct
stop_machine_data
*
smdata
,
static
unsigned
int
num_threads
;
enum
stopmachine_state
newstate
)
static
atomic_t
thread_ack
;
static
DEFINE_MUTEX
(
lock
);
/* setup_lock protects refcount, stop_machine_wq and stop_machine_work. */
static
DEFINE_MUTEX
(
setup_lock
);
/* Users of stop_machine. */
static
int
refcount
;
static
struct
workqueue_struct
*
stop_machine_wq
;
static
struct
stop_machine_data
active
,
idle
;
static
const
struct
cpumask
*
active_cpus
;
static
void
__percpu
*
stop_machine_work
;
static
void
set_state
(
enum
stopmachine_state
newstate
)
{
{
/* Reset ack counter. */
/* Reset ack counter. */
atomic_set
(
&
thread_ack
,
num_threads
);
atomic_set
(
&
smdata
->
thread_ack
,
smdata
->
num_threads
);
smp_wmb
();
smp_wmb
();
state
=
newstate
;
s
mdata
->
s
tate
=
newstate
;
}
}
/* Last one to ack a state moves to the next state. */
/* Last one to ack a state moves to the next state. */
static
void
ack_state
(
void
)
static
void
ack_state
(
struct
stop_machine_data
*
smdata
)
{
{
if
(
atomic_dec_and_test
(
&
thread_ack
))
if
(
atomic_dec_and_test
(
&
smdata
->
thread_ack
))
set_state
(
state
+
1
);
set_state
(
s
mdata
,
smdata
->
s
tate
+
1
);
}
}
/* This is the actual function which stops the CPU. It runs
/* This is the cpu_stop function which stops the CPU. */
* in the context of a dedicated stopmachine workqueue. */
static
int
stop_machine_cpu_stop
(
void
*
data
)
static
void
stop_cpu
(
struct
work_struct
*
unused
)
{
{
struct
stop_machine_data
*
smdata
=
data
;
enum
stopmachine_state
curstate
=
STOPMACHINE_NONE
;
enum
stopmachine_state
curstate
=
STOPMACHINE_NONE
;
struct
stop_machine_data
*
smdata
=
&
idle
;
int
cpu
=
smp_processor_id
(),
err
=
0
;
int
cpu
=
smp_processor_id
();
bool
is_active
;
int
err
;
if
(
!
smdata
->
active_cpus
)
is_active
=
cpu
==
cpumask_first
(
cpu_online_mask
);
else
is_active
=
cpumask_test_cpu
(
cpu
,
smdata
->
active_cpus
);
if
(
!
active_cpus
)
{
if
(
cpu
==
cpumask_first
(
cpu_online_mask
))
smdata
=
&
active
;
}
else
{
if
(
cpumask_test_cpu
(
cpu
,
active_cpus
))
smdata
=
&
active
;
}
/* Simple state machine */
/* Simple state machine */
do
{
do
{
/* Chill out and ensure we re-read stopmachine_state. */
/* Chill out and ensure we re-read stopmachine_state. */
cpu_relax
();
cpu_relax
();
if
(
state
!=
curstate
)
{
if
(
s
mdata
->
s
tate
!=
curstate
)
{
curstate
=
state
;
curstate
=
s
mdata
->
s
tate
;
switch
(
curstate
)
{
switch
(
curstate
)
{
case
STOPMACHINE_DISABLE_IRQ
:
case
STOPMACHINE_DISABLE_IRQ
:
local_irq_disable
();
local_irq_disable
();
hard_irq_disable
();
hard_irq_disable
();
break
;
break
;
case
STOPMACHINE_RUN
:
case
STOPMACHINE_RUN
:
/* On multiple CPUs only a single error code
if
(
is_active
)
* is needed to tell that something failed. */
err
=
smdata
->
fn
(
smdata
->
data
);
err
=
smdata
->
fn
(
smdata
->
data
);
if
(
err
)
smdata
->
fnret
=
err
;
break
;
break
;
default:
default:
break
;
break
;
}
}
ack_state
();
ack_state
(
smdata
);
}
}
}
while
(
curstate
!=
STOPMACHINE_EXIT
);
}
while
(
curstate
!=
STOPMACHINE_EXIT
);
local_irq_enable
();
local_irq_enable
();
return
err
;
}
}
/* Callback for CPUs which aren't supposed to do anything. */
static
int
chill
(
void
*
unused
)
{
return
0
;
}
int
stop_machine_create
(
void
)
{
mutex_lock
(
&
setup_lock
);
if
(
refcount
)
goto
done
;
stop_machine_wq
=
create_rt_workqueue
(
"kstop"
);
if
(
!
stop_machine_wq
)
goto
err_out
;
stop_machine_work
=
alloc_percpu
(
struct
work_struct
);
if
(
!
stop_machine_work
)
goto
err_out
;
done:
refcount
++
;
mutex_unlock
(
&
setup_lock
);
return
0
;
err_out:
if
(
stop_machine_wq
)
destroy_workqueue
(
stop_machine_wq
);
mutex_unlock
(
&
setup_lock
);
return
-
ENOMEM
;
}
EXPORT_SYMBOL_GPL
(
stop_machine_create
);
void
stop_machine_destroy
(
void
)
{
mutex_lock
(
&
setup_lock
);
refcount
--
;
if
(
refcount
)
goto
done
;
destroy_workqueue
(
stop_machine_wq
);
free_percpu
(
stop_machine_work
);
done:
mutex_unlock
(
&
setup_lock
);
}
EXPORT_SYMBOL_GPL
(
stop_machine_destroy
);
int
__stop_machine
(
int
(
*
fn
)(
void
*
),
void
*
data
,
const
struct
cpumask
*
cpus
)
int
__stop_machine
(
int
(
*
fn
)(
void
*
),
void
*
data
,
const
struct
cpumask
*
cpus
)
{
{
struct
work_struct
*
sm_work
;
struct
stop_machine_data
smdata
=
{
.
fn
=
fn
,
.
data
=
data
,
int
i
,
ret
;
.
num_threads
=
num_online_cpus
(),
.
active_cpus
=
cpus
};
/* Set up initial state. */
mutex_lock
(
&
lock
);
/* Set the initial state and stop all online cpus. */
num_threads
=
num_online_cpus
();
set_state
(
&
smdata
,
STOPMACHINE_PREPARE
);
active_cpus
=
cpus
;
return
stop_cpus
(
cpu_online_mask
,
stop_machine_cpu_stop
,
&
smdata
);
active
.
fn
=
fn
;
active
.
data
=
data
;
active
.
fnret
=
0
;
idle
.
fn
=
chill
;
idle
.
data
=
NULL
;
set_state
(
STOPMACHINE_PREPARE
);
/* Schedule the stop_cpu work on all cpus: hold this CPU so one
* doesn't hit this CPU until we're ready. */
get_cpu
();
for_each_online_cpu
(
i
)
{
sm_work
=
per_cpu_ptr
(
stop_machine_work
,
i
);
INIT_WORK
(
sm_work
,
stop_cpu
);
queue_work_on
(
i
,
stop_machine_wq
,
sm_work
);
}
/* This will release the thread on our CPU. */
put_cpu
();
flush_workqueue
(
stop_machine_wq
);
ret
=
active
.
fnret
;
mutex_unlock
(
&
lock
);
return
ret
;
}
}
int
stop_machine
(
int
(
*
fn
)(
void
*
),
void
*
data
,
const
struct
cpumask
*
cpus
)
int
stop_machine
(
int
(
*
fn
)(
void
*
),
void
*
data
,
const
struct
cpumask
*
cpus
)
{
{
int
ret
;
int
ret
;
ret
=
stop_machine_create
();
if
(
ret
)
return
ret
;
/* No CPUs can come up or down during this. */
/* No CPUs can come up or down during this. */
get_online_cpus
();
get_online_cpus
();
ret
=
__stop_machine
(
fn
,
data
,
cpus
);
ret
=
__stop_machine
(
fn
,
data
,
cpus
);
put_online_cpus
();
put_online_cpus
();
stop_machine_destroy
();
return
ret
;
return
ret
;
}
}
EXPORT_SYMBOL_GPL
(
stop_machine
);
EXPORT_SYMBOL_GPL
(
stop_machine
);
#endif
/* CONFIG_STOP_MACHINE */
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment