内核驱动踩坑记录
内核驱动踩坑记录
一:银河麒麟操作系统+飞腾处理器
此处省略一万字
二:用户空间访问问题
块设备驱动提供ioctl接口供用户直接向特定扇区读写数据,在ioctl系统调用参数中传递用户缓冲区的地址。ioctl内核处理函数中直接将请求打包成相应的命令,加入SSD请求队列等待执行。由于未实现中断功能,利用定时器定时轮询对应位置查看命令执行情况并在设备空闲时发送请求。此时调用copy_from_user函数失败。这是因为定时器回调函数是异步执行的,它类似于一种“软件中断”,而且是处于非进程的上下文中,所以回调函数必须遵守以下规则:
- 没有 current 指针、不允许访问用户空间。因为没有进程上下文,相关代码和被中断的进程没有任何联系。
- 不能执行休眠(或可能引起休眠的函数)和调度。
- 任何被访问的数据结构都应该针对并发访问进行保护,以防止竞争条件。
inux内核timer执行上下文,内核定时器的使用(好几个例子add_timer)
解决:
如果是写命令,先将数据写入申请的内核缓冲区,再将内核缓冲区的地址写入命令。如果是读命令,先将读到的数据写入内核缓冲区,再将数据写入用户缓冲区。
ioctl请求的一般执行过程为:①解析参数 ②申请内核缓冲区存放数据 ③ 将请求封装成命令,加入命令队列 ④等待命令执行完成,并执行必要的数据传输工作。
三:模块卸载出错
有时候编写代码时并不会检查函数返回值等信息,有时候资源并没有申请成功,而在.remove函数中却照常进行了资源释放,或者申请了资源却忘了在remove函数中进行释放,便会造成空指针 死机等一系列的后果。
例如在.probe函数中注册了定时器却忘了注销,注册块设备失败却照常注销块设备,get_device与put_device数目不一致
add_timer(&hps_dev->timer); //注册定时器
del_timer(&hps_dev->timer); //注销定时器
device_add_disk(hps_dev->dev, hps_dev->disk, hps_attr_groups); // 注册块设备
del_gendisk(hps_dev->disk); // 移除块设备
hps_dev->dev = get_device(&pdev->dev); // 增加设备计数
put_device(hps_dev->dev); // 减小设备计数
所以比较安全的方式就是增加出错处理代码或在资源释放时判断是否持有。
ret = request();
if(ret!=0){
// 出错处理
}
if(hold(p)) release p;
四:DMA缓冲区大小问题
dma_alloc_coherent函数申请超过4M便会报错,配置CMA使DMA能够申请更大的空间,这可能需要需要重新编译内核(如果高版本宿主机编译内核建议先通过deb安装文件切换至相近版本的内核,再通过源码编译内核)。
dev->data_buf = dma_alloc_coherent(dev->dev, MAX_DATA_SIZE, &dev->data_buf_dma_addr, GFP_KERNEL);
参考博客:
dma_alloc_coherent 申请内存用法和问题总结
在Linux内核模块中使用CMA内存分配
大致步骤如下:
cat /boot/config-$(uname -r) | grep DMA_CMA # 查看当前内核是否开启了CMA功能
如果不存在则需要重新编译内核,建议配置内核时顺带勾选kgdb选项
CONFIG_DMA_CMA选项没在memuconfig找到,可直接通过vim .config修改,也可将其他的CMA选项打开
在编译内核是会选择CMA空间大小,后面也可以在grub配置文件中修改相应大小。
cat /proc/meminfo # 查看是否修改成功
CmaTotal: 1048576 kB
CmaFree: 1048576 kB
dmesg | grep cma # 显示内核启动时cma相关信息
配置成功后就能申请更大的连续物理内存
五:linux内存页大小问题
在用户程序中使用mmap系统调用,传入的映射长度为32k,可是内核mmap处理函数中vm_area的长度却显示为64k。
void* mmap(void* start,size_t length,int prot,int flags,int fd,off_t offset);
int munmap(void* start,size_t length);
解决:
getconf PAGE_SIZE # 查看当前页大小
65536
mmap 必须以PAGE_SIZE为单位进行映射,而内存也只能以页为单位进行映射,若要映射非PAGE_SIZE整数倍的地址范围,要先进行内存对齐,强行以PAGE_SIZE的倍数大小进行映射。由于传入长度小于64k,进行了一次向上取整。故重新编译内核,在menuconfig中修改page size。
六:网络不稳定,同属于一个局域网却不能ping通
通过打印内核启动时的信息可以发现网卡在一直自动协商连接速度,通过ethtool工具关闭自动协商功能即可。
# 将网卡eth2速度固定1000M,双工,且关闭自动协商
sudo ethtool -s eth2 speed 1000 duplex full autoneg off
Linux 关闭链路自动协商功能