您的位置:首页 > 其它

smpboot_register_percpu_thread_cpumask的作用

2017-11-28 16:01 281 查看
int smpboot_register_percpu_thread_cpumask(struct smp_hotplug_thread *plug_thread,
const struct cpumask *cpumask)
用于在cpumask表示的每个cpu上创建并运行一个thread
其用法如下:
err = smpboot_register_percpu_thread_cpumask(&watchdog_threads,
&watchdog_cpumask);

其中第一个参数watchdog_threads的定于如下:
static struct smp_hotplug_thread watchdog_threads = {
.store			= &softlockup_watchdog,
.thread_should_run	= watchdog_should_run,
.thread_fn		= watchdog,
.thread_comm		= "watchdog/%u",
.setup			= watchdog_enable,
.cleanup		= watchdog_cleanup,
.park			= watchdog_disable,
.unpark			= watchdog_enable,
};
下来我们看看smpboot_register_percpu_thread_cpumask 执行的过程
int smpboot_register_percpu_thread_cpumask(struct smp_hotplug_thread *plug_thread,
const struct cpumask *cpumask)
{
unsigned int cpu;
int ret = 0;
#申请一个plug_thread->cpumask 后,通过cpumask_copy将cpumask 里面的值赋值给plug_thread->cpumask
if (!alloc_cpumask_var(&plug_thread->cpumask, GFP_KERNEL))
return -ENOMEM;
cpumask_copy(plug_thread->cpumask, cpumask);

get_online_cpus();
mutex_lock(&smpboot_threads_lock);
#遍历所有online,为每个online的cpu建立一个thread
for_each_online_cpu(cpu) {
ret = __smpboot_create_thread(plug_thread, cpu);
if (ret) {
smpboot_destroy_threads(plug_thread);
free_cpumask_var(plug_thread->cpumask);
goto out;
#如果online cpu不再cpumask中的话,则unpark 这个thread
if (cpumask_test_cpu(cpu, cpumask))
smpboot_unpark_thread(plug_thread, cpu);
}
#将所有通过smpboot_register_percpu_thread_cpumask 创建的thread都添加到hotplug_threads 中
list_add(&plug_thread->list, &hotplug_threads);
out:
mutex_unlock(&smpboot_threads_lock);
put_online_cpus();
return ret;
}
下来继续看看__smpboot_create_thread
static int
__smpboot_create_thread(struct smp_hotplug_thread *ht, unsigned int cpu)
{
struct task_struct *tsk = *per_cpu_ptr(ht->store, cpu);
struct smpboot_thread_data *td;

if (tsk)
return 0;
#申请一个smpboot_thread_data *td ,作为thread的参数
td = kzalloc_node(sizeof(*td), GFP_KERNEL, cpu_to_node(cpu));
if (!td)
return -ENOMEM;
td->cpu = cpu;
td->ht = ht;
#在特定的cpu上创建thread,注意这个thread的回调函数是smpboot_thread_fn
tsk = kthread_create_on_cpu(smpboot_thread_fn, td, cpu,
ht->thread_comm);
if (IS_ERR(tsk)) {
kfree(td);
return PTR_ERR(tsk);
}
/*
* Park the thread so that it could start right on the CPU
* when it is available.
*/
kthread_park(tsk);
get_task_struct(tsk);
*per_cpu_ptr(ht->store, cpu) = tsk;
//如果ht->create 不为null的话,调用ht->create,本例子中ht->create为null,具体可以参看watchdog_threads 并没有实现create函数
if (ht->create) {
/*
* Make sure that the task has actually scheduled out
* into park position, before calling the create
* callback. At least the migration thread callback
* requires that the task is off the runqueue.
*/
if (!wait_task_inactive(tsk, TASK_PARKED))
WARN_ON(1);
else
ht->create(cpu);
}
return 0;
}
继续看看回调函数smpboot_thread_fn,这个回调函数是一个死循环
static int smpboot_thread_fn(void *data)
{
struct smpboot_thread_data *td = data;
struct smp_hotplug_thread *ht = td->ht;

while (1) {
set_current_state(TASK_INTERRUPTIBLE);
preempt_disable();
if (kthread_should_stop()) {
__set_current_state(TASK_RUNNING);
preempt_enable();
/* cleanup must mirror setup */
if (ht->cleanup && td->status != HP_THREAD_NONE)
ht->cleanup(td->cpu, cpu_online(td->cpu));
kfree(td);
return 0;
}

if (kthread_should_park()) {
__set_current_state(TASK_RUNNING);
preempt_enable();
if (ht->park && td->status == HP_THREAD_ACTIVE) {
BUG_ON(td->cpu != smp_processor_id());
ht->park(td->cpu);
td->status = HP_THREAD_PARKED;
}
kthread_parkme();
/* We might have been woken for stop */
continue;
}

BUG_ON(td->cpu != smp_processor_id());
#根据td->status判断是否要运行客户定义的函数,这里就是watchdog_threads的.thread_fn		= watchdog,
/* Check for state change setup */
switch (td->status) {
case HP_THREAD_NONE:
__set_current_state(TASK_RUNNING);
preempt_enable();
if (ht->setup)
ht->setup(td->cpu);
td->status = HP_THREAD_ACTIVE;
continue;

case HP_THREAD_PARKED:
__set_current_state(TASK_RUNNING);
preempt_enable();
if (ht->unpark)
ht->unpark(td->cpu);
td->status = HP_THREAD_ACTIVE;
continue;
}

if (!ht->thread_should_run(td->cpu)) {
preempt_enable_no_resched();
schedule();
} else {
__set_current_state(TASK_RUNNING);
preempt_enable();
#这里是核心调用watchdog_threads的.thread_fn		= watchdog,
ht->thread_fn(td->cpu);
}
}
}
到这里应该明白smpboot_register_percpu_thread_cpumask的作用了,这个函数可以简化用户在每个cpu上创建thread的工作量.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: