CPU/Memory 的 Hot Plug
Hot Plug 包括常说的 Hot Plug-in/out。Hot Plug 的{zd0}作用就是可以在线更新,从而确保系统能够持续可靠的运行。从硬件的角度看,Hot Plug 有四种状态:
1. CPU/Memory 不在 socket 上,firmware/BIOS 不知道它的存在
2. CPU/Memory 在 socket 上,firmware/BIOS 已知但是未通知 OS,因此 OS 未知
3. CPU/Memory 在 socket 上,firmware/BIOS 也通知了 OS,OS 已知但是未使能它,application 无法使用
4. CPU/Memory 在 socket 上,firmware/BIOS,OS 都已知并且 OS 使能了它,application 可以使用
从 2 到 3 的变化,就是通常所说的 Hot Add,反之则是 Hot Remove;从 3 到 4 的变化,就是通常说的 online,反之则是 offline。从系统设计的角度来看,任何一个 CPU/Memory 都是对等的,都可以执行 Hot Plug 操作,但是由于软硬件的某些限制,对于 BSP(Boot Strap Processor)CPU 一般是不可以被 Hot Remove 的。
下面以 CPU 的 Hot Add 为例描述这一过程:
1. 用户将 CPU 插入一个空闲的 socket 中
2. 用户通过 Hot Plug 的接口初始化 Hot Add 这一动作。接口可以是 OS 提供的 UI 接口,按一个按钮,或者是某些管理接口,如 IPMI,AMT
3. firmware/BIOS 对插入的 CPU 进行必要的初始化操作,如配置 QPI 总线的路由表,更新地址解码等
4. 通过 ACPI 中断接口(SCI 中断)向 OS 产生一个 Hot Add 的事件
5. OS 在接收到这个 ACPI 事件后首先需要通过 ACPI 的 _OSI 方法检查当前系统是否支持"Module Device"的能力,如果是则表明可以进行 Hot Add 操作
6. OS 通过 ACPI 的 _MAT 方法得到 MADT 描述表,用来初始化 Local APIC/SAPIC 以及 local NMI 中断
7. OS 对新增的 CPU 进行相关的电源管理配置,如 P/C/T state
8. OS 调用 ACPI 的 _OST 方法通知 firmware/BIOS 本次 Hot Add 成功与否
在这个过程中,ACPICA(ACPICA 是 OS 用来和 firmware/BIOS 的接口)会完成绝大部分和 ACPI table 交互的工作,最终,ACPICA 会通过工作线程 acpi_os_execute_deferred将后继的事件分发处理交由 kernel 完成。
1. CPU Hot-Add 的执行过程
static void acpi_os_execute_deferred(struct work_struct *work)
{
struct acpi_os_dpc *dpc = container_of(work, struct acpi_os_dpc, work);
if (dpc->wait)
acpi_os_wait_events_complete(NULL);
dpc->function(dpc->context);/* acpi_ev_notify_dispatchwill be called */
kfree(dpc);
}
acpi_ev_notify_dispatch ->
container_notify_cb
static void container_notify_cb(acpi_handle handle, u32 type, void *context)
{
struct acpi_device *device = NULL;
int result;
int present;
acpi_status status;
present = is_device_present(handle);
switch (type) {
case ACPI_NOTIFY_BUS_CHECK:
/* Fall through */
case ACPI_NOTIFY_DEVICE_CHECK:
printk(KERN_WARNING "Container driver received %s event\n",
(type == ACPI_NOTIFY_BUS_CHECK) ?
"ACPI_NOTIFY_BUS_CHECK" : "ACPI_NOTIFY_DEVICE_CHECK");
status = acpi_bus_get_device(handle, &device);
if (present) {
if (ACPI_FAILURE(status) || !device) {
result = container_device_add(&device, handle);
if (!result)
kobject_uevent(&device->dev.kobj,
KOBJ_ONLINE);
else
printk(KERN_WARNING
"Failed to add container\n");
}
} else {
if (ACPI_SUCCESS(status)) {
/* device exist and this is a remove request */
kobject_uevent(&device->dev.kobj, KOBJ_OFFLINE);
}
}
break;
case ACPI_NOTIFY_EJECT_REQUEST:
if (!acpi_bus_get_device(handle, &device) && device) {
kobject_uevent(&device->dev.kobj, KOBJ_OFFLINE);
}
break;
default:
break;
}
return;
}
对于 Hot Add 操作,首先系统会打印诸如 ” Container driver received ACPI_NOTIFY_BUS_CHECK event”的信息到控制台,然后根据检测到的 Bus 状态和 Device 状态决定是否需要在 ACPI Bus 上添加一个新的设备,这里当然是需要的,因此 container_device_add会被调用,在执行过程中利用 sysfs 的对象模型,根据安装在 ACPI Bus 上的 driver 来匹配新增加的设备,经历
acpi_bus_add
acpi_bus_scan
device_add
driver_probe_device
acpi_device_probe
acpi_processor_add
等一系列操作,最终一个新的 CPU 会被添加到相应的节点上并提供一个 sysfs 的接口供用户使用。然后用户可以通过执行诸如"echo 1 > /sys/devices/system/cpu/cpuX/online"这样的命令来使能新加入的 CPU,即 CPU online 操作,从而让 CPU 可以进入正常的调度作业。至此,一个新的 CPU 就被加入到正在运行的系统中了。
注:什么是 CPU_XXX_FROZEN 事件
在 CPU offline 操作中真正使用的事件只有 CPU_DOWN_PREPARE 和 CPU_DEAD,无论是 BSP 还是 non-boot 的 CPU。因为正在执行 offline 操作的 CPU,其上正在执行的 task 必定是活动的,否则无法执行代码。另外两个事件,CPU_DOWN_PREPARE_FROZEN / CPU_DEAD_FROZEN 其实是为了 non-boot CPU 在 suspend 的时候使用。换言之,suspend/resume 利用了 Hot Plug 的部分机制来完成自己的操作。如下所示:
#define CPU_DOWN_PREPARE_FROZEN (
CPU_DOWN_PREPARE | CPU_TASKS_FROZEN)
suspend_enter ->
disable_nonboot_cpus ->
/* 1 means CPU_TASKS_FROZEN */
_cpu_down(cpu, 1)
online/offline 的 sys 接口位于 drivers/base/cpu.c 中:
static SYSDEV_ATTR(online, 0644, show_online, store_online);
以 CPU offline 为例,实现过程如下:
store_online->cpu_down(_cpu_down)
* 发送通知事件 CPU_DOWN_PREPARE 或者 CPU_DOWN_PREPARE_FROZEN 给准备 offline 的 CPU,让其进行准备工作。这包括:
o 将所有关联在准备 offline 的 CPU 上的进程迁移到其他 CPU 上
o 将所有关联在准备 offline 的 CPU 上的中断迁移到其他 CPU 上
* OS 调用体系结构相关的函数 __cpu_disable()执行体系结构相关的清理工作
* 如果以上操作成功,则发送一个清理成功的事件到处于 CPU_DEAD 或者 CPU_DEAD_FROZEN 的 CPU 上
2. CPU offline 的执行过程
static int __ref _cpu_down(unsigned int cpu, int tasks_frozen)
{
….
err = __raw_notifier_call_chain(&cpu_chain, CPU_DOWN_PREPARE| mod,
hcpu, -1, &nr_calls);
….
err = __stop_machine(take_cpu_down, &tcd_param, cpumask_of(cpu));
….
/* This actually kills the CPU. */
__cpu_die(cpu);
/* CPU is completely dead: tell everyone. Too late to complain. */
if (raw_notifier_call_chain(&cpu_chain, CPU_DEAD| mod,
hcpu) == NOTIFY_BAD)
BUG();
….
}
static int __ref take_cpu_down(void *_param)
{
struct take_cpu_down_param *param = _param;
int err;
/* Ensure this CPU doesn’t handle any more interrupts. */
err = __cpu_disable();
if (err < 0)
return err;
raw_notifier_call_chain(&cpu_chain, CPU_DYING | param->mod,
param->hcpu);
/* Force idle task to run as soon as we yield: it should
immediately notice cpu is offline and die quickly. */
sched_idle_next();
return 0;
}
BSP Hot Remove 的限制
在 x86 架构中,默认的 timer 中断定义如下
static struct irqaction irq0 = {
.handler = timer_interrupt,
.flags = IRQF_DISABLED |
IRQF_NOBALANCING|
IRQF_IRQPOLL | IRQF_TIMER,
.name = "timer"
};
由于 IRQF_NOBALANCING 的存在,在 IRQ descriptor 初始化的时候会和其他中断有所分别:
__setup_irq
….
/* Exclude IRQ from balancing */
/* if requested */
if (new->flags & IRQF_NOBALANCING)
desc->status |= IRQ_NO_BALANCING;
….
以使用 IOAPIC 中断控制器为例,在初始化中断向量表的 RTE(Redirection Table Entry)时,由于 IRQ_NO_BALANCING 的限制会使得 IRQ0 被绑定在了 BSP CPU 上。
setup_ioapic_dest
….
if (desc->status & (IRQ_NO_BALANCING|
IRQ_AFFINITY_SET))
mask = desc->affinity;
….
即使通过 /proc/irq/xx/smp_affnity 来修改 interrupt affinity 也是不可以的,因为在其对应的写接口实现中限制仍然存在:
irq_affinity_proc_write
….
if (!irq_to_desc(irq)->chip->set_affinity
||no_irq_affinity ||
irq_balancing_disabled(irq))
return -EIO;
….
目前 Linux 内核对 CPU online/offline 的实现已经比较稳定,但是对于 CPU/Memory 的 Hot Add 支持还不是很稳定,因为这涉及到 firmware/BIOS 与 OS 的交互,如插入一个新的内存模块时 OS 需要得到用来更新 NUMA 节点相关的 SRAT 表,这需要 firmware/BIOS 和 OS 两方面协同工作。这还主要是针对 64bit 的平台而言,至于 32bit 的平台,基本上还处于一个不可用的状态。这主要是实用性的问题,毕竟这类需求多用在服务器平台上。而对于服务器而言,由于内存尺寸,寻址空间等限制很少会选择 32bit 的平台,所以很多功能在 23bit 的平台上没有实现或者并没有充分测试过可用性。至于 Hot Remove,无论是软件,firmware/BIOS 甚至是硬件平台,其支持程度都很不成熟,到目前为止还无法真正投入使用。这也一定程度上限制了 Linux 在服务器领域的进一步拓展。
Leave a Reply