Loading include/linux/netpoll.h +8 −3 Original line number Original line Diff line number Diff line Loading @@ -21,15 +21,20 @@ struct netpoll { __be32 local_ip, remote_ip; __be32 local_ip, remote_ip; u16 local_port, remote_port; u16 local_port, remote_port; u8 remote_mac[ETH_ALEN]; u8 remote_mac[ETH_ALEN]; struct list_head rx; /* rx_np list element */ }; }; struct netpoll_info { struct netpoll_info { atomic_t refcnt; atomic_t refcnt; int rx_flags; int rx_flags; spinlock_t rx_lock; spinlock_t rx_lock; struct netpoll *rx_np; /* netpoll that registered an rx_hook */ struct list_head rx_np; /* netpolls that registered an rx_hook */ struct sk_buff_head arp_tx; /* list of arp requests to reply to */ struct sk_buff_head arp_tx; /* list of arp requests to reply to */ struct sk_buff_head txq; struct sk_buff_head txq; struct delayed_work tx_work; struct delayed_work tx_work; }; }; Loading @@ -51,7 +56,7 @@ static inline int netpoll_rx(struct sk_buff *skb) unsigned long flags; unsigned long flags; int ret = 0; int ret = 0; if (!npinfo || (!npinfo->rx_np && !npinfo->rx_flags)) if (!npinfo || (list_empty(&npinfo->rx_np) && !npinfo->rx_flags)) return 0; return 0; spin_lock_irqsave(&npinfo->rx_lock, flags); spin_lock_irqsave(&npinfo->rx_lock, flags); Loading @@ -67,7 +72,7 @@ static inline int netpoll_rx_on(struct sk_buff *skb) { { struct netpoll_info *npinfo = skb->dev->npinfo; struct netpoll_info *npinfo = skb->dev->npinfo; return npinfo && (npinfo->rx_np || npinfo->rx_flags); return npinfo && (!list_empty(&npinfo->rx_np) || npinfo->rx_flags); } } static inline int netpoll_receive_skb(struct sk_buff *skb) static inline int netpoll_receive_skb(struct sk_buff *skb) Loading net/core/netpoll.c +106 −63 Original line number Original line Diff line number Diff line Loading @@ -407,11 +407,24 @@ static void arp_reply(struct sk_buff *skb) __be32 sip, tip; __be32 sip, tip; unsigned char *sha; unsigned char *sha; struct sk_buff *send_skb; struct sk_buff *send_skb; struct netpoll *np = NULL; struct netpoll *np, *tmp; unsigned long flags; int hits = 0; if (list_empty(&npinfo->rx_np)) return; /* Before checking the packet, we do some early inspection whether this is interesting at all */ spin_lock_irqsave(&npinfo->rx_lock, flags); list_for_each_entry_safe(np, tmp, &npinfo->rx_np, rx) { if (np->dev == skb->dev) hits++; } spin_unlock_irqrestore(&npinfo->rx_lock, flags); if (npinfo->rx_np && npinfo->rx_np->dev == skb->dev) /* No netpoll struct is using this dev */ np = npinfo->rx_np; if (!hits) if (!np) return; return; /* No arp on this interface */ /* No arp on this interface */ Loading @@ -437,21 +450,26 @@ static void arp_reply(struct sk_buff *skb) arp_ptr += skb->dev->addr_len; arp_ptr += skb->dev->addr_len; memcpy(&sip, arp_ptr, 4); memcpy(&sip, arp_ptr, 4); arp_ptr += 4; arp_ptr += 4; /* if we actually cared about dst hw addr, it would get copied here */ /* If we actually cared about dst hw addr, it would get copied here */ arp_ptr += skb->dev->addr_len; arp_ptr += skb->dev->addr_len; memcpy(&tip, arp_ptr, 4); memcpy(&tip, arp_ptr, 4); /* Should we ignore arp? */ /* Should we ignore arp? */ if (tip != np->local_ip || if (ipv4_is_loopback(tip) || ipv4_is_multicast(tip)) ipv4_is_loopback(tip) || ipv4_is_multicast(tip)) return; return; size = arp_hdr_len(skb->dev); size = arp_hdr_len(skb->dev); spin_lock_irqsave(&npinfo->rx_lock, flags); list_for_each_entry_safe(np, tmp, &npinfo->rx_np, rx) { if (tip != np->local_ip) continue; send_skb = find_skb(np, size + LL_ALLOCATED_SPACE(np->dev), send_skb = find_skb(np, size + LL_ALLOCATED_SPACE(np->dev), LL_RESERVED_SPACE(np->dev)); LL_RESERVED_SPACE(np->dev)); if (!send_skb) if (!send_skb) return; continue; skb_reset_network_header(send_skb); skb_reset_network_header(send_skb); arp = (struct arphdr *) skb_put(send_skb, size); arp = (struct arphdr *) skb_put(send_skb, size); Loading @@ -463,14 +481,15 @@ static void arp_reply(struct sk_buff *skb) sha, np->dev->dev_addr, sha, np->dev->dev_addr, send_skb->len) < 0) { send_skb->len) < 0) { kfree_skb(send_skb); kfree_skb(send_skb); return; continue; } } /* /* * Fill out the arp protocol part. * Fill out the arp protocol part. * * * we only support ethernet device type, * we only support ethernet device type, * which (according to RFC 1390) should always equal 1 (Ethernet). * which (according to RFC 1390) should * always equal 1 (Ethernet). */ */ arp->ar_hrd = htons(np->dev->type); arp->ar_hrd = htons(np->dev->type); Loading @@ -489,25 +508,33 @@ static void arp_reply(struct sk_buff *skb) memcpy(arp_ptr, &sip, 4); memcpy(arp_ptr, &sip, 4); netpoll_send_skb(np, send_skb); netpoll_send_skb(np, send_skb); /* If there are several rx_hooks for the same address, we're fine by sending a single reply */ break; } spin_unlock_irqrestore(&npinfo->rx_lock, flags); } } int __netpoll_rx(struct sk_buff *skb) int __netpoll_rx(struct sk_buff *skb) { { int proto, len, ulen; int proto, len, ulen; int hits = 0; struct iphdr *iph; struct iphdr *iph; struct udphdr *uh; struct udphdr *uh; struct netpoll_info *npi = skb->dev->npinfo; struct netpoll_info *npinfo = skb->dev->npinfo; struct netpoll *np = npi->rx_np; struct netpoll *np, *tmp; if (!np) if (list_empty(&npinfo->rx_np)) goto out; goto out; if (skb->dev->type != ARPHRD_ETHER) if (skb->dev->type != ARPHRD_ETHER) goto out; goto out; /* check if netpoll clients need ARP */ /* check if netpoll clients need ARP */ if (skb->protocol == htons(ETH_P_ARP) && if (skb->protocol == htons(ETH_P_ARP) && atomic_read(&trapped)) { atomic_read(&trapped)) { skb_queue_tail(&npi->arp_tx, skb); skb_queue_tail(&npinfo->arp_tx, skb); return 1; return 1; } } Loading Loading @@ -551,16 +578,23 @@ int __netpoll_rx(struct sk_buff *skb) goto out; goto out; if (checksum_udp(skb, uh, ulen, iph->saddr, iph->daddr)) if (checksum_udp(skb, uh, ulen, iph->saddr, iph->daddr)) goto out; goto out; list_for_each_entry_safe(np, tmp, &npinfo->rx_np, rx) { if (np->local_ip && np->local_ip != iph->daddr) if (np->local_ip && np->local_ip != iph->daddr) goto out; continue; if (np->remote_ip && np->remote_ip != iph->saddr) if (np->remote_ip && np->remote_ip != iph->saddr) goto out; continue; if (np->local_port && np->local_port != ntohs(uh->dest)) if (np->local_port && np->local_port != ntohs(uh->dest)) goto out; continue; np->rx_hook(np, ntohs(uh->source), np->rx_hook(np, ntohs(uh->source), (char *)(uh+1), (char *)(uh+1), ulen - sizeof(struct udphdr)); ulen - sizeof(struct udphdr)); hits++; } if (!hits) goto out; kfree_skb(skb); kfree_skb(skb); return 1; return 1; Loading Loading @@ -684,6 +718,7 @@ int netpoll_setup(struct netpoll *np) struct net_device *ndev = NULL; struct net_device *ndev = NULL; struct in_device *in_dev; struct in_device *in_dev; struct netpoll_info *npinfo; struct netpoll_info *npinfo; struct netpoll *npe, *tmp; unsigned long flags; unsigned long flags; int err; int err; Loading @@ -704,7 +739,7 @@ int netpoll_setup(struct netpoll *np) } } npinfo->rx_flags = 0; npinfo->rx_flags = 0; npinfo->rx_np = NULL; INIT_LIST_HEAD(&npinfo->rx_np); spin_lock_init(&npinfo->rx_lock); spin_lock_init(&npinfo->rx_lock); skb_queue_head_init(&npinfo->arp_tx); skb_queue_head_init(&npinfo->arp_tx); Loading Loading @@ -785,7 +820,7 @@ int netpoll_setup(struct netpoll *np) if (np->rx_hook) { if (np->rx_hook) { spin_lock_irqsave(&npinfo->rx_lock, flags); spin_lock_irqsave(&npinfo->rx_lock, flags); npinfo->rx_flags |= NETPOLL_RX_ENABLED; npinfo->rx_flags |= NETPOLL_RX_ENABLED; npinfo->rx_np = np; list_add_tail(&np->rx, &npinfo->rx_np); spin_unlock_irqrestore(&npinfo->rx_lock, flags); spin_unlock_irqrestore(&npinfo->rx_lock, flags); } } Loading @@ -801,9 +836,16 @@ int netpoll_setup(struct netpoll *np) return 0; return 0; release: release: if (!ndev->npinfo) if (!ndev->npinfo) { spin_lock_irqsave(&npinfo->rx_lock, flags); list_for_each_entry_safe(npe, tmp, &npinfo->rx_np, rx) { npe->dev = NULL; } spin_unlock_irqrestore(&npinfo->rx_lock, flags); kfree(npinfo); kfree(npinfo); np->dev = NULL; } dev_put(ndev); dev_put(ndev); return err; return err; } } Loading @@ -823,9 +865,10 @@ void netpoll_cleanup(struct netpoll *np) if (np->dev) { if (np->dev) { npinfo = np->dev->npinfo; npinfo = np->dev->npinfo; if (npinfo) { if (npinfo) { if (npinfo->rx_np == np) { if (!list_empty(&npinfo->rx_np)) { spin_lock_irqsave(&npinfo->rx_lock, flags); spin_lock_irqsave(&npinfo->rx_lock, flags); npinfo->rx_np = NULL; list_del(&np->rx); if (list_empty(&npinfo->rx_np)) npinfo->rx_flags &= ~NETPOLL_RX_ENABLED; npinfo->rx_flags &= ~NETPOLL_RX_ENABLED; spin_unlock_irqrestore(&npinfo->rx_lock, flags); spin_unlock_irqrestore(&npinfo->rx_lock, flags); } } Loading Loading
include/linux/netpoll.h +8 −3 Original line number Original line Diff line number Diff line Loading @@ -21,15 +21,20 @@ struct netpoll { __be32 local_ip, remote_ip; __be32 local_ip, remote_ip; u16 local_port, remote_port; u16 local_port, remote_port; u8 remote_mac[ETH_ALEN]; u8 remote_mac[ETH_ALEN]; struct list_head rx; /* rx_np list element */ }; }; struct netpoll_info { struct netpoll_info { atomic_t refcnt; atomic_t refcnt; int rx_flags; int rx_flags; spinlock_t rx_lock; spinlock_t rx_lock; struct netpoll *rx_np; /* netpoll that registered an rx_hook */ struct list_head rx_np; /* netpolls that registered an rx_hook */ struct sk_buff_head arp_tx; /* list of arp requests to reply to */ struct sk_buff_head arp_tx; /* list of arp requests to reply to */ struct sk_buff_head txq; struct sk_buff_head txq; struct delayed_work tx_work; struct delayed_work tx_work; }; }; Loading @@ -51,7 +56,7 @@ static inline int netpoll_rx(struct sk_buff *skb) unsigned long flags; unsigned long flags; int ret = 0; int ret = 0; if (!npinfo || (!npinfo->rx_np && !npinfo->rx_flags)) if (!npinfo || (list_empty(&npinfo->rx_np) && !npinfo->rx_flags)) return 0; return 0; spin_lock_irqsave(&npinfo->rx_lock, flags); spin_lock_irqsave(&npinfo->rx_lock, flags); Loading @@ -67,7 +72,7 @@ static inline int netpoll_rx_on(struct sk_buff *skb) { { struct netpoll_info *npinfo = skb->dev->npinfo; struct netpoll_info *npinfo = skb->dev->npinfo; return npinfo && (npinfo->rx_np || npinfo->rx_flags); return npinfo && (!list_empty(&npinfo->rx_np) || npinfo->rx_flags); } } static inline int netpoll_receive_skb(struct sk_buff *skb) static inline int netpoll_receive_skb(struct sk_buff *skb) Loading
net/core/netpoll.c +106 −63 Original line number Original line Diff line number Diff line Loading @@ -407,11 +407,24 @@ static void arp_reply(struct sk_buff *skb) __be32 sip, tip; __be32 sip, tip; unsigned char *sha; unsigned char *sha; struct sk_buff *send_skb; struct sk_buff *send_skb; struct netpoll *np = NULL; struct netpoll *np, *tmp; unsigned long flags; int hits = 0; if (list_empty(&npinfo->rx_np)) return; /* Before checking the packet, we do some early inspection whether this is interesting at all */ spin_lock_irqsave(&npinfo->rx_lock, flags); list_for_each_entry_safe(np, tmp, &npinfo->rx_np, rx) { if (np->dev == skb->dev) hits++; } spin_unlock_irqrestore(&npinfo->rx_lock, flags); if (npinfo->rx_np && npinfo->rx_np->dev == skb->dev) /* No netpoll struct is using this dev */ np = npinfo->rx_np; if (!hits) if (!np) return; return; /* No arp on this interface */ /* No arp on this interface */ Loading @@ -437,21 +450,26 @@ static void arp_reply(struct sk_buff *skb) arp_ptr += skb->dev->addr_len; arp_ptr += skb->dev->addr_len; memcpy(&sip, arp_ptr, 4); memcpy(&sip, arp_ptr, 4); arp_ptr += 4; arp_ptr += 4; /* if we actually cared about dst hw addr, it would get copied here */ /* If we actually cared about dst hw addr, it would get copied here */ arp_ptr += skb->dev->addr_len; arp_ptr += skb->dev->addr_len; memcpy(&tip, arp_ptr, 4); memcpy(&tip, arp_ptr, 4); /* Should we ignore arp? */ /* Should we ignore arp? */ if (tip != np->local_ip || if (ipv4_is_loopback(tip) || ipv4_is_multicast(tip)) ipv4_is_loopback(tip) || ipv4_is_multicast(tip)) return; return; size = arp_hdr_len(skb->dev); size = arp_hdr_len(skb->dev); spin_lock_irqsave(&npinfo->rx_lock, flags); list_for_each_entry_safe(np, tmp, &npinfo->rx_np, rx) { if (tip != np->local_ip) continue; send_skb = find_skb(np, size + LL_ALLOCATED_SPACE(np->dev), send_skb = find_skb(np, size + LL_ALLOCATED_SPACE(np->dev), LL_RESERVED_SPACE(np->dev)); LL_RESERVED_SPACE(np->dev)); if (!send_skb) if (!send_skb) return; continue; skb_reset_network_header(send_skb); skb_reset_network_header(send_skb); arp = (struct arphdr *) skb_put(send_skb, size); arp = (struct arphdr *) skb_put(send_skb, size); Loading @@ -463,14 +481,15 @@ static void arp_reply(struct sk_buff *skb) sha, np->dev->dev_addr, sha, np->dev->dev_addr, send_skb->len) < 0) { send_skb->len) < 0) { kfree_skb(send_skb); kfree_skb(send_skb); return; continue; } } /* /* * Fill out the arp protocol part. * Fill out the arp protocol part. * * * we only support ethernet device type, * we only support ethernet device type, * which (according to RFC 1390) should always equal 1 (Ethernet). * which (according to RFC 1390) should * always equal 1 (Ethernet). */ */ arp->ar_hrd = htons(np->dev->type); arp->ar_hrd = htons(np->dev->type); Loading @@ -489,25 +508,33 @@ static void arp_reply(struct sk_buff *skb) memcpy(arp_ptr, &sip, 4); memcpy(arp_ptr, &sip, 4); netpoll_send_skb(np, send_skb); netpoll_send_skb(np, send_skb); /* If there are several rx_hooks for the same address, we're fine by sending a single reply */ break; } spin_unlock_irqrestore(&npinfo->rx_lock, flags); } } int __netpoll_rx(struct sk_buff *skb) int __netpoll_rx(struct sk_buff *skb) { { int proto, len, ulen; int proto, len, ulen; int hits = 0; struct iphdr *iph; struct iphdr *iph; struct udphdr *uh; struct udphdr *uh; struct netpoll_info *npi = skb->dev->npinfo; struct netpoll_info *npinfo = skb->dev->npinfo; struct netpoll *np = npi->rx_np; struct netpoll *np, *tmp; if (!np) if (list_empty(&npinfo->rx_np)) goto out; goto out; if (skb->dev->type != ARPHRD_ETHER) if (skb->dev->type != ARPHRD_ETHER) goto out; goto out; /* check if netpoll clients need ARP */ /* check if netpoll clients need ARP */ if (skb->protocol == htons(ETH_P_ARP) && if (skb->protocol == htons(ETH_P_ARP) && atomic_read(&trapped)) { atomic_read(&trapped)) { skb_queue_tail(&npi->arp_tx, skb); skb_queue_tail(&npinfo->arp_tx, skb); return 1; return 1; } } Loading Loading @@ -551,16 +578,23 @@ int __netpoll_rx(struct sk_buff *skb) goto out; goto out; if (checksum_udp(skb, uh, ulen, iph->saddr, iph->daddr)) if (checksum_udp(skb, uh, ulen, iph->saddr, iph->daddr)) goto out; goto out; list_for_each_entry_safe(np, tmp, &npinfo->rx_np, rx) { if (np->local_ip && np->local_ip != iph->daddr) if (np->local_ip && np->local_ip != iph->daddr) goto out; continue; if (np->remote_ip && np->remote_ip != iph->saddr) if (np->remote_ip && np->remote_ip != iph->saddr) goto out; continue; if (np->local_port && np->local_port != ntohs(uh->dest)) if (np->local_port && np->local_port != ntohs(uh->dest)) goto out; continue; np->rx_hook(np, ntohs(uh->source), np->rx_hook(np, ntohs(uh->source), (char *)(uh+1), (char *)(uh+1), ulen - sizeof(struct udphdr)); ulen - sizeof(struct udphdr)); hits++; } if (!hits) goto out; kfree_skb(skb); kfree_skb(skb); return 1; return 1; Loading Loading @@ -684,6 +718,7 @@ int netpoll_setup(struct netpoll *np) struct net_device *ndev = NULL; struct net_device *ndev = NULL; struct in_device *in_dev; struct in_device *in_dev; struct netpoll_info *npinfo; struct netpoll_info *npinfo; struct netpoll *npe, *tmp; unsigned long flags; unsigned long flags; int err; int err; Loading @@ -704,7 +739,7 @@ int netpoll_setup(struct netpoll *np) } } npinfo->rx_flags = 0; npinfo->rx_flags = 0; npinfo->rx_np = NULL; INIT_LIST_HEAD(&npinfo->rx_np); spin_lock_init(&npinfo->rx_lock); spin_lock_init(&npinfo->rx_lock); skb_queue_head_init(&npinfo->arp_tx); skb_queue_head_init(&npinfo->arp_tx); Loading Loading @@ -785,7 +820,7 @@ int netpoll_setup(struct netpoll *np) if (np->rx_hook) { if (np->rx_hook) { spin_lock_irqsave(&npinfo->rx_lock, flags); spin_lock_irqsave(&npinfo->rx_lock, flags); npinfo->rx_flags |= NETPOLL_RX_ENABLED; npinfo->rx_flags |= NETPOLL_RX_ENABLED; npinfo->rx_np = np; list_add_tail(&np->rx, &npinfo->rx_np); spin_unlock_irqrestore(&npinfo->rx_lock, flags); spin_unlock_irqrestore(&npinfo->rx_lock, flags); } } Loading @@ -801,9 +836,16 @@ int netpoll_setup(struct netpoll *np) return 0; return 0; release: release: if (!ndev->npinfo) if (!ndev->npinfo) { spin_lock_irqsave(&npinfo->rx_lock, flags); list_for_each_entry_safe(npe, tmp, &npinfo->rx_np, rx) { npe->dev = NULL; } spin_unlock_irqrestore(&npinfo->rx_lock, flags); kfree(npinfo); kfree(npinfo); np->dev = NULL; } dev_put(ndev); dev_put(ndev); return err; return err; } } Loading @@ -823,9 +865,10 @@ void netpoll_cleanup(struct netpoll *np) if (np->dev) { if (np->dev) { npinfo = np->dev->npinfo; npinfo = np->dev->npinfo; if (npinfo) { if (npinfo) { if (npinfo->rx_np == np) { if (!list_empty(&npinfo->rx_np)) { spin_lock_irqsave(&npinfo->rx_lock, flags); spin_lock_irqsave(&npinfo->rx_lock, flags); npinfo->rx_np = NULL; list_del(&np->rx); if (list_empty(&npinfo->rx_np)) npinfo->rx_flags &= ~NETPOLL_RX_ENABLED; npinfo->rx_flags &= ~NETPOLL_RX_ENABLED; spin_unlock_irqrestore(&npinfo->rx_lock, flags); spin_unlock_irqrestore(&npinfo->rx_lock, flags); } } Loading