栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

记一次Linux socket can驱动开发中的问题解决历程

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

记一次Linux socket can驱动开发中的问题解决历程

背景:

Philip Sja1000芯片,因为原厂硬件的限制,每次只能写入一个帧,在一个加拿大飞机制造商使用产品时,发现并不能满足他们的超高要求,他们要求相邻的两帧直接的间隔在15μs以内,而当前产品相邻两帧的间隔是75μs,因此不能达到客户的要求,于是和硬件研发一起讨论,得出了如下的方案。

1.在硬件FPGA里面增加FIFO,大小比如4096字节,这样,通过驱动(软件)写FIFO,进而达到缩短相邻两帧的时间间隔到15μs以内。

在开发过程中遇到诸多linux kernel问题,打印出一堆linux内核的log,当时很难解决,不过慢慢都找到原因,解决了。

问题1:内核打印如下日志

can_put_echo_skb: BUG! echo_skb 0 is occupied!

刚开始看到这个有点懵逼,无从下手,但看样子是内核打印的,所以就到内核中搜索can_put_echo_skb函数的源码,列出来如下:

int can_put_echo_skb(struct sk_buff *skb, struct net_device *dev,
		     unsigned int idx, unsigned int frame_len)
{
	struct can_priv *priv = netdev_priv(dev);

	BUG_ON(idx >= priv->echo_skb_max);

	
	if (!(dev->flags & IFF_ECHO) ||
	    (skb->protocol != htons(ETH_P_CAN) &&
	     skb->protocol != htons(ETH_P_CANFD))) {
		kfree_skb(skb);
		return 0;
	}

	if (!priv->echo_skb[idx]) {
		skb = can_create_echo_skb(skb);
		if (!skb)
			return -ENOMEM;

		
		skb->ip_summed = CHECKSUM_UNNECESSARY;
		skb->dev = dev;

		
		can_skb_prv(skb)->frame_len = frame_len;

		skb_tx_timestamp(skb);

		
		priv->echo_skb[idx] = skb;
	} else {
		
		netdev_err(dev, "%s: BUG! echo_skb %d is occupied!n", __func__, idx);
		kfree_skb(skb);
		return -EBUSY;
	}

	return 0;
}
EXPORT_SYMBOL_GPL(can_put_echo_skb);

可以看到,日志是在else分支打出来的,进而判断!priv->echo_skb[idx]为假,所以

priv->echo_skb[idx]为真,说明该指针已经有值了,被占用了;但是尚不清楚再哪里给这个指针赋值的,最后阅读内核源码,得知是在该函数else分支的上一行中赋值的,如下图:

所以得找到该指针在哪里释放或者置空,最终阅读内核源码,在can_get_echo_skb内核函数的__can_get_echo_skb函数中,如下:

 

 

接着继续追踪,最后发现是因为这次写FIFO后的调用逻辑层次改变了,原来是往设备硬件寄存器写,每次只写一帧(写之前netif_stop_queue停止上次继续写入),然后can_put_echo_skb,将内容放到设备堆栈等待后续处理,等到硬件的Tx buffered released的时候,调用can_get_echo_skb 通知内核去开始接收这一帧的内容;最后通过netif_wake_queue唤醒上层继续写入,才开始传输下一帧;而新的驱动写FIFO时 每次只进行can_put_echo_skb,没有紧接着can_get_echo_skb ,所以打印了“can_put_echo_skb: BUG! echo_skb 0 is occupied!”错误日志!can_put_echo_skb和can_get_echo_skb在写FIFO的函数中成对调用时就不再出现该问题了,该问题得到解决。

问题2:内核打印了一堆莫名的堆栈信息,但是没有奔溃,依然收发

 

 该处这些内核信息报错的排查了好久,本来打算正面硬钢该问题,使用KGDB进行源码级调试,但是对内核进行了编译后,发现内核编译过了,但在双机调试时,无法串口通信,这个离能源码级调试还有很长的路,时间有限就放弃了;

然后想到既然linux内核是开源的,那就再函数上下文看呗,把内核中的代码都粘贴到我的驱动中,然后打日志看,最后发现了问题:

can_put_echo_skb -->can_create_echo_skb-->__kfree_skb-->skb_release_all

-->skb_release_head_state

当时能够确定就是这一行打印出来的,但是没裂解这个怎么会导致异常,请教老大后,一语惊醒:

这个是不能在中断中调度长函数、不能等待等,所以内核会检测,检测到了调用函数,然后就打印了那个日志,并且打出了一堆内核堆栈调用信息。因为我在FIFO保存不下一帧的时候暂停了发送,等到TX中断到来时候,又再次调用了一次发送函数导致的,恍然大悟,茅塞顿开。

于是采用schedule_work的方式,上面的日志倒是不报了,但是又奔溃了,追踪起来又很麻烦~~~

后来和领导又讨论,可以不这样,在FIFO Almost Full的时候,暂停发送,在FIFO Almost Empty的时候在恢复发送,这样就和原来的逻辑完全一样了就不会有问题了,然后立马开干,修改代码后果然好了!!好激动~~~

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/845472.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号