migration/* kernel thread用途
console:/proc/13 # ps -Af |grep migration root 13 2 0 02:29:43 ? 00:00:00 [migration/0] root 16 2 0 02:29:43 ? 00:00:00 [migration/1] root 21 2 0 02:29:43 ? 00:00:00 [migration/2] root 26 2 0 02:29:43 ? 00:00:00 [migration/3]
内核线程migration/*是一个工作线程,可以将工作发给它让它执行,将工作发给它有两种API,如下,这个两个API的差异是一个会等这个work function调用完成,它有一个完成量(completion),可以理解为同步方式;而另外一个是不等它完成,queue到migration/*线程的work queue并wake它就会返回,可以理解为异步方式。这两个API会根据其cpu参数找到这个cpu的per cpu变量struct cpu_stopper,然后将此work加到此cpu_stopper变量的works链表上
这两个API参数含义:
cpu: 表示这个work你要给与这个cpu绑定的migration thread去处理
fn: work function,work所做的事情;
arg:传给fn的参数,即fn(arg):
int stop_one_cpu(unsigned int cpu, cpu_stop_fn_t fn, void *arg); bool stop_one_cpu_nowait(unsigned int cpu, cpu_stop_fn_t fn, void *arg, struct cpu_stop_work *work_buf);
至于migration/*线程是怎么创建的以及有什么特性描述如下:
它是和cpu core绑定的,每个cpu core都会有这样一个线程,另外这些kernel thread sched class是stop_sched_class,sched policy是SCHED_FIFO(RT),priority是99,是RT priority里最低的优先级。在cpu_stop_threads.cpu_stop_create函数里设置此thread的sched class、policy、priority:
kernel/sched/core.c
void sched_set_stop_task(int cpu, struct task_struct *stop) { struct sched_param param = { .sched_priority = MAX_RT_PRIO - 1 }; struct task_struct *old_stop = cpu_rq(cpu)->stop; if (stop) { /* * Make it appear like a SCHED_FIFO task, its something * userspace knows about and won't get confused about. * * Also, it will make PI more or less work without too * much confusion -- but then, stop work should not * rely on PI working anyway. */ sched_setscheduler_nocheck(stop, SCHED_FIFO, ¶m); stop->sched_class = &stop_sched_class; } cpu_rq(cpu)->stop = stop; if (old_stop) { /* * Reset it back to a normal scheduling class so that * it can die in pieces. */ old_stop->sched_class = &rt_sched_class; } }
4.19/kernel/stop_machine.c
static void cpu_stop_create(unsigned int cpu) { sched_set_stop_task(cpu, per_cpu(cpu_stopper.thread, cpu)); }
static struct smp_hotplug_thread cpu_stop_threads = { .store = &cpu_stopper.thread, .thread_should_run = cpu_stop_should_run, .thread_fn = cpu_stopper_thread, .thread_comm = "migration/%u", .create = cpu_stop_create, .park = cpu_stop_park, .selfparking = true, };
static int __init cpu_stop_init(void) { unsigned int cpu; for_each_possible_cpu(cpu) { struct cpu_stopper *stopper = &per_cpu(cpu_stopper, cpu); raw_spin_lock_init(&stopper->lock); INIT_LIST_HEAD(&stopper->works); } BUG_ON(smpboot_register_percpu_thread(&cpu_stop_threads)); stop_machine_unpark(raw_smp_processor_id()); stop_machine_initialized = true; return 0; } early_initcall(cpu_stop_init);
使用stop_one_cpu()或者stop_one_cpu_nowait()将work发给目标migration线程并wake它后,migration线程将会wakeup执行cpu_stopper_thread(),从works链表中取出work,并执行其work function:
static void cpu_stopper_thread(unsigned int cpu) { struct cpu_stopper *stopper = &per_cpu(cpu_stopper, cpu); struct cpu_stop_work *work; repeat: work = NULL; raw_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); } raw_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; int ret; /* cpu stop callbacks must not sleep, make in_atomic() == T */ preempt_count_inc(); ret = fn(arg); if (done) { if (ret) done->ret = ret; cpu_stop_signal_done(done); } preempt_count_dec(); WARN_ONCE(preempt_count(), "cpu_stop: %pf(%p) leaked preempt count\n", fn, arg); goto repeat; } }