Linux kernel IOMMU代码分析笔记(7)——intel_enable_irq_remapping(1)

看一下intel_enable_irq_remapping的代码:

static int __init intel_enable_irq_remapping(void)
{
    struct dmar_drhd_unit *drhd;
    struct intel_iommu *iommu;
    bool x2apic_present;
    int setup = 0;
    int eim = 0;

    x2apic_present = x2apic_supported();

    if (parse_ioapics_under_ir() != 1) {
        printk(KERN_INFO "Not enable interrupt remapping\n");
        goto error;
    }

    if (x2apic_present) {
        pr_info("Queued invalidation will be enabled to support x2apic and Intr-remapping.\n");

        eim = !dmar_x2apic_optout();
        if (!eim)
            printk(KERN_WARNING
                "Your BIOS is broken and requested that x2apic be disabled.\n"
                "This will slightly decrease performance.\n"
                "Use 'intremap=no_x2apic_optout' to override BIOS request.\n");
    }

    for_each_iommu(iommu, drhd) {
        /*
         * If the queued invalidation is already initialized,
         * shouldn't disable it.
         */
        if (iommu->qi)
            continue;

        /*
         * Clear previous faults.
         */
        dmar_fault(-1, iommu);

        /*
         * Disable intr remapping and queued invalidation, if already
         * enabled prior to OS handover.
         */
        iommu_disable_irq_remapping(iommu);

        dmar_disable_qi(iommu);
    }

    /*
     * check for the Interrupt-remapping support
     */
    for_each_iommu(iommu, drhd) {
        if (!ecap_ir_support(iommu->ecap))
            continue;

        if (eim && !ecap_eim_support(iommu->ecap)) {
            printk(KERN_INFO "DRHD %Lx: EIM not supported by DRHD, "
                   " ecap %Lx\n", drhd->reg_base_addr, iommu->ecap);
            goto error;
        }
    }

    /*
     * Enable queued invalidation for all the DRHD's.
     */
    for_each_iommu(iommu, drhd) {
        int ret = dmar_enable_qi(iommu);

        if (ret) {
            printk(KERN_ERR "DRHD %Lx: failed to enable queued, "
                   " invalidation, ecap %Lx, ret %d\n",
                   drhd->reg_base_addr, iommu->ecap, ret);
            goto error;
        }
    }

    /*
     * Setup Interrupt-remapping for all the DRHD's now.
     */
    for_each_iommu(iommu, drhd) {
        if (!ecap_ir_support(iommu->ecap))
            continue;

        if (intel_setup_irq_remapping(iommu, eim))
            goto error;

        setup = 1;
    }

    if (!setup)
        goto error;

    irq_remapping_enabled = 1;

    /*
     * VT-d has a different layout for IO-APIC entries when
     * interrupt remapping is enabled. So it needs a special routine
     * to print IO-APIC entries for debugging purposes too.
     */
    x86_io_apic_ops.print_entries = intel_ir_io_apic_print_entries;

    pr_info("Enabled IRQ remapping in %s mode\n", eim ? "x2apic" : "xapic");

    return eim ? IRQ_REMAP_X2APIC_MODE : IRQ_REMAP_XAPIC_MODE;

error:
    /*
     * handle error condition gracefully here!
     */

    if (x2apic_present)
        pr_warn("Failed to enable irq remapping.  You are vulnerable to irq-injection attacks.\n");

    return -1;
}

(1)

x2apic_present = x2apic_supported();

查看系统是否支持x2APIC模式。

(2)

if (parse_ioapics_under_ir() != 1) {
    printk(KERN_INFO "Not enable interrupt remapping\n");
    goto error;
}

parse_ioapics_under_ir函数如下:

/*
 * Finds the assocaition between IOAPIC's and its Interrupt-remapping
 * hardware unit.
 */
static int __init parse_ioapics_under_ir(void)
{
    struct dmar_drhd_unit *drhd;
    struct intel_iommu *iommu;
    int ir_supported = 0;
    int ioapic_idx;

    for_each_iommu(iommu, drhd)
        if (ecap_ir_support(iommu->ecap)) {
            if (ir_parse_ioapic_hpet_scope(drhd->hdr, iommu))
                return -1;

            ir_supported = 1;
        }

    if (!ir_supported)
        return 0;

    for (ioapic_idx = 0; ioapic_idx < nr_ioapics; ioapic_idx++) {
        int ioapic_id = mpc_ioapic_id(ioapic_idx);
        if (!map_ioapic_to_ir(ioapic_id)) {
            pr_err(FW_BUG "ioapic %d has no mapping iommu, "
                   "interrupt remapping will be disabled\n",
                   ioapic_id);
            return -1;
        }
    }

    return 1;
}

ir_parse_ioapic_hpet_scope函数的作用是解析device scope类型是IOAPICHPET (High Precision Event Timer)

static int ir_parse_ioapic_hpet_scope(struct acpi_dmar_header *header,
                      struct intel_iommu *iommu)
{
    struct acpi_dmar_hardware_unit *drhd;
    struct acpi_dmar_device_scope *scope;
    void *start, *end;

    drhd = (struct acpi_dmar_hardware_unit *)header;

    start = (void *)(drhd + 1);
    end = ((void *)drhd) + header->length;

    while (start < end) {
        scope = start;
        if (scope->entry_type == ACPI_DMAR_SCOPE_TYPE_IOAPIC) {
            if (ir_ioapic_num == MAX_IO_APICS) {
                printk(KERN_WARNING "Exceeded Max IO APICS\n");
                return -1;
            }

            printk(KERN_INFO "IOAPIC id %d under DRHD base "
                   " 0x%Lx IOMMU %d\n", scope->enumeration_id,
                   drhd->address, iommu->seq_id);

            ir_parse_one_ioapic_scope(scope, iommu);
        } else if (scope->entry_type == ACPI_DMAR_SCOPE_TYPE_HPET) {
            if (ir_hpet_num == MAX_HPET_TBS) {
                printk(KERN_WARNING "Exceeded Max HPET blocks\n");
                return -1;
            }

            printk(KERN_INFO "HPET id %d under DRHD base"
                   " 0x%Lx\n", scope->enumeration_id,
                   drhd->address);

            ir_parse_one_hpet_scope(scope, iommu);
        }
        start += scope->length;
    }

    return 0;
}

ir_parse_one_ioapic_scope为例(ir_parse_one_hpet_scope类似):

static void ir_parse_one_ioapic_scope(struct acpi_dmar_device_scope *scope,
                      struct intel_iommu *iommu)
{
    struct acpi_dmar_pci_path *path;
    u8 bus;
    int count;

    bus = scope->bus;
    path = (struct acpi_dmar_pci_path *)(scope + 1);
    count = (scope->length - sizeof(struct acpi_dmar_device_scope))
        / sizeof(struct acpi_dmar_pci_path);

    while (--count > 0) {
        /*
         * Access PCI directly due to the PCI
         * subsystem isn't initialized yet.
         */
        bus = read_pci_config_byte(bus, path->device, path->function,
                       PCI_SECONDARY_BUS);
        path++;
    }

    ir_ioapic[ir_ioapic_num].bus   = bus;
    ir_ioapic[ir_ioapic_num].devfn = PCI_DEVFN(path->device, path->function);
    ir_ioapic[ir_ioapic_num].iommu = iommu;
    ir_ioapic[ir_ioapic_num].id    = scope->enumeration_id;
    ir_ioapic_num++;
}

可以看到,实际上是通过递归访问path得到IOAPIC信息的过程:bus好,对应的iommu设备单元,等等。

for (ioapic_idx = 0; ioapic_idx < nr_ioapics; ioapic_idx++) {
    int ioapic_id = mpc_ioapic_id(ioapic_idx);
    if (!map_ioapic_to_ir(ioapic_id)) {
        pr_err(FW_BUG "ioapic %d has no mapping iommu, "
               "interrupt remapping will be disabled\n",
               ioapic_id);
        return -1;
    }
}

这段代码则是检查IOAPIC是否都有对应的IOMMU

(3)

if (x2apic_present) {
        pr_info("Queued invalidation will be enabled to support x2apic and Intr-remapping.\n");

        eim = !dmar_x2apic_optout();
        if (!eim)
            printk(KERN_WARNING
                "Your BIOS is broken and requested that x2apic be disabled.\n"
                "This will slightly decrease performance.\n"
                "Use 'intremap=no_x2apic_optout' to override BIOS request.\n");
    }  

dmar_x2apic_optout函数实现如下:

static int __init dmar_x2apic_optout(void)
{
    struct acpi_table_dmar *dmar;
    dmar = (struct acpi_table_dmar *)dmar_tbl;
    if (!dmar || no_x2apic_optout)
        return 0;
    return dmar->flags & DMAR_X2APIC_OPT_OUT;
}

这个函数的返回值表示系统是否使用X2APIC功能(1表示不使用,0表示使用)。

参考资料:
"BIOS Considerations" in *Intel ® Virtualization Technology for Directed I/Oc

Linux kernel IOMMU代码分析笔记(6)——intel_irq_remapping_supported

看一下intel_irq_remapping_supported的代码:

static int __init intel_irq_remapping_supported(void)
{
    struct dmar_drhd_unit *drhd;
    struct intel_iommu *iommu;

    if (disable_irq_remap)
        return 0;
    if (irq_remap_broken) {
        printk(KERN_WARNING
            "This system BIOS has enabled interrupt remapping\n"
            "on a chipset that contains an erratum making that\n"
            "feature unstable.  To maintain system stability\n"
            "interrupt remapping is being disabled.  Please\n"
            "contact your BIOS vendor for an update\n");
        add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_STILL_OK);
        disable_irq_remap = 1;
        return 0;
    }

    if (!dmar_ir_support())
        return 0;

    for_each_iommu(iommu, drhd)
        if (!ecap_ir_support(iommu->ecap))
            return 0;

    return 1;
}

disable_irq_remapirq_remap_broken定义在irq_remapping.c中,用来判断是否支持IRQ remapping这个功能。

dmar_ir_support定义在dmar.c中:

/*
 * Check interrupt remapping support in DMAR table description.
 */
int __init dmar_ir_support(void)
{
    struct acpi_table_dmar *dmar;
    dmar = (struct acpi_table_dmar *)dmar_tbl;
    if (!dmar)
        return 0;
    return dmar->flags & 0x1;
}

flags的第0位标示平台是否支持IRQ remapping这个功能。

    for_each_iommu(iommu, drhd)
        if (!ecap_ir_support(iommu->ecap))
            return 0;

这段代码检查DMA Remapping Hardware Unit的所代表的IOMMU单元是否支持IRQ remapping这个功能。

如果检查都通过了,返回1,表示支持IRQ remapping这个功能。

参考资料:
"BIOS Considerations" in *Intel ® Virtualization Technology for Directed I/O

Git rebase简介

假设当前git repository的情况是这样的:

5-3

git rebase <new-base>命令可以把当前branchcommit ID移到<new-base> branch的后面。执行下面两条命令:

git checkout about
git rebase master

git repository变为:

5-4

 

可以看到,同“3-way merge”相比(参考:Git branch简介),不用产生一个新的commit ID

git rebase -i <new-base>是交互式地进行rebase操作:可以对每个commit进行操作。举例如下:

git rebase -i master

执行完以后,会打开一个文本编辑器,列出所有需要操作的commit。修改如下:

pick 5cf316e Add empty page in about section
squash 964e013 Add contents to about page
pick 89db9ab Add HTML page for personal bio
squash 2bda8e5 Add empty HTML page for Mary's bio
pick 915466f Add link to about section in home page

前两个commit会合并成一个commit,接下来两个commit会合并成一个commit,最后一个commit保持不变。但是新生成的3commit都会有新的commit ID。这表明不仅仅是合并commit这么简单,而是git repositoryhistory完全被改写了。如下图所示:

5-5

 

另外,git rebase -i <new-base>还可以改写某个snapshot。举例如下:

git rebase -i master

然后edit中间的commit

pick 58dec2a Create the about page
edit 6ac8a9f Begin creating bio pages
pick 51c958c Add link to about section in home page

git处理到第2commit时,会停下来做一下“amending”:

5-6

处理完后,运行下面命令,提交commit

git add about/mary.html
git status
git commit --amend

--amend告诉git使用staged snapshot替代存在的commit而不是新创建一个commit
接下来继续rebase

git rebase --continue

如果rebase操作中间想放弃这次操作,可以使用“git rebase --abort”操作。

 

Lua笔记(13)——module简介

Lua用户的观点来看,module就是一段可以通过require加载的代码(CLua),并且生成和返回一个tableLua中所有的标准库都是module

通过require命令加载module的步骤:
(1)查看module是否已经在package.loaded table中了,如果已经在了,就返回table中的值;否则转向步骤(2);
(2)查找以module命名的Lua文件。找到就使用loadfile加载,会得到一个称之为“loader”的函数。否则转向步骤(3);
(3)查找以module命名的C库文件。找到就使用package.loadlib加载,寻找luaopen_modname函数,也就是“loader”的函数。

有了loader以后,require会调用loader函数,并传入模块名和得到loader的文件名作为参数(很多module会忽略参数)。loader的返回值会被存入package.loaded table中,如果没有返回值,require表现会像loader返回了true,这样可以防止多次加载同一module

有时,为了避免module名冲突,需要重命名module(比如,加载同一module的不同版本)。Lua module没有把名字固定在内部,所以简单地重命名Lua文件就好了。但是对binary文件,由于无法改luaopen_*函数,所以需要一个trick:当module名有“-”时,require期待的open函数是使用”-“以后的名字。举个例子,如果一个module名字是v1-mod,则require期待的open函数就是luaopen_mod。所以只要把module前面加上版本和”-“以进行区分就可以了。

require查找modulepath是一系列用“;”分开的template,例如:

 /usr/local/share/lua/5.3/?.lua;/usr/local/share/lua/5.3/?/init.lua;/usr/local/lib/lua/5.3/?.lua;/usr/local/lib/lua/5.3/?/init.lua;./?.lua;./?/init.lua

对每个templaterequiremodule名字代替?,看是否存在这个文件。找到就返回,没有就尝试下一个template

require查找Lua文件的pathpackage.path变量里的值。这个值是这样得到的:
1)首先看是否定义环境变量LUA_PATH_5_2(取决于具体的Lua版本,这里只是个例子),如果有,则把这个值赋给package.path,没有转向2);
2)如果定义了环境变量LUA_PATH,则把这个值赋给package.path,没有转向3);
3)使用预定义的default path的值赋给package.path)。
需要注意的是,当使用环境变量定义的值时,如果有“;;”,则要把它替换成default path。举例来说,如果LUA_PATH_5_2的值是“mydir/?.lua;;”,则最后得到package.path应该是mydir/?.lua后面跟上default path
查找C库是package.cpath变量里的值。类似地,也有LUA_CPATH_5_2(同样取决于具体的`Lua`版本)和LUA_CPATH