网卡驱动收包

一、NAPI方式处理数据包接收(中断与轮询配合,由硬件中断触发接收处理,之后关闭网卡硬中断,当处理完成后,再将网卡硬中断开启,在高负荷工作情况下,可以减少因频繁切换中断上下文造成的CPU开销)
1、e100网卡驱动接收中断触发
e100_intr
//e100网卡驱动中,在如下流程中设置中断回调
//netdev->open   (当应用层执行ifup时,调用了设备回调netdev->open)
//e100_open
//    e100_up
//        request_irq(nic->pdev->irq, e100_intr, IRQF_SHARED,...)
//		 e100_enable_irq(nic)

  u8 stat_ack = readb(&nic->csr->scb.stat_ack)
  if(stat_ack == stat_ack_not_ours ||stat_ack == stat_ack_not_present)
  return IRQ_NONE;

  writeb(stat_ack, &nic->csr->scb.stat_ack)

  if(stat_ack & stat_ack_rnr)
  nic->ru_running = RU_SUSPENDED;

  if(likely(netif_rx_schedule_prep(netdev)))
  //如果设置为打开状态(__LINK_STATE_START)并且先前还未进行接收调度
  //(__LINK_STATE_RX_SCHED),则条件满足
  e100_disable_irq(nic);	//关闭硬件中断
  __netif_rx_schedule(netdev)
  //将当前设备加入到每CPU的轮询设备列表中
  //分配当前设备的接收报文配额
  //触发软中断NET_RX_SOFTIRQ
  list_add_tail(&dev->poll_list, &__get_cpu_var(softnet_data).poll_list);
  dev->quota = dev->weight;
  __raise_softirq_irqoff(NET_RX_SOFTIRQ);
  
2、软中断NET_RX_SOFTIRQ中断函数,之前在net_dev_init中初始化
net_rx_action
  queue = &__get_cpu_var(softnet_data)
  //获取每CPU软中断处理队列
  budget = netdev_budget;
  //获取总的执行配额

  while (!list_empty(&queue->poll_list))
  //遍历轮询设备列表中每个需要接收的设备
  if (budget <= 0 || jiffies - start_time > 1)
  //当总的收包配额使用完,或者已经执行了1秒钟则暂时退出轮询处理
  goto softnet_break;

  dev = list_entry(queue->poll_list.next,....)
  //从轮询设备列表中取出第一个待处理的设备
  
  if (dev->quota <= 0 || dev->poll(dev, &budget))
  //如果在处理的设备自己的收包配额使用完,还有未接收的数据,则当前设备
  //轮询列表首部移动轮询列表尾部,同时更新此设备的接收配额。
  list_move_tail(&dev->poll_list, &queue->poll_list);
  dev->quota = dev->weight;
  Else
  dev_put(dev)		//释放该设备

  ......
  
3、dev->poll,e100驱动在PCI总线探测回调中设置该函数回调为e100_poll
e100_poll
  work_to_do = min(netdev->quota, *budget)
  //budget是调用dev->poll时传入的参数,budget是设备核心模块总的一次轮询配额,这
  //里用当前设备的收包配额和总的收包配额进行对比,取出最小接收包配额值。
  
  e100_rx_clean(nic, &work_done, work_to_do);
  //这里从网卡中获取数据包,并调用netif_receive_skb函数进行上层协议处理,这里暂
  //时不进行分析。
  
  if((!tx_cleaned && (work_done == 0)) || !netif_running(netdev))
  //如果发送和接收都处理完成,或者设备未开启,则将设备从轮询列表删除,清除正在
  //接收的调度标记,同时开启硬中断
  netif_rx_complete
  list_del(&dev->poll_list);
  clear_bit(__LINK_STATE_RX_SCHED, &dev->state);
  e100_enable_irq
  Return 0
  
  *budget -= work_done;			//将总的接收配额及设备接收配额递减,并返回还有待
  netdev->quota -= work_done;		//处理的数据(retur 1)
  Return 1

二、非NAPI方式处理数据包接收(始终靠网卡的硬中断来处理收包)
1、de600网卡驱动接收中断触发
de600_rx_intr
  netif_rx(skb)
  //接收中断触发后执行netif_rx进行接收包处理
  if (netpoll_rx(skb))
  return NET_RX_DROP;
  
  if (!skb->tstamp.off_sec)
  net_timestamp(skb);
  //skb->tstamp,更新收包的时间戳
  
  queue = &__get_cpu_var(softnet_data);
  //获取每CPU软中断处理队列
  
  if (queue->input_pkt_queue.qlen <= netdev_max_backlog)
  //如果接收包的队列没满则继续处理,否则丢弃。
  if (queue->input_pkt_queue.qlen)
  //如果队列中已经有值,表明软中断正在处理,直接将包放入队列尾。
  enqueue:
  __skb_queue_tail(&queue->input_pkt_queue, skb);
  return NET_RX_SUCCESS;
  
  netif_rx_schedule(&queue->backlog_dev);
  if (netif_rx_schedule_prep(dev))
  __netif_rx_schedule(dev)
  //接口为开启,先没有进行接收调度,则触发接收调度,将设备
  //加入到轮询设备列表中,同时触发接收软中断。注意这里的向
  //轮询列表加入的设备是虚拟的backlog_dev,同时接收软中断在
  //调用设备接收回调函数也是使用的这个虚拟设备的接收函数。
  goto enqueue;
  //如果队列是空的,则激活软中断,同时将包放入队列中。
  
  kfree_skb(skb)
  return NET_RX_DROP;
  //在上面判断队列已经满了,则新来的包丢弃处理。
  
2、接收软中断任务在上面已经描述过了,接收软中断任务主要从设备轮询列表调用对应设备的poll回调函数,非NAPI方式的驱动在上面使用的是虚拟设备backlog_dev,该设备的回调函数在net_dev_init初始化时已经设置为process_backlog

process_backlog
  quota = min(backlog_dev->quota, *budget)
  //从总的收包配额,与设备的收包配额中选取一个较小的值

for (;;)
  skb = __skb_dequeue(&queue->input_pkt_queue);
  //从接收队列中获取一个包,如果队列已空,则返回接收完成。
  if (!skb)
  goto job_done;

  dev = skb->dev;
  netif_receive_skb(skb);
  //调用上层接口进行包处理
  
  work++;
  if (work >= quota || jiffies - start_time > 1)
  //如果已经达到了收包限额,或者执行了1秒中,则退出循环
  break;

backlog_dev->quota -= work;
*budget -= work;
return -1;
//将设备限额和总收包限额递减,返回-1表明还有包需要处理

job_done:
backlog_dev->quota -= work;
*budget -= work;
list_del(&backlog_dev->poll_list);
//接收队列中已经没有包需要处理,则递减包限额后,从轮询列表中删除虚拟设备。

netif_poll_enable(backlog_dev);
//将虚拟设备状态去除__LINK_STATE_RX_SCHED,表明已经接收调度已经没有运行。

三、netif_receive_skb
if (skb->dev->poll && netpoll_rx(skb))
//如果netpoll处理,则这里直接丢弃包,不再向上层传递。
  return NET_RX_DROP;
  
if (!skb->tstamp.off_sec)
//更新时间戳
  net_timestamp(skb);
  
if (!skb->input_dev)
  skb->input_dev = skb->dev;

orig_dev = skb_bond(skb);
//如果有绑定功能,则获取绑定主设备,否则不支持绑定功能,则取当前获取包的设备

//初始化skb一些参数值
skb->h.raw = skb->nh.raw = skb->data;
skb->mac_len = skb->nh.raw - skb->mac.raw;

pt_prev = NULL;

list_for_each_entry_rcu(ptype, &ptype_all, list)
//将包传给所有嗅探器,这里ptype_all是指可以处理任何二层以上的包类型列表。通常应用
//层实现嗅探器创建的套接口类型为socket(AF_PACKET,SOCK_RAW,htons(ETH_P_ALL)),
//则系统就会调用dev_add_pack向ptype_all包类型列表中添加。这里嗅探器得到接收的所有
//报文,同样也会得到所有发送的报文。在dev_add_pack函数执行时,如果包类型为
//ETH_P_ALL,则递增全局变量netdev_nit,之后dev_hard_start_xmit发包函数中判断如果
//netdev_nit大于0,则会把发出的包放到当前嗅探器的套接收接收队列中。
  if (!ptype->dev || ptype->dev == skb->dev)
  if (pt_prev)
  deliver_skb(skb, pt_prev, orig_dev);
  pt_prev->func(skb, skb->dev, pt_prev, orig_dev);
  pt_prev = ptype;

if (handle_bridge(&skb, &pt_prev, &ret, orig_dev))
//如果二层桥已经处理,则返回
  goto out;
  
type = skb->protocol;
list_for_each_entry_rcu(ptype, &ptype_base[ntohs(type)&15], list)
//处理所有注册到基本包类型的包。(最后一个不做处理)
  if (ptype->type == type && (!ptype->dev || ptype->dev == skb->dev))
  if (pt_prev)
  ret = deliver_skb(skb, pt_prev, orig_dev);
  pt_prev->func(skb, skb->dev, pt_prev, orig_dev);
  pt_prev = ptype;
  
if (pt_prev)
  ret = pt_prev->func(skb, skb->dev, pt_prev, orig_dev);
  //处理注册到基本包类型的最后一个符合条件的包。
esle
  kfree_skb(skb)
  ret = NET_RX_DROP;
  //不识别的类型包,丢弃处理。


  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值