【问题标题】:commenting out a printk statement causes crash in a linux device driver test注释掉 printk 语句会导致 linux 设备驱动程序测试崩溃
【发布时间】:2021-08-02 07:09:22
【问题描述】:

我在一个简单的 linux 驱动程序测试 (arm64) 中看到了一个奇怪的案例。
用户程序调用设备驱动程序的 ioctl 并将 uint64_t 的数组 'arg' 作为参数传递。顺便说一句,arg[2] 包含一个指向应用程序中变量的指针。下面是代码sn-p。

    case SetRunParameters:
        copy_from_user(args, (void __user *)arg, 8*3);
        offs = args[2] % PAGE_SIZE;
        down_read(&current->mm->mmap_sem);
        res = get_user_pages( (unsigned long)args[2], 1, 1, &pages, NULL);
        if (res) {
            kv_page_addr = kmap(pages);
            kv_addr = ((unsigned long long int)(kv_page_addr)+offs);
            args[2] = page_to_phys(pages) + offset; // args[2] changed to physical
        }
        else {
            printk("get_user_pages failed!\n");
        }
        up_read(&current->mm->mmap_sem);
        *(vaddr + REG_IOCTL_ARG/4) = virt_to_phys(args);  // from axpu_regs.h
        printk("ldd:writing %x at %px\n",cmdx,vaddr + REG_IOCTL_CMD/4); // <== line 248. not ok w/o this printk line why?..
        *(vaddr + REG_IOCTL_CMD/4) = cmdx;  // this command is different from ioctl cmd!
        put_page(pages); //page_cache_release(page);
        break;
    case ...

我在上面的代码中标记了第 248 行。如果我在此处注释掉 printk,则会发生陷阱并且虚拟机崩溃(我在 qemu 虚拟机上执行此操作)。 cmdx 是根据应用程序的 ioctl 命令设置的整数值,vaddr 是设备的虚拟地址(从 ioremap 获取)。如果我保留 printk,它会按我的预期工作。什么情况下可以做到这一点? (缓存还是 tlb?)

【问题讨论】:

  • 您能否发布崩溃报告?如果它报告了什么
  • 因为虚拟机本身崩溃了,我在VM上看不到内核的报告。我只看到这个:./runit20a: line 10: 16669 Segmentation fault (core dumped) qemu-system-aarch64 -nographic -machine ab21q,gic-version=max,iommu=smmuv3 -m 1G -cpu max -smp 4 -netdev user,id=vnet,hostfwd=:127.0.0.1:0-:22 -device virtio-net-pci,netdev=vnet -drive file=ab21q-ubuntu-20.04-image.img,if=none,id=drive0, cache=writeback -device virtio-blk,drive=drive0,bootindex=0 -drive file=ubuntu-20.04-mini.iso,if=none,id=drive1,cache=writeback
  • -device virtio-blk,drive=drive1,bootindex=1 -drive file=flash0.img,format=raw,if=pflash -drive file=flash1.img,format=raw,if=快闪。但这是来自 qemu 崩溃。
  • 有时没有 printk 语句,它不会崩溃,但来自用户域的值不会改变(设备应该会改变值)。当它没问题时(使用 printk),该值已被设备(qemu 模型)更改。
  • 我猜printk 调用是一个障碍。您应该使用writel 来写入寄存器。

标签: linux-kernel linux-device-driver arm64


【解决方案1】:

通过简单的 C 结构(例如 *(vaddr + REG_IOCTL_ARG/4))访问内存映射寄存器是个坏主意。如果访问权限是volatile-qualified,您可能会在某些平台上侥幸逃脱,但它在某些平台上将无法可靠运行或根本无法运行。访问内存映射寄存器的正确方法是通过#include &lt;asm/io.h&gt;#include &lt;linux/io.h&gt; 声明的函数。这些将处理任何特定于架构的要求,以确保就 CPU 而言正确排序写入1

内存映射寄存器访问的函数在 Linux 内核文档Bus-Independent Device Accesses 下进行了描述。

这段代码:

        *(vaddr + REG_IOCTL_ARG/4) = virt_to_phys(args);
        *(vaddr + REG_IOCTL_CMD/4) = cmdx;

可以改写为:

        writel(virt_to_phys(args), vaddr + REG_IOCTL_ARG/4);
        writel(cmdx, vaddr + REG_IOCTL_CMD/4);

1 如果寄存器写入的顺序很重要,则特定总线类型(例如 PCI)的写入顺序可能需要额外的代码来在写入不同寄存器之间读取寄存器。这是因为写入是异步“发布”到 PCI 总线的,PCI 设备可能会乱序处理对不同寄存器的写入。在处理完所有先前的写入之前,设备不会处理中间寄存器读取,因此它可用于强制发布写入的顺序。

【讨论】:

  • 非常感谢您的清晰解释。将其更改为 writel 使其可以在有或没有 printk 的情况下工作。
猜你喜欢
  • 1970-01-01
  • 2021-03-22
  • 2012-06-16
  • 1970-01-01
  • 2011-09-05
  • 1970-01-01
相关资源
最近更新 更多