分类: LINUX
2013-01-04 17:46:52
CPU热插拔的FAQ
Q:如何使我的内核能够支持处理器热插拔?
A: 在make defconfig时使能CPU热插拔的支持:
"Processor type and Features" -> Support for Hotpluggable CPUs
另外还需打开CONFIG_HOTPLUG和CONFIG_SMP选项。如果需要支持SMP的挂起/恢复,也需要打开CONFIG_HOTPLUG_CPU选项。
Q: 如果测试一个新编译的内核是否支持热插拔?
A: 请注意/sys中的一个文件。
首先使用mount命令确定sysfs是否已挂载。请注意输出中是否有如下的语句。
....
none on /sys type sysfs (rw)
....
如果有这句话表明/sys已经挂载。
/sys如果没有挂载,可以通过如下方式挂载:
#mount -t sysfs sys /sys
现在可以看到与所有系统中已存在的CPU对应的文件夹,下面是一个8路系统中的例子。
#pwd
#/sys/devices/system/cpu
#ls -l
total 0
drwxr-xr-x 10 root root 0 Sep 19 07:44 .
drwxr-xr-x 13 root root 0 Sep 19 07:45 ..
drwxr-xr-x 3 root root 0 Sep 19 07:44 cpu0
drwxr-xr-x 3 root root 0 Sep 19 07:44 cpu1
drwxr-xr-x 3 root root 0 Sep 19 07:44 cpu2
drwxr-xr-x 3 root root 0 Sep 19 07:44 cpu3
drwxr-xr-x 3 root root 0 Sep 19 07:44 cpu4
drwxr-xr-x 3 root root 0 Sep 19 07:44 cpu5
drwxr-xr-x 3 root root 0 Sep 19 07:44 cpu6
drwxr-xr-x 3 root root 0 Sep 19 07:48 cpu7
在每个文件夹下,都有名为“online”的文件,这是一个控制文件[control file. I like the word.],可以用来使能/禁用[online/offline]一个处理器。
Q:热插/热拔是否对应物理上对处理器的添加/移除?
A: 这里对热插/热拔的使用并不是与其字面上的意义完全一致。CONFIG_HOTPLUG_CPU使得内核能够进行逻辑上的使能与禁用。为了支持物理上的添加/移除,需要一些BIOS回调函数,并且还需要平台具有一些类似于PCI热插拔按钮之类的机制。CONFIG_ACPI_HOTPLUG_CPU使得ACPI能够支持CPU在物理上的添加/移除。
Q:如何在逻辑上禁用一个CPU?
A: 执行如下操作。
#echo 0 > /sys/devices/system/cpu/cpuX/online
如果逻辑上的禁用成功,检查
#cat /proc/interrupts
在此文件中,将看不到被移除的CPU对应的列了。当CPU被移除后,它的online文件为0,否则为1.
#To display the current cpu state.
#cat /sys/devices/system/cpu/cpuX/online
Q: 为什么在一些系统中无法移除CPU0?
A:一些体系结构中,某些中断只能发送给特定的CPU,一般是CPU0。
这种情况下,你会发现cpu0没有online文件。
Q: 如果一个特殊的CPU不能被移除,我如何找出它?
A: 这个依赖于具体的实现方法,在一些体系结构上,我们找不到这些CPU的“online”文件。
在某些情况下,可以在运行时进行检查,即,如果你希望移除最后一个CPU,这是不允许的。此时,“echo”命令会给出一个错误提示。
Q:当一个CPU在逻辑上被移除时,会发生什么?
A:将会发生下面的事情,排列是无序的 J。
- 内核中的模块会接收到一个通知[notification],对应的事件是CPU_DOWN_PREPARE 或者CPU_DOWN_PREPARE_FROZEN,具体是哪个事件则依赖于CPU被移除时,是否有任务被“冷冻”,任务被冷冻的原因是正在执行挂起操作。
- 此CPU上的所有进程都被迁移到新的CPU上。新CPU通过每个进程的当前处理器设置(cpuset)进行选择,这些设置可能是所有在用CPU的子集。
- 所有定向到此CPU上的中断都被迁移到新的CPU上。
- 定时器/BH/task lets也将被迁移到新的CPU上。
- 一旦所有的服务都被迁移了,内核便调用一个体系结构相关[arch specific]的例程__cpu_disable()来执行体系结构相关的清理工作。
- 如果上面的工作也完成了,一个代表清理成功的事件将被发送,此事件为CPU_DEAD。(如果存在冰冻任务,相应的事件则为CPU_DEAD_FROZEN。也就是说在移除CPU时,系统正在执行挂起操作。)
当CPU_DOWN_PREPARE通知链被调用时,所有服务都应该被清除。当CPU_DEAD被调用时,不应在有任何东西运行于被移除的CPU上。
Q: 如果我的内核代码需要能够感知CPU的到达和离开,我如何正确地安排通知链?
A: 下面的代码给出了你的内核代码在收到一个通知时应做的工作。
#include
static int __cpuinit foobar_cpu_callback(struct notifier_block *nfb,
unsigned long action, void *hcpu)
{
unsigned int cpu = (unsigned long)hcpu;
switch (action) {
case CPU_ONLINE:
case CPU_ONLINE_FROZEN:
foobar_online_action(cpu);
break;
case CPU_DEAD:
case CPU_DEAD_FROZEN:
foobar_dead_action(cpu);
break;
}
return NOTIFY_OK;
}
static struct notifier_block __cpuinitdata foobar_cpu_notifer =
{
.notifier_call = foobar_cpu_callback,
};
你需要在初始化函数中调用函数register_cpu_notifier()。初始化函数可能具有2种类型:
1. Early init: 仅在启动处理器在用时才调用的初始化函数。
2. Late init: 在所有的处理器都在用后才调用的初始化函数。
对第一种情况,你应该在初始化函数中加入下面的代码。
register_cpu_notifier(&foobar_cpu_notifier);
对第二种情况,你应该将下面的代码加在初始化函数中。
register_hotcpu_notifier(&foobar_cpu_notifier);
如果在准备资源时出了任何错误,你的PREPARE通知链都将出错。这将终止活动,并随后发送一个CANCELED事件。
CPU_DEAD不应该失败,它仅仅是在通知一个好消息。当某个通知链在执行时发出了一个BAD通知时,则意味着可能会发生坏事情。
Q: 我的代码被调用的次数貌似并不等于所有被使能或正在运行的CPU的个数?
A: 是的,CPU通知链仅在新的CPU被使能或禁用时才会被调用。如果你需要为系统中的每个CPU都执行一段程序,请参考下面的代码。
for_each_online_cpu(i) {
foobar_cpu_callback(&foobar_cpu_notifier, CPU_UP_PREPARE, i);
foobar_cpu_callback(&foobar_cpu_notifier, CPU_ONLINE, i);
}
Q:如果我想为一种新的体系结构开发CPU热插拔的支持,如何做才能使工作量最小?
A: 要使CPU热插拔的基础框架能够正确工作,需要做以下工作。
- 确定在Kconfig中添加了使能CONFIG_HOTPLUG_CPU的选项。
- __cpu_up(): 使能一个CPU时,面向体系结构的接口。
- __cpu_disable(): 关闭一个CPU时,面向体系结构的接口,在此函数返回时,不在有中断会被内核处理。局部APIC定时器等设备也被关掉了。
- __cpu_die(): 此函数用来确认某个CPU是否真的被关掉了。最好还是去仔细看一下为其它体系结构而编写的热插拔代码。尤其是在这种体系结构上,如何将CPU中idle()循环中关掉。__cpu_die()通常是在等待某些每CPU状态被设置,以确保处理器关闭例程被正确地调用。
Q: 当一些与某个特殊的CPU相关的工作正在执行时,我需要确保这个特殊的CPU不会被移除。
A: 有2种方式,可以使一个CPU不会被移除。如果你的代码可以在中断上下文中执行,那么就使用函数smp_call_function_single(),否则就使用work_on_cpu()。需要注意的是,work_on_cpu()执行的很慢,而且可能由于内存不够而失败。
int my_func_on_cpu(int cpu)
{
int err;
get_online_cpus();
if (!cpu_online(cpu))
err = -EINVAL;
else
#if NEEDS_BLOCKING
err = work_on_cpu(cpu, __my_func_on_cpu, NULL);
#else
smp_call_function_single(cpu, __my_func_on_cpu, &err,
true);
#endif
put_online_cpus();
return err;
}