在移植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的内存,问题得到解决.