Linux内核堆栈分配方式导致的panic分析

在移植NFC驱动时遇到系统panic的问题,查看堆栈信息,发现是i2c传输导致。

可以看出,在i2c read时出现DMA映射异常,这份驱动已经在其他平台使用过,为什么在新平台i2c会出现异常了?

首先看pn544_dev_read实现, 定义局部数组char tmp [512],传递给i2c_master_read函数,感觉很多驱动都是这么写的,也从没有

出现过错误.

static ssize_t pn544_dev_read(struct file *filp, char __user *buf,
        size_t count, loff_t *offset)
{
    struct pn544_dev *pn544_dev = filp->private_data;
    char tmp[MAX_BUFFER_SIZE];
    int ret
    /* Read data */
    ret = i2c_master_recv(pn544_dev->client, pn544_dev->client, count);
    if (copy_to_user(buf, tmp, ret)) {
        pr_warning("%s : failed to copy to user space\n", __func__);
        return -EFAULT;
    }
    return ret;
}

既然是报DMA传输错误,而且显示GPD页表项为空

[ffffffc02fa33b40] *pgd=0000000000000000, *pud=0000000000000000

我们对DMA的通用理解是DMA内存必须要物理上连续, 那么tmp内存是在栈上,难道会不连续吗?

进程的内核堆栈建立过程:

fork->copy_process->alloc_thread_stack_node:

可以看到,如果定义了CONFIG_VMAP_STACK,那么线程内核堆栈采用vmalloc分配,否则直接分配连续的物理页面.

static unsigned long *alloc_thread_stack_node(struct task_struct *tsk, int node)
{
#ifdef CONFIG_VMAP_STACK
    void *stack;
    
    stack = __vmalloc_node_range(THREAD_SIZE, THREAD_ALIGN,
                     VMALLOC_START, VMALLOC_END,
                     THREADINFO_GFP,
                     PAGE_KERNEL,
                     0, node, __builtin_return_address(0));
    if (stack)
        tsk->stack_vm_area = find_vm_area(stack);
    return stack;
#else
    struct page *page = alloc_pages_node(node, THREADINFO_GFP,
                         THREAD_SIZE_ORDER);

    return page ? page_address(page) : NULL;
#endif
}

通过查看defconfig文件,发现还真定义了CONFIG_VMAP_STACK,所以在函数定义局部的tmp数组,是通过vmalloc而来,

tmp[512]虽然虚拟地址连续,但是这512Byte内存,可能分部在两个不连续的物理page里面。然后进行DMA操作,就有可能错误.

解决办法: 采用kmalloc分配tmp的内存,问题得到解决.

猜你喜欢

转载自blog.csdn.net/bin_linux96/article/details/84982278