0

在过去的几周里,我一直试图让 SMP 支持在 Linux/MIPS 内核的端口上再次运行到 SGI Octane (IP30)。单处理器支持工作正常,但我在使用第二个 CPU 时遇到了很多问题。我可以将机器引导到该init进程,但大多数情况下都会因 SIGSEGV 或 SIGBUS 而死。我从 5 年前编写的补丁中获得了大部分支持代码,但我怀疑我要么没有正确锁定事物,要么意外地重新启用了 IRQ。


一些硬件背景:

MIPS R10000 系列 CPU 实现了 8 个中断,IP0IP7

  • IP0IP1:仅软件中断,目前使用不多。
  • IP2to IP6:一般路由到其他一些硬件函数进行处理
  • IP7:R10K 定时器/计数器/比较中断。

  • R10K 支持 MIPS-IV ISA,并具有 I-cache 和 D-cache。

    • I-cache 为 32kB、VIPT、2-way 和 64 字节行大小。
    • D-cache 为 32kB、VIPT、2-way、无别名和 32 字节行大小。
  • R10K L2 高速缓存为 2MB、2 路和 128 字节行大小。
  • R10K 是超标量,采用推测执行,并且可以乱序执行。
  • Octane 是缓存一致的,因此不会受到推测执行的影响。
  • 具体来说,我在这个系统中有一个 R14000 双模块。除了它主要是具有芯片缩小和更快时钟速度的 R10K 之外,对此知之甚少。SGI 从未发布此处理器的硬件数据表,也没有任何勘误信息。


Octane 有一个称为HEART其内存控制器和中断控制器的 ASIC。 HEART旨在支持多达 4 个处理器,并提供 64 个中断 (IRQ)。这 64 个 IRQ 分为几个优先级,并映射到上面的 R10K CPU IPx IRQ:

  • 级别 0,IRQ015-> CPUIP2
  • 级别 1,1631-> CPU的 IRQIP3
  • 级别 2,3149-> CPU的 IRQIP4
  • 级别 3,IRQ 50-> CPUIP5
  • 51第 4 级,到63-> CPU的 IRQIP6


关于这些优先级有一些说明:

  • 0 级和 1 级 IRQ 主要分配给系统中的设备(SCSI、以太网等)。

  • 2 级有多种用途:

    • IRQ也32可供40系统中的设备使用(尤其是那些需要更高优先级的设备)。
    • IRQ41是硬连线的,用于按下电源按钮。
    • IRQ42用于45向 4 个可能的 CPU 发送调试器信号。
    • IRQ46494 个可能的 CPU 的 SMP 处理器间中断 (IPI)。

  • 第 3 级 IRQ50专门用于HEART自身的计数器/比较计时器。它以 12.5MHz(我认为是 80ns)运行。它有一个计数寄存器和比较寄存器。从 Linux 的clockevent角度来看,我认为这是一个更好的分辨率定时器,可用作系统定时器(52 位计数器,24 位比较)。

  • 级别 4 用于错误 IRQ:

    • IRQ是 XIO 总线(51以星形拓扑排列的高速总线,由ASIC 提供服务)上588 个可用小部件中的每一个的错误 IRQ 。XtalkXBOW
    • IRQ59624 个可能的 CPU 的总线错误 IRQ。
    • IRQ63HEART自身的异常错误 IRQ。

HEART提供了几个用于处理中断的寄存器。每个寄存器为 64 位宽,每个中断一位:

  • HEART_ISR- 只读寄存器,用于获取挂起的中断列表。
  • HEART_SET_ISR- 只写寄存器设置一个特定的中断位。
  • HEART_CLR_ISR- 只写寄存器清除特定中断位
  • HEAR_IMR(x)- 读/写寄存器,用于设置或清除特定 CPU 上特定中断的中断掩码,由 . 表示x


我将以下代码用于基本的 IRQ ack/mask/unmasking 操作

u64 *imr;                       /* Address of the mask register to work on */
static int heart_irq_owner[64]; /* Which CPU owns which IRQ? (global) */

Ack:    writeq((1UL << irq), HEART_CLR_ISR);

Mask:   imr = HEART_IMR(heart_irq_owner[irq]);
        writeq(readq(imr) & (~(1UL << irq)), imr);

Unmask: imr = HEART_IMR(heart_irq_owner[irq]);
        writeq(readq(imr) | (1UL << irq), imr);


这些基本操作是使用struct irq_chip3.1x 系列 Linux 内核中的访问器实现的,我使用和保护对HEART寄存器的访问。我不能 100% 确定我是否应该在这些访问器中使用这些锁定功能。spin_lock_irqsavespin_unlock_irqrestore



为了处理所有中断,标准的 Linux/MIPS 平台调度函数采取以下动作:

  • IP7-> 调用do_IRQ()处理 CPU 定时器 IRQ。
  • IP6-> 调用以向系统日志ip30_do_error_irq()报告任何错误。HEART
  • IP5-> 调用do_IRQ()处理分配给HEART定时器的clockevent IRQ。
  • IP4, IP3, 和IP2-> 调用ip30_do_heart_irq()处理HEART从 0 到 49 的所有 IRQ。


这是当前用于的代码ip30_do_heart_irq()

static noinline void ip30_do_heart_irq(void)
{
    int irqnum = 49;
    int cpu = smp_processor_id();
    u64 heart_isr = readq(HEART_ISR);
    u64 heart_imr = readq(HEART_IMR(cpu));
    u64 irqs = (heart_isr & 0x0003ffffffffffffULL &
                heart_imr);

    /* Poll all IRQs in decreasing priority order */
    do {
        if (irqs & (1UL << irqnum))
            do_IRQ(irqnum);
        irqnum--;
    } while (likely(irqnum >= 0));
}


在 SMP 支持方面,与其他 Linux/MIPS 平台不同,我没有类似于硬件中的邮箱寄存器的东西来存储应该采取什么样的 IPI 操作。原始代码使用ip30_ipi_mailbox由 CPUID 索引的全局 int 数组 ( ) 来指定要传递给其他处理器的 IPI 操作。

此外,即使HEART设计为最多支持 4 个处理器,SGI 也只生产过双 CPU 模块。因此,IRQ 44- 4548-4961-62从未实际用于任何事情。

给定这些全局变量:

#define IPI_CPU(x) (46 + (x))
static DEFINE_SPINLOCK(ip30_ipi_lock);
static u32 ip30_ipi_mailbox[4];


这是当前用于向其他 CPU 发送 IPI 的代码:

static void ip30_send_ipi_single(int cpu, u32 action)
{
    unsigned long flags;

    spin_lock_irqsave(&ip30_ipi_lock, flags);
    ip30_ipi_mailbox[cpu] |= action;
    spin_unlock_irqrestore(&ip30_ipi_lock, flags);
    writeq(1UL << IPI_CPU(cpu)), HEART_SET_ISR);
}


为了响应 IPI,每个 CPU 调用request_irq其初始化代码并注册一个中断处理程序。这是处理程序中当前用于服务 IPI 中断的代码:

static irqreturn_t ip30_ipi_irq(int irq, void *dev_id)
{
    u32 action;
    int cpu = smp_processor_id();
    unsigned long flags;

    spin_lock_irqsave(&ip30_ipi_lock, flags);
    action = ip30_ipi_mailbox[cpu];
    ip30_ipi_mailbox[cpu] = 0;
    spin_unlock_irqrestore(&ip30_ipi_lock, flags);

    if (action & SMP_RESCHEDULE_YOURSELF)
        scheduler_ipi();

    if (action & SMP_CALL_FUNCTION)
        smp_call_function_interrupt();

    return IRQ_HANDLED;
}



这就是背景信息。

我当前的内核配置除了帧缓冲区和“Impact”视频驱动程序之外的所有内容都被剥离了。没有 PCI,没有块层,没有网络,没有串行,没有键盘/鼠标。我有一个大约 7 年历史的 initramfs,我正在加载它,如果一切正常,应该放到 bash 提示符下。然而,因为它加载到 RAM 中,它能够相当快地暴露内存损坏,结果我要么得到上述 SIGSEGV 或 SIGBUS 错误。

由于 IOC3 PCI 设备,目前无法选择使用远程 GDB 或内置 KGDB。IOC3 是一个多功能 PCI 设备,它声称是一个单功能设备,其背后是键盘/鼠标、串行端口、实时时钟和以太网的硬件位。目前还不存在绕过 IOC3 并直接访问远程 GDB 的串行端口的代码,而且 KGDB 也不知道如何与 IOC3 上的标准 i8042 键盘控制器通信。

我添加了一个标准 PCI 串行卡(基于 Moschip),但该驱动程序显然不是字节序安全的,因此探测串行端口会使内核恐慌。


我希望,回答以下问题将使我能够更好地识别错误代码并专注于使其正常工作,从而使我走上让 SMP 正常工作的正确道路:

  • 我是否正确使用自旋锁?
  • 我是否使用了正确的自旋锁变体?
  • 我是否需要在任何地方添加同步调用(即smp_rmb(),smp_wmb()等)?
  • 我的问题可能出在这个核心平台支持代码之外(例如在视频驱动程序中)吗?
  • 我可以查看随机破坏内存的未知硬件错误吗?
  • 上述任何代码都可以更好地实现吗?(其中大部分是从 Linux 2.6.17 的原始端口到 Octane 的代码,刚刚更新以更符合内核中其他事物的工作方式)

任何可以让我走上正确道路的信息都将不胜感激。我的希望是让 SMP 进入可用状态(效率无关紧要,我只需要它工作),因此我可以开始着手将其分解为补丁,并在某个时候将其包含在主线内核中。如果我不能让 SMP 工作,我将放弃它的支持并专注于让单处理器代码向上游发送。

4

2 回答 2

1

该错误最终被解决为没有将 IRQ 编号分配给正确的处理程序。我最初分配所有 64 个 IRQ 来使用handle_level_irq,这对于 SMP 处理器间中断 (IPI) 是不正确的。修复结果是分配了 8 个特定于 CPU 的中断,42-45 和 46-49 handle_percpu_irq

于 2016-04-04T02:59:14.853 回答
0

这个案例非常有趣:特别是如果有办法重现它。不幸的是我没有它:)。但是有很多事情我想检查是否必须解决这个问题: 1-禁用所有级别的缓存(每个 cpu)以丢弃错误的窥探和缓存同步机制。2-绑定单个CPU上的所有中断并启动板。如果出现问题,那么我会说您添加用于处理 SMP 中断的代码不是罪魁祸首。请将 irqs 的关联性设置为一个 CPU(比如说 cpu0)。对于这个测试,保持你的代码不变......只要确保修改中断与一个 CPU 的关联。

不要犹豫,分享结果。希望有帮助。

艾门。

于 2014-11-24T22:15:21.183 回答