Chinaunix首页 | 论坛 | 博客
  • 博客访问: 786450
  • 博文数量: 129
  • 博客积分: 3477
  • 博客等级: 中校
  • 技术积分: 1329
  • 用 户 组: 普通用户
  • 注册时间: 2006-11-30 21:53
文章分类

全部博文(129)

文章存档

2023年(10)

2022年(4)

2021年(10)

2020年(9)

2018年(1)

2017年(1)

2014年(4)

2013年(3)

2012年(12)

2011年(24)

2010年(2)

2009年(8)

2008年(6)

2007年(34)

2006年(1)

分类: LINUX

2012-02-13 12:13:39

简介: RAS 技术广泛应用于服务器领域,用以确保服务器能够安全,可靠,长时间不间断的运行。本文着重论述 RAS 在 x86 平台上的相关应用。

RAS 的全称为 Reliability, Availability and Serviceability。Reliability(可靠性)指的是系统必须尽可能的可靠,不会意外的崩溃,重启甚至导致系统物理损坏,这意味着一个 具有可靠性的系统必须能够对于某些小的错误能够做到自修复,对于无法自修复的错误也尽可能进行隔离,保障系统其余部分正常运转。 Availability(可用性)指的是系统必须能够确保尽可能长时间工作而不下线,即使系统出现一些小的问题也不会影响整个系统的正常运行,在某些情 况下甚至可以进行 Hot Plug 的操作,替换有问题的组件,从而严格的确保系统的 downtime 时间在一定范围内。Serviceability 指的是系统能够提供便利的诊断功能,如系统日志,动态检测等手段方便管理人员进行系统诊断和维护操作,从而及早的发现错误并且修复错误。综上所述,RAS 作为一个整体,其作用在于确保整个系统尽可能长期可靠的运行而不下线,并且具备足够强大的容错机制。这对于像大型的数据中心,网络中心如股票证券交易所, 电信机房,银行的数据库中心等应用环境是不可或缺的一部分。

由于 RAS 的独特性,构建一个稳固的 RAS 系统并不是一个简单的工程,这需要软硬件的协同配合。以 X86 平台为例,在 CPU,chipset,IO 控制器等硬件层面上需要提供诸如 ECC/Parity 检验这类的错误检测,错误纠正和错误报告扩展,同时为了提高可靠性,可能还需要进一步提供 RAID 存储以及可以 Hot Plug 的硬盘,内存,甚至是 CPU,以确保在必要的时候可以快速的替换出问题的组件。在 Firmware(这里可近似认为是 BIOS)这样的中间层则需要提供一个无缝的接口,对硬件报告的错误能够处理,同时也能够将错误或者处理的结果通知给 OS 以便软件能做进一步处理。对于 OS 和 application 而言,则需要提供更加细分的处理逻辑,譬如能够将错误分类分级以便进行有针对性的处理,记录等操作,或者提供测试接口以便自诊断或者调试。以下就以 x86 平台为例,对以上提及的几个方面分别介绍其功能和在 Linux 系统中的实现。

Hot Plug 包括常说的 Hot Plug-in/out。Hot Plug 的最大作用就是可以在线更新,从而确保系统能够持续可靠的运行。从硬件的角度看,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. static void acpi_os_execute_deferred(struct work_struct *work)
  2.  {
  3.     struct acpi_os_dpc *dpc = container_of(work, struct acpi_os_dpc, work);

  4.     if (dpc->wait)
  5.         acpi_os_wait_events_complete(NULL);

  6.     dpc->function(dpc->context);/* acpi_ev_notify_dispatchwill be called */
  7.     kfree(dpc);
  8.  }

  9.  acpi_ev_notify_dispatch ->
  10.     container_notify_cb

  11.  static void container_notify_cb(acpi_handle handle, u32 type, void *context)
  12.  {
  13.     struct acpi_device *device = NULL;
  14.     int result;
  15.     int present;
  16.     acpi_status status;

  17.     present = is_device_present(handle);

  18.     switch (type) {
  19.     case ACPI_NOTIFY_BUS_CHECK:
  20.           /* Fall through */
  21.     case ACPI_NOTIFY_DEVICE_CHECK:
  22.             printk(KERN_WARNING "Container driver received %s event\n",
  23.                    (type == ACPI_NOTIFY_BUS_CHECK) ?
  24.                    "ACPI_NOTIFY_BUS_CHECK" : "ACPI_NOTIFY_DEVICE_CHECK");
  25.             status = acpi_bus_get_device(handle, &device);
  26.             if (present) {
  27.                 if (ACPI_FAILURE(status) || !device) {
  28.                         result = container_device_add(&device, handle);
  29.                         if (!result)
  30.                             kobject_uevent(&device->dev.kobj,
  31.                                    KOBJ_ONLINE);
  32.                         else
  33.                             printk(KERN_WARNING
  34.                                "Failed to add container\n");
  35.                 }
  36.             } else {
  37.                 if (ACPI_SUCCESS(status)) {
  38.                     /* device exist and this is a remove request */
  39.                     kobject_uevent(&device->dev.kobj, KOBJ_OFFLINE);
  40.                 }
  41.             }
  42.             break;
  43.         case ACPI_NOTIFY_EJECT_REQUEST:
  44.             if (!acpi_bus_get_device(handle, &device) && device) {
  45.                 kobject_uevent(&device->dev.kobj, KOBJ_OFFLINE);
  46.             }
  47.             break;
  48.         default:
  49.             break;
  50.         }
  51.         return;
  52.  }
对于 Hot Add 操作,首先系统会打印诸如 ” Container driver received ACPI_NOTIFY_BUS_CHECK event”的信息到控制台,然后根据检测到的 Bus 状态和 Device 状态决定是否需要在 ACPI Bus 上添加一个新的设备,这里当然是需要的,因此 container_device_add会被调用,在执行过程中利用 sysfs 的对象模型,根据安装在 ACPI Bus 上的 driver 来匹配新增加的设备,经历
  1. acpi_bus_add
  2. acpi_bus_scan
  3. device_add
  4. driver_probe_device
  5. acpi_device_probe
  6. 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 的部分机制来完成自己的操作。如下所示:

  1. #define CPU_DOWN_PREPARE_FROZEN (
  2.     CPU_DOWN_PREPARE | CPU_TASKS_FROZEN)
  3.  suspend_enter ->
  4.  disable_nonboot_cpus ->
  5.  /* 1 means CPU_TASKS_FROZEN */
  6.  _cpu_down(cpu, 1)

online/offline 的 sys 接口位于 drivers/base/cpu.c 中:

  1. static SYSDEV_ATTR(online, 0644, show_online, store_online);

以 CPU offline 为例,实现过程如下:

  1. store_online->cpu_down(_cpu_down)
  • 发送通知事件 CPU_DOWN_PREPARE 或者 CPU_DOWN_PREPARE_FROZEN 给准备 offline 的 CPU,让其进行准备工作。这包括:
    • 将所有关联在准备 offline 的 CPU 上的进程迁移到其他 CPU 上
    • 将所有关联在准备 offline 的 CPU 上的中断迁移到其他 CPU 上
  • OS 调用体系结构相关的函数 __cpu_disable()执行体系结构相关的清理工作
  • 如果以上操作成功,则发送一个清理成功的事件到处于 CPU_DEAD 或者 CPU_DEAD_FROZEN 的 CPU 上

  1. static int __ref _cpu_down(unsigned int cpu, int tasks_frozen)
  2.  {
  3.     ....
  4.     err = __raw_notifier_call_chain(&cpu_chain, CPU_DOWN_PREPARE| mod,
  5.                                 hcpu, -1, &nr_calls);
  6.     ....
  7.     err = __stop_machine(take_cpu_down, &tcd_param, cpumask_of(cpu));
  8.     ....
  9.     /* This actually kills the CPU. */
  10.     __cpu_die(cpu);

  11.     /* CPU is completely dead: tell everyone. Too late to complain. */
  12.     if (raw_notifier_call_chain(&cpu_chain, CPU_DEAD| mod,
  13.                             hcpu) == NOTIFY_BAD)
  14.         BUG();
  15.     ....
  16.  }

  17.  static int __ref take_cpu_down(void *_param)
  18.  {
  19.     struct take_cpu_down_param *param = _param;
  20.     int err;

  21.     /* Ensure this CPU doesn't handle any more interrupts. */
  22.     err = __cpu_disable();
  23.     if (err < 0)
  24.         return err;

  25.     raw_notifier_call_chain(&cpu_chain, CPU_DYING | param->mod,
  26.                             param->hcpu);

  27.     /* Force idle task to run as soon as we yield: it should
  28.         immediately notice cpu is offline and die quickly. */
  29.        sched_idle_next();
  30.         
  31.     return 0;
  32.  }

BSP Hot Remove 的限制

在 x86 架构中,默认的 timer 中断定义如下

  1. static struct irqaction irq0 = {
  2.   .handler = timer_interrupt,
  3.   .flags = IRQF_DISABLED |
  4.    IRQF_NOBALANCING|
  5.   IRQF_IRQPOLL | IRQF_TIMER,
  6.  .name = "timer"
  7.      };

由于 IRQF_NOBALANCING 的存在,在 IRQ descriptor 初始化的时候会和其他中断有所分别:

  1. __setup_irq
  2.   ....
  3.  /* Exclude IRQ from balancing */
  4.  /* if requested */
  5.  if (new->flags & IRQF_NOBALANCING)
  6.    desc->status |= IRQ_NO_BALANCING;

以使用 IOAPIC 中断控制器为例,在初始化中断向量表的 RTE(Redirection Table Entry)时,由于 IRQ_NO_BALANCING 的限制会使得 IRQ0 被绑定在了 BSP CPU 上。

  1. setup_ioapic_dest
  2.   ....
  3.  if (desc->status & (IRQ_NO_BALANCING|
  4.           IRQ_AFFINITY_SET))
  5.     mask = desc->affinity;
  6.      ....

即使通过 /proc/irq/xx/smp_affnity 来修改 interrupt affinity 也是不可以的,因为在其对应的写接口实现中限制仍然存在:

  1. irq_affinity_proc_write
  2.  ....
  3.  if (!irq_to_desc(irq)->chip->set_affinity
  4.   ||no_irq_affinity ||
  5.      irq_balancing_disabled(irq))
  6.      return -EIO;
  7.      ....

目前 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 在服务器领域的进一步拓展。







阅读(2647) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~