/* the actual stopper, one per every possible cpu, enabled on online cpus */ structcpu_stopper { // 每个cpu对应的内核线程,名字为"migration/序号" structtask_struct *thread; spinlock_t lock; bool enabled; /* is this stopper enabled? */ // 排队等待执行的work链表,由上面lock保护 structlist_headworks;/* list of pending works */ // stop_cpus使用该成员挂载到works链表,被stop_cpus_mutex保护 structcpu_stop_workstop_work;/* for stop_cpus */ }; staticDEFINE_PER_CPU(struct cpu_stopper, cpu_stopper);
/** * smpboot_register_percpu_thread - Register a per_cpu thread related to hotplug * @plug_thread: Hotplug thread descriptor * * Creates and starts the threads on all online cpus. */ intsmpboot_register_percpu_thread(struct smp_hotplug_thread *plug_thread) { unsignedint cpu; int ret = 0; if (!alloc_cpumask_var(&plug_thread->cpumask, GFP_KERNEL)) return -ENOMEM; cpumask_copy(plug_thread->cpumask, cpu_possible_mask);
int stop_machine(int (*fn)(void *), void *data, const struct cpumask *cpus) { int ret;
/* No CPUs can come up or down during this. */ // 暂时阻止CPU热插拔 get_online_cpus(); // 不考虑CPU热插拔的stop_machine实现 ret = __stop_machine(fn, data, cpus); // 恢复CPU热插拔 put_online_cpus(); return ret; }
int __stop_machine(int (*fn)(void *), void *data, const struct cpumask *cpus) { // 栈上的msdata,multi_cpu_stop函数会接收其指针作为参数 structmulti_stop_datamsdata = { .fn = fn, .data = data, .num_threads = num_online_cpus(), .active_cpus = cpus, }; // 如果在cpu_stop_init初始化完成之前,就是直接关中断执行fn。正常不会进入这里 if (!stop_machine_initialized) { /* * Handle the case where stop_machine() is called * early in boot before stop_machine() has been * initialized. */ unsignedlong flags; int ret; WARN_ON_ONCE(msdata.num_threads != 1); local_irq_save(flags); hard_irq_disable(); ret = (*fn)(data); local_irq_restore(flags); return ret; } /* Set the initial state and stop all online cpus. */ // 初始化msdata状态机相关 set_state(&msdata, MULTI_STOP_PREPARE); // stop_cpus参数cpu_online_mask。停止所有在线的cpu的其他工作,调用函数multi_cpu_stop,函数参数为msdata return stop_cpus(cpu_online_mask, multi_cpu_stop, &msdata); }
multi_stop_state
1 2 3 4 5 6 7 8 9 10 11 12 13
/* This controls the threads on each CPU. */ enum multi_stop_state { /* Dummy starting state for thread. */ MULTI_STOP_NONE, /* Awaiting everyone to be scheduled. */ MULTI_STOP_PREPARE, /* Disable interrupts. */ MULTI_STOP_DISABLE_IRQ, /* Run the function */ MULTI_STOP_RUN, /* Exit */ MULTI_STOP_EXIT, };
stop_cpus
1 2 3 4 5 6 7 8 9 10 11 12 13
intstop_cpus(const struct cpumask *cpumask, cpu_stop_fn_t fn, void *arg) { int ret; /* static works are used, process one request at a time */ // 同一时间只允许有一个stop_cpus运行 // 因为这里面调用的函数queue_stop_cpus_work用到了cpu_stopper.stop_work // 将其插入了cpu_stopper.works链表,在运行完成前,stop_work这个成员不能被重复使用。 mutex_lock(&stop_cpus_mutex); ret = __stop_cpus(cpumask, fn, arg); mutex_unlock(&stop_cpus_mutex); return ret; }
/* This is the cpu_stop function which stops the CPU. */ staticintmulti_cpu_stop(void *data) { structmulti_stop_data *msdata = data; enum multi_stop_state curstate = MULTI_STOP_NONE; int cpu = smp_processor_id(), err = 0; unsignedlong flags; bool is_active; /* * When called from stop_machine_from_inactive_cpu(), irq might * already be disabled. Save the state and restore it on exit. */ local_save_flags(flags); // 选择哪些cpu执行函数msdata->fn // active_cpus为NULL,则只有第一个在线cpu执行fn。不为NULL则掩码内所有cpu执行fn // 由stop_machine接收的参数决定 if (!msdata->active_cpus) is_active = cpu == cpumask_first(cpu_online_mask); else is_active = cpumask_test_cpu(cpu, msdata->active_cpus); /* Simple state machine */ // 一个简单的状态机,用于同步所有需要停止的cpu的工作阶段。 // 所有需要停止的cpu都经历过一个状态后,才会进入下一个状态,由最后一个计数的cpu做msdata的状态变更。 // 先是进入准备状态 // 关闭中断状态,每个进入此状态的cpu关闭中断 // 任务执行状态,根据前面判定的is_active决定当前cpu是否执行函数fn,stop_machine参数中的函数 // 最后是退出状态 do { /* Chill out and ensure we re-read multi_stop_state. */ cpu_relax(); if (msdata->state != curstate) { curstate = msdata->state; switch (curstate) { case MULTI_STOP_DISABLE_IRQ: local_irq_disable(); hard_irq_disable(); break; case MULTI_STOP_RUN: if (is_active) err = msdata->fn(msdata->data); break; default: break; } // 计数和状态切换 ack_state(msdata); } elseif (curstate > MULTI_STOP_PREPARE) { /* * At this stage all other CPUs we depend on must spin * in the same loop. Any reason for hard-lockup should * be detected and reported on their side. */ touch_nmi_watchdog(); } } while (curstate != MULTI_STOP_EXIT); local_irq_restore(flags); return err; }