dev_queue_xmit 导致死机。
小弟最近在研究零拷贝发送。所以需要直接调用dev_queue_xmit 进行发送。 可是之前调用dev_queue_xmit 都能正常发送,今天突然出现死机。这里贴出代码,希望哪位大大能够帮忙看一下。
- data = kmap(fifo_item->page);
- fifo_item->kernel_vir_addr = (unsigned long)data;
- data += offset;
- block_header = (struct UBM_block_header_t*)data;
- size = block_header->end - block_header->head;
- /* build skb head*/
- skb_orig = kmem_cache_alloc(skbuff_head_cache, GFP_KERNEL & ~__GFP_DMA);
- if (assemble_skb(skb_orig,size,data+block_header->head)<0){
- res = -1;
- kmem_cache_free(skbuff_head_cache, skb_orig);
- goto out;
- }
- skb = skb_clone(skb_orig, GFP_ATOMIC & ~__GFP_DMA);
- if (NULL == skb){
- res = -1;
- kmem_cache_free(skbuff_head_cache, skb_orig);
- goto out;
- }
- /*init send infomation in UBM fifo item */
- fifo_item->send_info.skb_orig = skb_orig;
- fifo_item->send_info.sleeping_task = current;
- fifo_item->send_info.status = status;
- /*build skb body*/
- skb->dev = dev;
- skb->pkt_type = PACKET_OTHERHOST;//PACKET_OUTGOING;
- skb->protocol = __constant_htons(ETH_P_IP);
- skb->ip_summed = CHECKSUM_NONE;
- skb->destructor = UBM_skb_destroy;
- skb->priority = 0;
- skb->next = NULL;
- skb_reserve (skb,size);
- skb_push(skb, block_header->tail - block_header->data);
- skb_push (skb, block_header->data - block_header->transport_header);
- skb_reset_transport_header(skb);
- if (sockfd < 0){
- skb_push (skb, block_header->transport_header - block_header->network_header);
- skb_reset_network_header(skb);
- iph = ip_hdr(skb);
- ip_send_check(iph);
- skb_push(skb, block_header->network_header - block_header->mac_header);
- skb_reset_mac_header(skb);
- res = dev_queue_xmit(skb);
- if (res != NETDEV_TX_OK)
- MSG_DEBUG("Failed to send");
- else
- MSG_DEBUG("Send out");
- }
- else{
- inet->inet_daddr = daddr;
- ubm_sock->sk->sk_protocol = ULP_PROTOCOL_NUMBER;
- skb->sk = ubm_sock->sk;
- res = ip_queue_xmit(skb);
- if (res != NETDEV_TX_OK)
- MSG_DEBUG("Failed to send, res : %d\n",res);
- else{
- MSG_DEBUG("Send out %lu, res : %d\n", send_counter++,res);
- }
- }
复制代码blockhead 是一个用户空间结构。在发送页面当中。他的head,tail,data,end 是数据相对于数据头部的偏移量。在调试的时候,输出信息正确。CLONE SKB 的原因是我的数据区处于用户空间。为了防止内核释放掉SKB的数据区,所以克隆。然后当实际释放的时候,克隆的SKB会KUNMAP掉数据区,并且释放原SKB的头部。assemble_skb 负责组合SKB 头 与数据区,并且初始化skb_shared_info信息。 dev 是在网络设备启动时初始话的,在之前有检测是否为空。 奇怪的是,如果我通过提交SOCK信息,并且调用ip_queue_xmit,那么就一切正常。但是调用dev_queue_xmit就死机。另外就是,我通过实验发现当我调用ip_queue_xmit的时候,如果我发包速度非常快,那么前N个包会丢失,但返回信息是正常的。有哪位大大知道这是怎么回事。谢谢了。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
我自己顶一下,希望有哪位大大能够帮忙看一下,提一些建议。
想问一下,这段代码是工作于哪个位置的?
注册在什么 hook 下,还是直接嵌在内核中什么位置?
回复 3# platinum
没有注册在HOOK函数下,是自己签到模块里,由用户直接呼叫的。 另外问题已经解决了,原因是在用户端,我在极短的时间内发送同一个数据包。那么当DMA 这个数据包到网卡的同时,我另外一个CPU在对这个数据包的IP头进行CHECKSUM, 这样就造成死机了。
哦,是不是 lock 没处理好,产生竞态导致的死机?
恩,差不多一个原理,但不完全是LOCK的原因。加锁只能保证内核区安全,但是用户仍有可能更改正在DMA的内存。我后来用MPROTECT来解决这个问题。然后加上信号处理函数。
而且考虑到快速转发的原因,我接受的数据同时也是使用用户内存。这个时候由于接受过程发生在硬中断内,我是无法加锁的。如果我使用底半操作,那会影响响应时间。所以我最后还是设置了一个标志位,同时在即将驱动即将DMA前加上MPROTECT保护。在影中断处理完返还用户时,去除保护,并重写标志位。这样寄希望于用户按照接口来操作,检查标志位。同时即使出现非法操作,也能通过信号处理机制解决。
bl851031 兄研究的好深啊,佩服佩服!!!:)
零拷贝技术仅适用于那些需要在 userspace 处理数据包的情况?
如果所有程序都是直接在内核处理的,是不是就无所谓是不是零拷贝了?
不知道我对零拷贝的理解是否有问题,还请 bl851031 兄多多指点!
回复 8# platinum
我也是个菜鸟,只是毕业设计是这个东东,不得不硬着头皮上。 关于你的问题,原则上是这样的。但是也要看你的模块在内核的哪一个层。比如如果你的模块是紧贴着VFS层,那么你就要考虑数据包分片(发送)或者线性化(接收)时可能发生的拷贝。不过我想这个可以通过你注册新的协议族来解决。另外零拷贝我体会还有两个好处。一个是内核内存实在是小了点,只有892MB,那么对于大量需要做LOG操作的数据包你就可能会丢包。当然你可以在内核中非配HIGH_MEM 但那样和用户就没区别了。其次是,驱动在每次接收到一个PACKET后,会重新非配一段内存等待接受第二轮的包。这个时间,如果用零拷贝可以提前到INIT态里面,即在INIT时就装配好SKB,然后当DRIVER需要的时候就直接提供,而避免了重新非配。
因为没研究过驱动代码,不知道是不是利用 slab 或 slub 分配的,如果是,他本身效率也是很高的
我感觉这个意义还是很大的,无论处理数据是在 kernel space 还是 user space,这样都可以简化处理路径
目前针对这个有具体实现吗?可否与大家分享一下?