网络子系统8_netpoll机制

来源:互联网 发布:python快速编程入门 编辑:程序博客网 时间:2024/05/21 17:50
//1.netpoll的作用://用于让内核在网络和I/O子系统尚不能完整可用时,依然能发送和接收数据包,主要用于网络控制台和远程。//2.netpoll机制需要驱动程序的支持://使外部通过软件方式调用驱动程序的中断处理程序。//大部分poll_controller定义如下://void this_controller(struct net_device *dev)//{//disable_dev_interrupt(dev);//call_interrupt_handler(dev->irq, dev);//enable_device_interrupt(dev);//}//3.netpoll运行条件://3.1 以太网介质//3.2 本机l2地址,或者l2广播地址//3.3 l3协议为ip协议//3.4 ip数据包没有分片,且有效//3.5 l4协议为udp协议,且有效//4.netpoll在协议栈中的切入点为netif_receive_skb//5.static atomic_t trapped;//trapped用于指示在netpoll_rx执行之后,netif_receive_skb是否丢弃该封包。//函数任务://1. 如果入口封包为arp封包,且开启了trapped模式,则处理入口arp,并返回。//2. 检查入口skb是否可以被处理。//3. 在关中断的情况下,遍历已经注册的netpoll控制块,向其传递skb。//调用路径:netif_receive_skb->netpoll_rx1.1 int netpoll_rx(struct sk_buff *skb){int proto, len, ulen;struct iphdr *iph;//ip头struct udphdr *uh;//udp头struct netpoll *np;//netpoll描述符struct list_head *p;unsigned long flags;//netpoll只适用以太网设备if (skb->dev->type != ARPHRD_ETHER)goto out;//trapped被设置,则由netpoll机制处理入口arpif (skb->protocol == __constant_htons(ETH_P_ARP) &&    atomic_read(&trapped)) {arp_reply(skb);return 1;}proto = ntohs(eth_hdr(skb)->h_proto);//l3协议需要是ip协议if (proto != ETH_P_IP)goto out;//应该为本机l2地址,或广播地址if (skb->pkt_type == PACKET_OTHERHOST)goto out;//skb没有被其他部分引用if (skb_shared(skb))goto out;iph = (struct iphdr *)skb->data;//skb->data - tail之间满足20字节的ip头if (!pskb_may_pull(skb, sizeof(struct iphdr)))goto out;//skb->data - tail之间满足完整的ip头if (iph->ihl < 5 || iph->version != 4)goto out;if (!pskb_may_pull(skb, iph->ihl*4))goto out;//检查ip报头校验和if (ip_fast_csum((u8 *)iph, iph->ihl) != 0)goto out;//检查ip数据包是否完整len = ntohs(iph->tot_len);if (skb->len < len || len < iph->ihl*4)goto out;//l4协议需要时udp协议if (iph->protocol != IPPROTO_UDP)goto out;//跨越ip报头以及ip选项len -= iph->ihl*4;//udp头部uh = (struct udphdr *)(((char *)iph) + iph->ihl*4);ulen = ntohs(uh->len);//udp报头中指定长度与实际长度不等if (ulen != len)goto out;//计算udp校验和if (checksum_udp(skb, uh, ulen, iph->saddr, iph->daddr) < 0)goto out;//关中断的情况下,向控制块传递skbspin_lock_irqsave(&rx_list_lock, flags);list_for_each(p, &rx_list) {//遍历已经注册的netpoll控制块np = list_entry(p, struct netpoll, rx_list);//比较匹配条件if (np->dev && np->dev != skb->dev)continue;if (np->local_ip && np->local_ip != ntohl(iph->daddr))continue;if (np->remote_ip && np->remote_ip != ntohl(iph->saddr))continue;if (np->local_port && np->local_port != ntohs(uh->dest))continue;spin_unlock_irqrestore(&rx_list_lock, flags);//调用控制块的回调函数if (np->rx_hook)np->rx_hook(np, ntohs(uh->source),    (char *)(uh+1),    ulen - sizeof(struct udphdr));return 1;}spin_unlock_irqrestore(&rx_list_lock, flags);out:return atomic_read(&trapped);//如果trapped非零,则netif_receive_skb会在netpoll_rx返回后,直接释放skb,跳过后续的执行。}//netpoll使用专用的skb缓存,对入口arp响应。//在缓存链表中,获取一个空闲的skb//函数主要任务://1.为skb缓存链表补充skb//2.如果缓存链表有空闲skb,则更新缓存的数量,返回skb//3.如果无法获取空闲的skb//3.1 调用netpoll_poll,加快网卡设备的数据接收,希望释放空闲skb//4.重复3,直到有空闲skb//调用路径:arp_reply->find_skb2.1 static struct sk_buff * find_skb(struct netpoll *np, int len, int reserve){int once = 1, count = 0;unsigned long flags;struct sk_buff *skb = NULL;zap_completion_queue();repeat://skb缓存不足if (nr_skbs < MAX_SKBS)refill_skbs();//分配skbskb = alloc_skb(len, GFP_ATOMIC);//分配skb失败if (!skb) {//关中断spin_lock_irqsave(&skb_list_lock, flags);//从skb缓存链表取一个skbskb = skbs;//如果获取skb缓存成功if (skb)skbs = skb->next;//更新skb缓存链表skb->next = NULL;nr_skbs--;//递减计数器spin_unlock_irqrestore(&skb_list_lock, flags);}//如果获取skb依然失败if(!skb) {//递增失败次数count++;//失败次数已经达到最大if (once && (count == 1000000)) {printk("out of netpoll skbs!\n");once = 0;}//加快网卡设备上的数据接收,以此来释放更多的空闲skbnetpoll_poll(np);goto repeat;}//设置skb使用者的个数atomic_set(&skb->users, 1);//预留skb头空间skb_reserve(skb, reserve);return skb;}//补充空闲skb后备链表//调用路径arp_reply->find_skb->refill_skbs2.3 static void refill_skbs(void){struct sk_buff *skb;unsigned long flags;//关中断,获取skb_list_lockspin_lock_irqsave(&skb_list_lock, flags);//当前可用skb的个数小于最大的skb数while (nr_skbs < MAX_SKBS) {//分配skbskb = alloc_skb(MAX_SKB_SIZE, GFP_ATOMIC);if (!skb)break;//将skb添加到链表skb->next = skbs;skbs = skb;nr_skbs++;//递增skb个数计数器}spin_unlock_irqrestore(&skb_list_lock, flags);}//加快dev上的数据接收//调用路径arp_reply->find_skb->netpoll_poll2.4 void netpoll_poll(struct netpoll *np){//netpoll没有指定设备,或者设备已经停止,或者设备没有提供poll控制器,则返回if(!np->dev || !netif_running(np->dev) || !np->dev->poll_controller)return;//软件方式调用设备中断处理例程np->dev->poll_controller(np->dev);//调度设备napi,完成数据接收if (np->dev->poll)poll_napi(np);zap_completion_queue();}//poll_napi调用驱动程序的poll函数,模拟net_rx_action//函数主要任务://1.获取该cpu的softnet_data//2.通过softnet_data->poll_list获取有入口数据的dev//3.通过dev->poll接收数据//调用路径arp_reply->find_skb->netpoll_poll->poll_napi2.5 static void poll_napi(struct netpoll *np){int budget = 16;unsigned long flags;struct softnet_data *queue;//关中断,获取锁spin_lock_irqsave(&netpoll_poll_lock, flags);queue = &__get_cpu_var(softnet_data);//获取per-cpu变量if (test_bit(__LINK_STATE_RX_SCHED, &np->dev->state) &&//当前设备被调度,说明有数据等待接收    !list_empty(&queue->poll_list)) {//本cpu的接收队列上有等待poll的设备np->dev->netpoll_rx |= NETPOLL_RX_DROP;//atomic_inc(&trapped);//递增trapped,使驱动程序的poll->netif_receive_skb->netpoll_rx之后,netif_receive_skb直接丢弃skb。np->dev->poll(np->dev, &budget);//驱动程序提供的poll函数atomic_dec(&trapped);np->dev->netpoll_rx &= ~NETPOLL_RX_DROP;}spin_unlock_irqrestore(&netpoll_poll_lock, flags);}

原创粉丝点击