Message ID | 20100527134935.GA6916@psychotron.lab.eng.brq.redhat.com |
---|---|
State | Superseded, archived |
Delegated to: | David Miller |
Headers | show |
Jiri Pirko wrote: > @@ -511,10 +512,16 @@ static void macvlan_setup(struct net_device *dev) > dev->tx_queue_len = 0; > } > > +static struct netdev_rx_handler macvlan_rx_handler = { > + .order = NETDEV_RX_HANDLER_ORDER_MACVLAN, > + .callback = macvlan_handle_frame, > +}; It seems this could be const since you duplicate it on registration. > + > static int macvlan_port_create(struct net_device *dev) > { > struct macvlan_port *port; > unsigned int i; > + int err; > > if (dev->type != ARPHRD_ETHER || dev->flags & IFF_LOOPBACK) > return -EINVAL; > @@ -528,6 +535,15 @@ static int macvlan_port_create(struct net_device *dev) > for (i = 0; i < MACVLAN_HASH_SIZE; i++) > INIT_HLIST_HEAD(&port->vlan_hash[i]); > rcu_assign_pointer(dev->macvlan_port, port); > + > + err = netdev_rx_handler_register(dev, &macvlan_rx_handler); > + if (err) { > + rcu_assign_pointer(dev->macvlan_port, NULL); > + synchronize_rcu(); > + kfree(port); > + return err; > + } I'd prefer goto-based unroll since that makes changes in the future easier. > + > return 0; > } > > diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h > index a1bff65..8e95b2d 100644 > --- a/include/linux/netdevice.h > +++ b/include/linux/netdevice.h > @@ -254,6 +254,15 @@ struct netdev_hw_addr_list { > #define netdev_for_each_mc_addr(ha, dev) \ > netdev_hw_addr_list_for_each(ha, &(dev)->mc) > > + > +struct netdev_rx_handler { > + struct list_head list; > + unsigned int order; > +#define NETDEV_RX_HANDLER_ORDER_BRIDGE 1 > +#define NETDEV_RX_HANDLER_ORDER_MACVLAN 2 Any reason for not using an enum? > + struct sk_buff *(*callback)(struct sk_buff *skb); > +}; > + > struct hh_cache { > struct hh_cache *hh_next; /* Next entry */ > atomic_t hh_refcnt; /* number of users */ > @@ -1031,6 +1040,10 @@ struct net_device { > /* GARP */ > struct garp_port *garp_port; > > + /* receive handlers (hooks) list */ > + spinlock_t rx_handlers_lock; > + struct list_head rx_handlers; > + > /* class/net/name entry */ > struct device dev; > /* space for optional device, statistics, and wireless sysfs groups */ > diff --git a/net/core/dev.c b/net/core/dev.c > index 6c82065..8d4a817 100644 > --- a/net/core/dev.c > +++ b/net/core/dev.c > @@ -2744,6 +2688,82 @@ void netif_nit_deliver(struct sk_buff *skb) > rcu_read_unlock(); > } > > +static bool rx_handlers_equal(struct netdev_rx_handler *rh1, > + struct netdev_rx_handler *rh2) > +{ > + return (rh1->order == rh2->order) && (rh1->callback == rh2->callback); > +} > + > +/** > + * netdev_rx_handler_register - register receive handler > + * @dev: device to register a handler for > + * @rh: receive handler to register > + * > + * Register a receive hander for a device. This handler will then be > + * called from __netif_receive_skb. A negative errno code is returned > + * on a failure. > + */ > +int netdev_rx_handler_register(struct net_device *dev, > + struct netdev_rx_handler *rh) > +{ > + struct list_head *list, *add_after; > + struct netdev_rx_handler *rh1; > + int err = 0; > + > + spin_lock_bh(&dev->rx_handlers_lock); Why are you using a spin lock and even disable BHs? This function should only be called from user context, so a mutex will work fine (and would fix the use of GFP_KERNEL in an atomic section). > + add_after = &dev->rx_handlers; > + list_for_each(list, &dev->rx_handlers) { Naming the element "list" is confusing. Also this should be using list_for_each_entry(). > + rh1 = list_entry(list, struct netdev_rx_handler, list); > + if (rx_handlers_equal(rh, rh1)) { > + err = -EEXIST; > + goto unlock; > + } > + if (rh1->order > rh->order) > + break; > + add_after = list; > + } > + rh1 = kzalloc(sizeof(*rh), GFP_KERNEL); > + if (!rh1) { > + err = -ENOMEM; > + goto unlock; > + } > + > + rh1->order = rh->order; > + rh1->callback = rh->callback; > + list_add_rcu(&rh1->list, add_after); > + > +unlock: > + spin_unlock_bh(&dev->rx_handlers_lock); > + > + return err; > +} > +EXPORT_SYMBOL(netdev_rx_handler_register); -- To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Le jeudi 27 mai 2010 à 15:49 +0200, Jiri Pirko a écrit : > +/** > + * netdev_rx_handler_unregister - unregister receive handler > + * @dev: device to unregister a handler from > + * @rh: receive handler to unregister > + * > + * Unregister a receive hander from a device. > + */ > +void netdev_rx_handler_unregister(struct net_device *dev, > + struct netdev_rx_handler *rh) > +{ > + struct netdev_rx_handler *rh1; > + > + spin_lock_bh(&dev->rx_handlers_lock); > + list_for_each_entry(rh1, &dev->rx_handlers, list) { > + if (rx_handlers_equal(rh, rh1)) { > + list_del_rcu(&rh1->list); > + synchronize_net(); > + kfree(rh1); > + break; > + } > + } > + spin_unlock_bh(&dev->rx_handlers_lock); > +} > +EXPORT_SYMBOL(netdev_rx_handler_unregister); > + Please dont synchronize_net(); inside the spin_lock_bh() section, at a very minimum. void netdev_rx_handler_unregister(struct net_device *dev, struct netdev_rx_handler *rh) { struct netdev_rx_handler *rh1, *found = NULL; spin_lock_bh(&dev->rx_handlers_lock); list_for_each_entry(rh1, &dev->rx_handlers, list) { if (rx_handlers_equal(rh, rh1)) { list_del_rcu(&rh1->list); found = rh1; break; } } spin_unlock_bh(&dev->rx_handlers_lock); if (found) { synchronize_net(); kfree(rh1); } } This synchronize_net() proliferation makes me very nervous. Am I the only one that think this thing is/should be avoided as much as possible ? Please dont use synchronize_net() but a call_rcu(), there is absolutely no point making this thread waits 30 or 40 ms, there is no risk here. Thanks -- To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Thu, 27 May 2010 15:49:36 +0200 Jiri Pirko <jpirko@redhat.com> wrote: > What this patch does is it removes two receive frame hooks (for bridge and for > macvlan) from __netif_receive_skb. These are replaced them with a general > list of rx_handlers which is iterated thru instead. > > Then a network driver (of virtual netdev like macvlan or bridge) can register > an rx_handler for needed net device to be called from __netif_receive_skb. > > Signed-off-by: Jiri Pirko <jpirko@redhat.com> This is almost identical to my previous RFC but it is per device. Why bother with a rx handler lock, why not just use RTNL since the hook should only be changed from control operations. -- To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Thu, May 27, 2010 at 04:06:54PM CEST, kaber@trash.net wrote: >Jiri Pirko wrote: >> @@ -511,10 +512,16 @@ static void macvlan_setup(struct net_device *dev) >> dev->tx_queue_len = 0; >> } >> >> +static struct netdev_rx_handler macvlan_rx_handler = { >> + .order = NETDEV_RX_HANDLER_ORDER_MACVLAN, >> + .callback = macvlan_handle_frame, >> +}; > >It seems this could be const since you duplicate it on >registration. Noted. > >> + >> static int macvlan_port_create(struct net_device *dev) >> { >> struct macvlan_port *port; >> unsigned int i; >> + int err; >> >> if (dev->type != ARPHRD_ETHER || dev->flags & IFF_LOOPBACK) >> return -EINVAL; >> @@ -528,6 +535,15 @@ static int macvlan_port_create(struct net_device *dev) >> for (i = 0; i < MACVLAN_HASH_SIZE; i++) >> INIT_HLIST_HEAD(&port->vlan_hash[i]); >> rcu_assign_pointer(dev->macvlan_port, port); >> + >> + err = netdev_rx_handler_register(dev, &macvlan_rx_handler); >> + if (err) { >> + rcu_assign_pointer(dev->macvlan_port, NULL); >> + synchronize_rcu(); >> + kfree(port); >> + return err; >> + } > >I'd prefer goto-based unroll since that makes changes in the >future easier. Ok, I thought this won't be necessary in this case, but right, looks better. > >> + >> return 0; >> } >> >> diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h >> index a1bff65..8e95b2d 100644 >> --- a/include/linux/netdevice.h >> +++ b/include/linux/netdevice.h >> @@ -254,6 +254,15 @@ struct netdev_hw_addr_list { >> #define netdev_for_each_mc_addr(ha, dev) \ >> netdev_hw_addr_list_for_each(ha, &(dev)->mc) >> >> + >> +struct netdev_rx_handler { >> + struct list_head list; >> + unsigned int order; >> +#define NETDEV_RX_HANDLER_ORDER_BRIDGE 1 >> +#define NETDEV_RX_HANDLER_ORDER_MACVLAN 2 > >Any reason for not using an enum? No, I can use enum, but this "inlining" gives a person who is looking at the code the connection on the first look. > >> + struct sk_buff *(*callback)(struct sk_buff *skb); >> +}; >> + >> struct hh_cache { >> struct hh_cache *hh_next; /* Next entry */ >> atomic_t hh_refcnt; /* number of users */ >> @@ -1031,6 +1040,10 @@ struct net_device { >> /* GARP */ >> struct garp_port *garp_port; >> >> + /* receive handlers (hooks) list */ >> + spinlock_t rx_handlers_lock; >> + struct list_head rx_handlers; >> + >> /* class/net/name entry */ >> struct device dev; >> /* space for optional device, statistics, and wireless sysfs groups */ >> diff --git a/net/core/dev.c b/net/core/dev.c >> index 6c82065..8d4a817 100644 >> --- a/net/core/dev.c >> +++ b/net/core/dev.c >> @@ -2744,6 +2688,82 @@ void netif_nit_deliver(struct sk_buff *skb) >> rcu_read_unlock(); >> } >> >> +static bool rx_handlers_equal(struct netdev_rx_handler *rh1, >> + struct netdev_rx_handler *rh2) >> +{ >> + return (rh1->order == rh2->order) && (rh1->callback == rh2->callback); >> +} >> + >> +/** >> + * netdev_rx_handler_register - register receive handler >> + * @dev: device to register a handler for >> + * @rh: receive handler to register >> + * >> + * Register a receive hander for a device. This handler will then be >> + * called from __netif_receive_skb. A negative errno code is returned >> + * on a failure. >> + */ >> +int netdev_rx_handler_register(struct net_device *dev, >> + struct netdev_rx_handler *rh) >> +{ >> + struct list_head *list, *add_after; >> + struct netdev_rx_handler *rh1; >> + int err = 0; >> + >> + spin_lock_bh(&dev->rx_handlers_lock); > >Why are you using a spin lock and even disable BHs? This function >should only be called from user context, so a mutex will work fine >(and would fix the use of GFP_KERNEL in an atomic section). Right, I will use rather rtnl_lock as suggested by Stephen. > >> + add_after = &dev->rx_handlers; >> + list_for_each(list, &dev->rx_handlers) { > >Naming the element "list" is confusing. Also this should be >using list_for_each_entry(). Well I'm not using list_for_each_entry because I use the list_head cursor as a head in list_add_rcu (add_after assignment) > >> + rh1 = list_entry(list, struct netdev_rx_handler, list); >> + if (rx_handlers_equal(rh, rh1)) { >> + err = -EEXIST; >> + goto unlock; >> + } >> + if (rh1->order > rh->order) >> + break; >> + add_after = list; >> + } >> + rh1 = kzalloc(sizeof(*rh), GFP_KERNEL); >> + if (!rh1) { >> + err = -ENOMEM; >> + goto unlock; >> + } >> + >> + rh1->order = rh->order; >> + rh1->callback = rh->callback; >> + list_add_rcu(&rh1->list, add_after); >> + >> +unlock: >> + spin_unlock_bh(&dev->rx_handlers_lock); >> + >> + return err; >> +} >> +EXPORT_SYMBOL(netdev_rx_handler_register); Thanks all for comments, I'll send V2 soon -- To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Thu, May 27, 2010 at 04:17:46PM CEST, eric.dumazet@gmail.com wrote: >Le jeudi 27 mai 2010 à 15:49 +0200, Jiri Pirko a écrit : > >> +/** >> + * netdev_rx_handler_unregister - unregister receive handler >> + * @dev: device to unregister a handler from >> + * @rh: receive handler to unregister >> + * >> + * Unregister a receive hander from a device. >> + */ >> +void netdev_rx_handler_unregister(struct net_device *dev, >> + struct netdev_rx_handler *rh) >> +{ >> + struct netdev_rx_handler *rh1; >> + >> + spin_lock_bh(&dev->rx_handlers_lock); >> + list_for_each_entry(rh1, &dev->rx_handlers, list) { >> + if (rx_handlers_equal(rh, rh1)) { >> + list_del_rcu(&rh1->list); >> + synchronize_net(); >> + kfree(rh1); >> + break; >> + } >> + } >> + spin_unlock_bh(&dev->rx_handlers_lock); >> +} >> +EXPORT_SYMBOL(netdev_rx_handler_unregister); >> + > >Please dont synchronize_net(); inside the spin_lock_bh() section, at a >very minimum. > >void netdev_rx_handler_unregister(struct net_device *dev, > struct netdev_rx_handler *rh) >{ > struct netdev_rx_handler *rh1, *found = NULL; > > spin_lock_bh(&dev->rx_handlers_lock); > list_for_each_entry(rh1, &dev->rx_handlers, list) { > if (rx_handlers_equal(rh, rh1)) { > list_del_rcu(&rh1->list); > found = rh1; > break; > } > } > spin_unlock_bh(&dev->rx_handlers_lock); > if (found) { > synchronize_net(); > kfree(rh1); > } >} I had it done in the same way originally. But I though that's not a problem to do in inside the lock. > > >This synchronize_net() proliferation makes me very nervous. > >Am I the only one that think this thing is/should be avoided as much as >possible ? > >Please dont use synchronize_net() but a call_rcu(), there is absolutely >no point making this thread waits 30 or 40 ms, there is no risk here. Ok, will do. Thanks a lot. > >Thanks > > -- To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index 87e8d4c..100b16a 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -145,15 +145,16 @@ static void macvlan_broadcast(struct sk_buff *skb, } /* called under rcu_read_lock() from netif_receive_skb */ -static struct sk_buff *macvlan_handle_frame(struct macvlan_port *port, - struct sk_buff *skb) +static struct sk_buff *macvlan_handle_frame(struct sk_buff *skb) { + struct macvlan_port *port; const struct ethhdr *eth = eth_hdr(skb); const struct macvlan_dev *vlan; const struct macvlan_dev *src; struct net_device *dev; unsigned int len; + port = rcu_dereference(skb->dev->macvlan_port); if (is_multicast_ether_addr(eth->h_dest)) { src = macvlan_hash_lookup(port, eth->h_source); if (!src) @@ -511,10 +512,16 @@ static void macvlan_setup(struct net_device *dev) dev->tx_queue_len = 0; } +static struct netdev_rx_handler macvlan_rx_handler = { + .order = NETDEV_RX_HANDLER_ORDER_MACVLAN, + .callback = macvlan_handle_frame, +}; + static int macvlan_port_create(struct net_device *dev) { struct macvlan_port *port; unsigned int i; + int err; if (dev->type != ARPHRD_ETHER || dev->flags & IFF_LOOPBACK) return -EINVAL; @@ -528,6 +535,15 @@ static int macvlan_port_create(struct net_device *dev) for (i = 0; i < MACVLAN_HASH_SIZE; i++) INIT_HLIST_HEAD(&port->vlan_hash[i]); rcu_assign_pointer(dev->macvlan_port, port); + + err = netdev_rx_handler_register(dev, &macvlan_rx_handler); + if (err) { + rcu_assign_pointer(dev->macvlan_port, NULL); + synchronize_rcu(); + kfree(port); + return err; + } + return 0; } @@ -535,6 +551,7 @@ static void macvlan_port_destroy(struct net_device *dev) { struct macvlan_port *port = dev->macvlan_port; + netdev_rx_handler_unregister(dev, &macvlan_rx_handler); rcu_assign_pointer(dev->macvlan_port, NULL); synchronize_rcu(); kfree(port); @@ -767,14 +784,12 @@ static int __init macvlan_init_module(void) int err; register_netdevice_notifier(&macvlan_notifier_block); - macvlan_handle_frame_hook = macvlan_handle_frame; err = macvlan_link_register(&macvlan_link_ops); if (err < 0) goto err1; return 0; err1: - macvlan_handle_frame_hook = NULL; unregister_netdevice_notifier(&macvlan_notifier_block); return err; } @@ -782,7 +797,6 @@ err1: static void __exit macvlan_cleanup_module(void) { rtnl_link_unregister(&macvlan_link_ops); - macvlan_handle_frame_hook = NULL; unregister_netdevice_notifier(&macvlan_notifier_block); } diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h index 938b7e8..0d241a5 100644 --- a/include/linux/if_bridge.h +++ b/include/linux/if_bridge.h @@ -102,8 +102,6 @@ struct __fdb_entry { #include <linux/netdevice.h> extern void brioctl_set(int (*ioctl_hook)(struct net *, unsigned int, void __user *)); -extern struct sk_buff *(*br_handle_frame_hook)(struct net_bridge_port *p, - struct sk_buff *skb); extern int (*br_should_route_hook)(struct sk_buff *skb); #endif diff --git a/include/linux/if_macvlan.h b/include/linux/if_macvlan.h index 9ea047a..c26a0e4 100644 --- a/include/linux/if_macvlan.h +++ b/include/linux/if_macvlan.h @@ -84,8 +84,4 @@ extern int macvlan_link_register(struct rtnl_link_ops *ops); extern netdev_tx_t macvlan_start_xmit(struct sk_buff *skb, struct net_device *dev); - -extern struct sk_buff *(*macvlan_handle_frame_hook)(struct macvlan_port *, - struct sk_buff *); - #endif /* _LINUX_IF_MACVLAN_H */ diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index a1bff65..8e95b2d 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -254,6 +254,15 @@ struct netdev_hw_addr_list { #define netdev_for_each_mc_addr(ha, dev) \ netdev_hw_addr_list_for_each(ha, &(dev)->mc) + +struct netdev_rx_handler { + struct list_head list; + unsigned int order; +#define NETDEV_RX_HANDLER_ORDER_BRIDGE 1 +#define NETDEV_RX_HANDLER_ORDER_MACVLAN 2 + struct sk_buff *(*callback)(struct sk_buff *skb); +}; + struct hh_cache { struct hh_cache *hh_next; /* Next entry */ atomic_t hh_refcnt; /* number of users */ @@ -1031,6 +1040,10 @@ struct net_device { /* GARP */ struct garp_port *garp_port; + /* receive handlers (hooks) list */ + spinlock_t rx_handlers_lock; + struct list_head rx_handlers; + /* class/net/name entry */ struct device dev; /* space for optional device, statistics, and wireless sysfs groups */ @@ -1685,6 +1698,11 @@ static inline void napi_free_frags(struct napi_struct *napi) napi->skb = NULL; } +extern int netdev_rx_handler_register(struct net_device *dev, + struct netdev_rx_handler *rh); +extern void netdev_rx_handler_unregister(struct net_device *dev, + struct netdev_rx_handler *rh); + extern void netif_nit_deliver(struct sk_buff *skb); extern int dev_valid_name(const char *name); extern int dev_ioctl(struct net *net, unsigned int cmd, void __user *); diff --git a/net/bridge/br.c b/net/bridge/br.c index 76357b5..c8436fa 100644 --- a/net/bridge/br.c +++ b/net/bridge/br.c @@ -63,7 +63,6 @@ static int __init br_init(void) goto err_out4; brioctl_set(br_ioctl_deviceless_stub); - br_handle_frame_hook = br_handle_frame; #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE) br_fdb_test_addr_hook = br_fdb_test_addr; @@ -100,7 +99,6 @@ static void __exit br_deinit(void) br_fdb_test_addr_hook = NULL; #endif - br_handle_frame_hook = NULL; br_fdb_fini(); } diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index 18b245e..1d6bf64 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -119,6 +119,11 @@ static void destroy_nbp_rcu(struct rcu_head *head) destroy_nbp(p); } +static struct netdev_rx_handler br_rx_handler = { + .order = NETDEV_RX_HANDLER_ORDER_BRIDGE, + .callback = br_handle_frame, +}; + /* Delete port(interface) from bridge is done in two steps. * via RCU. First step, marks device as down. That deletes * all the timers and stops new packets from flowing through. @@ -147,6 +152,7 @@ static void del_nbp(struct net_bridge_port *p) list_del_rcu(&p->list); + netdev_rx_handler_unregister(dev, &br_rx_handler); rcu_assign_pointer(dev->br_port, NULL); br_multicast_del_port(p); @@ -429,6 +435,11 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) goto err2; rcu_assign_pointer(dev->br_port, p); + + err = netdev_rx_handler_register(dev, &br_rx_handler); + if (err) + goto err3; + dev_disable_lro(dev); list_add_rcu(&p->list, &br->port_list); @@ -451,6 +462,9 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) br_netpoll_enable(br, dev); return 0; +err3: + rcu_assign_pointer(dev->br_port, NULL); + synchronize_rcu(); err2: br_fdb_delete_by_port(br, p, 1); err1: diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index d36e700..99647d8 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -131,15 +131,19 @@ static inline int is_link_local(const unsigned char *dest) } /* - * Called via br_handle_frame_hook. * Return NULL if skb is handled - * note: already called with rcu_read_lock (preempt_disabled) + * note: already called with rcu_read_lock (preempt_disabled) from + * netif_receive_skb */ -struct sk_buff *br_handle_frame(struct net_bridge_port *p, struct sk_buff *skb) +struct sk_buff *br_handle_frame(struct sk_buff *skb) { + struct net_bridge_port *p; const unsigned char *dest = eth_hdr(skb)->h_dest; int (*rhook)(struct sk_buff *skb); + if (skb->pkt_type == PACKET_LOOPBACK) + return skb; + if (!is_valid_ether_addr(eth_hdr(skb)->h_source)) goto drop; @@ -147,6 +151,8 @@ struct sk_buff *br_handle_frame(struct net_bridge_port *p, struct sk_buff *skb) if (!skb) return NULL; + p = rcu_dereference(skb->dev->br_port); + if (unlikely(is_link_local(dest))) { /* Pause frames shouldn't be passed up by driver anyway */ if (skb->protocol == htons(ETH_P_PAUSE)) diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 0f4a74b..c83519b 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -331,8 +331,7 @@ extern void br_features_recompute(struct net_bridge *br); /* br_input.c */ extern int br_handle_frame_finish(struct sk_buff *skb); -extern struct sk_buff *br_handle_frame(struct net_bridge_port *p, - struct sk_buff *skb); +extern struct sk_buff *br_handle_frame(struct sk_buff *skb); /* br_ioctl.c */ extern int br_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); diff --git a/net/core/dev.c b/net/core/dev.c index 6c82065..8d4a817 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2585,70 +2585,14 @@ static inline int deliver_skb(struct sk_buff *skb, return pt_prev->func(skb, skb->dev, pt_prev, orig_dev); } -#if defined(CONFIG_BRIDGE) || defined (CONFIG_BRIDGE_MODULE) - -#if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE) +#if (defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)) && \ + (defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)) /* This hook is defined here for ATM LANE */ int (*br_fdb_test_addr_hook)(struct net_device *dev, unsigned char *addr) __read_mostly; EXPORT_SYMBOL_GPL(br_fdb_test_addr_hook); #endif -/* - * If bridge module is loaded call bridging hook. - * returns NULL if packet was consumed. - */ -struct sk_buff *(*br_handle_frame_hook)(struct net_bridge_port *p, - struct sk_buff *skb) __read_mostly; -EXPORT_SYMBOL_GPL(br_handle_frame_hook); - -static inline struct sk_buff *handle_bridge(struct sk_buff *skb, - struct packet_type **pt_prev, int *ret, - struct net_device *orig_dev) -{ - struct net_bridge_port *port; - - if (skb->pkt_type == PACKET_LOOPBACK || - (port = rcu_dereference(skb->dev->br_port)) == NULL) - return skb; - - if (*pt_prev) { - *ret = deliver_skb(skb, *pt_prev, orig_dev); - *pt_prev = NULL; - } - - return br_handle_frame_hook(port, skb); -} -#else -#define handle_bridge(skb, pt_prev, ret, orig_dev) (skb) -#endif - -#if defined(CONFIG_MACVLAN) || defined(CONFIG_MACVLAN_MODULE) -struct sk_buff *(*macvlan_handle_frame_hook)(struct macvlan_port *p, - struct sk_buff *skb) __read_mostly; -EXPORT_SYMBOL_GPL(macvlan_handle_frame_hook); - -static inline struct sk_buff *handle_macvlan(struct sk_buff *skb, - struct packet_type **pt_prev, - int *ret, - struct net_device *orig_dev) -{ - struct macvlan_port *port; - - port = rcu_dereference(skb->dev->macvlan_port); - if (!port) - return skb; - - if (*pt_prev) { - *ret = deliver_skb(skb, *pt_prev, orig_dev); - *pt_prev = NULL; - } - return macvlan_handle_frame_hook(port, skb); -} -#else -#define handle_macvlan(skb, pt_prev, ret, orig_dev) (skb) -#endif - #ifdef CONFIG_NET_CLS_ACT /* TODO: Maybe we should just force sch_ingress to be compiled in * when CONFIG_NET_CLS_ACT is? otherwise some useless instructions @@ -2744,6 +2688,82 @@ void netif_nit_deliver(struct sk_buff *skb) rcu_read_unlock(); } +static bool rx_handlers_equal(struct netdev_rx_handler *rh1, + struct netdev_rx_handler *rh2) +{ + return (rh1->order == rh2->order) && (rh1->callback == rh2->callback); +} + +/** + * netdev_rx_handler_register - register receive handler + * @dev: device to register a handler for + * @rh: receive handler to register + * + * Register a receive hander for a device. This handler will then be + * called from __netif_receive_skb. A negative errno code is returned + * on a failure. + */ +int netdev_rx_handler_register(struct net_device *dev, + struct netdev_rx_handler *rh) +{ + struct list_head *list, *add_after; + struct netdev_rx_handler *rh1; + int err = 0; + + spin_lock_bh(&dev->rx_handlers_lock); + add_after = &dev->rx_handlers; + list_for_each(list, &dev->rx_handlers) { + rh1 = list_entry(list, struct netdev_rx_handler, list); + if (rx_handlers_equal(rh, rh1)) { + err = -EEXIST; + goto unlock; + } + if (rh1->order > rh->order) + break; + add_after = list; + } + rh1 = kzalloc(sizeof(*rh), GFP_KERNEL); + if (!rh1) { + err = -ENOMEM; + goto unlock; + } + + rh1->order = rh->order; + rh1->callback = rh->callback; + list_add_rcu(&rh1->list, add_after); + +unlock: + spin_unlock_bh(&dev->rx_handlers_lock); + + return err; +} +EXPORT_SYMBOL(netdev_rx_handler_register); + +/** + * netdev_rx_handler_unregister - unregister receive handler + * @dev: device to unregister a handler from + * @rh: receive handler to unregister + * + * Unregister a receive hander from a device. + */ +void netdev_rx_handler_unregister(struct net_device *dev, + struct netdev_rx_handler *rh) +{ + struct netdev_rx_handler *rh1; + + spin_lock_bh(&dev->rx_handlers_lock); + list_for_each_entry(rh1, &dev->rx_handlers, list) { + if (rx_handlers_equal(rh, rh1)) { + list_del_rcu(&rh1->list); + synchronize_net(); + kfree(rh1); + break; + } + } + spin_unlock_bh(&dev->rx_handlers_lock); +} +EXPORT_SYMBOL(netdev_rx_handler_unregister); + static inline void skb_bond_set_mac_by_master(struct sk_buff *skb, struct net_device *master) { @@ -2796,6 +2816,7 @@ EXPORT_SYMBOL(__skb_bond_should_drop); static int __netif_receive_skb(struct sk_buff *skb) { struct packet_type *ptype, *pt_prev; + struct netdev_rx_handler *rh; struct net_device *orig_dev; struct net_device *master; struct net_device *null_or_orig; @@ -2859,12 +2880,18 @@ static int __netif_receive_skb(struct sk_buff *skb) ncls: #endif - skb = handle_bridge(skb, &pt_prev, &ret, orig_dev); - if (!skb) - goto out; - skb = handle_macvlan(skb, &pt_prev, &ret, orig_dev); - if (!skb) - goto out; + /* + * Go through various rx handlers, like bridge, macvlan etc. + */ + list_for_each_entry_rcu(rh, &skb->dev->rx_handlers, list) { + if (pt_prev) { + ret = deliver_skb(skb, pt_prev, orig_dev); + pt_prev = NULL; + } + skb = rh->callback(skb); + if (!skb) + goto out; + } /* * Make sure frames received on VLAN interfaces stacked on @@ -4932,6 +4959,7 @@ int register_netdevice(struct net_device *dev) BUG_ON(!net); spin_lock_init(&dev->addr_list_lock); + spin_lock_init(&dev->rx_handlers_lock); netdev_set_addr_lockdep_class(dev); netdev_init_queue_locks(dev); @@ -5371,6 +5399,8 @@ struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name, dev_mc_init(dev); dev_uc_init(dev); + INIT_LIST_HEAD(&dev->rx_handlers); + dev_net_set(dev, &init_net); dev->_tx = tx;
What this patch does is it removes two receive frame hooks (for bridge and for macvlan) from __netif_receive_skb. These are replaced them with a general list of rx_handlers which is iterated thru instead. Then a network driver (of virtual netdev like macvlan or bridge) can register an rx_handler for needed net device to be called from __netif_receive_skb. Signed-off-by: Jiri Pirko <jpirko@redhat.com> --- drivers/net/macvlan.c | 24 +++++-- include/linux/if_bridge.h | 2 - include/linux/if_macvlan.h | 4 - include/linux/netdevice.h | 18 +++++ net/bridge/br.c | 2 - net/bridge/br_if.c | 14 ++++ net/bridge/br_input.c | 12 +++- net/bridge/br_private.h | 3 +- net/core/dev.c | 158 ++++++++++++++++++++++++++------------------ 9 files changed, 155 insertions(+), 82 deletions(-)