From 37f055c758e2d74ee4abc1ebca94aa62700f3ca0 Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Mon, 23 Aug 2010 15:30:10 +0900 Subject: [PATCH] datapath: use rx_handler_data pointer This adds compatibility with kernel changeset "bridge: use rx_handler_data pointer to store net_bridge_port pointer" which was added between 2.6.35 and 2.6.36-rc1. With this change it is now safe to (attempt to) insert both bridge and datapath with newer (>=2.6.36) kernels, although whichever is inserted second will fail to initialise on the call to netdev_rx_handler_register() Signed-off-by: Simon Horman [Jesse: fixed merge conflicts in vport-netdev.c and netdevice.h] Signed-off-by: Jesse Gross --- .../linux-2.6/compat-2.6/include/linux/if.h | 7 +++++ .../compat-2.6/include/linux/netdevice.h | 6 +++- datapath/vport-netdev.c | 29 ++++++++++++++----- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/datapath/linux-2.6/compat-2.6/include/linux/if.h b/datapath/linux-2.6/compat-2.6/include/linux/if.h index 0aa9ee35..2fd629c8 100644 --- a/datapath/linux-2.6/compat-2.6/include/linux/if.h +++ b/datapath/linux-2.6/compat-2.6/include/linux/if.h @@ -10,4 +10,11 @@ #endif /* linux kernel < 2.6.31 */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) +/* XXX: Change to a unique value after merge */ +#define IFF_OVS_DATAPATH IFF_BRIDGE_PORT +#else +#define IFF_OVS_DATAPATH 0 /* no-op flag */ +#endif + #endif diff --git a/datapath/linux-2.6/compat-2.6/include/linux/netdevice.h b/datapath/linux-2.6/compat-2.6/include/linux/netdevice.h index 29a7cccf..11d9f78f 100644 --- a/datapath/linux-2.6/compat-2.6/include/linux/netdevice.h +++ b/datapath/linux-2.6/compat-2.6/include/linux/netdevice.h @@ -96,9 +96,13 @@ static inline int netdev_rx_handler_register(struct net_device *dev, { if (dev->br_port) return -EBUSY; + rcu_assign_pointer(dev->br_port, rx_handler_data); return 0; } -static inline void netdev_rx_handler_unregister(struct net_device *dev) { } +static inline void netdev_rx_handler_unregister(struct net_device *dev) +{ + rcu_assign_pointer(dev->br_port, NULL); +} #endif #endif diff --git a/datapath/vport-netdev.c b/datapath/vport-netdev.c index e6045354..e93a5ff3 100644 --- a/datapath/vport-netdev.c +++ b/datapath/vport-netdev.c @@ -36,7 +36,7 @@ static struct sk_buff *netdev_frame_hook(struct sk_buff *skb) if (unlikely(skb->pkt_type == PACKET_LOOPBACK)) return skb; - vport = (struct vport *)rcu_dereference(skb->dev->br_port); + vport = netdev_get_vport(skb->dev); netdev_port_receive(vport, skb); @@ -150,15 +150,14 @@ static int netdev_attach(struct vport *vport) struct netdev_vport *netdev_vport = netdev_vport_priv(vport); int err; - rcu_assign_pointer(netdev_vport->dev->br_port, - (struct net_bridge_port *)vport); err = netdev_rx_handler_register(netdev_vport->dev, netdev_frame_hook, - NULL); + vport); if (err) return err; dev_set_promiscuity(netdev_vport->dev, 1); dev_disable_lro(netdev_vport->dev); + netdev_vport->dev->priv_flags |= IFF_OVS_DATAPATH; return 0; } @@ -167,8 +166,8 @@ static int netdev_detach(struct vport *vport) { struct netdev_vport *netdev_vport = netdev_vport_priv(vport); + netdev_vport->dev->priv_flags &= ~IFF_OVS_DATAPATH; netdev_rx_handler_unregister(netdev_vport->dev); - rcu_assign_pointer(netdev_vport->dev->br_port, NULL); dev_set_promiscuity(netdev_vport->dev, -1); return 0; @@ -304,7 +303,18 @@ static int netdev_send(struct vport *vport, struct sk_buff *skb) /* Returns null if this device is not attached to a datapath. */ struct vport *netdev_get_vport(struct net_device *dev) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) + /* XXX: The bridge code may have registered the data. + * So check that the handler pointer is the datapath's. + * Once the merge is done and IFF_OVS_DATAPATH stops + * being the same value as IFF_BRIDGE_PORT the check can + * simply be netdev_vport->dev->priv_flags & IFF_OVS_DATAPATH. */ + if (rcu_dereference(dev->rx_handler) != netdev_frame_hook) + return NULL; + return (struct vport *)rcu_dereference(dev->rx_handler_data); +#else return (struct vport *)rcu_dereference(dev->br_port); +#endif } struct vport_ops netdev_vport_ops = { @@ -332,10 +342,12 @@ struct vport_ops netdev_vport_ops = { .send = netdev_send, }; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) /* - * Open vSwitch cannot safely coexist with the Linux bridge module on any - * released version of Linux, because there is only a single bridge hook - * function and only a single br_port member in struct net_device. + * In kernels earlier than 2.6.36, Open vSwitch cannot safely coexist with + * the Linux bridge module on any released version of Linux, because there + * is only a single bridge hook function and only a single br_port member + * in struct net_device. * * Declaring and exporting this symbol enforces mutual exclusion. The bridge * module also exports the same symbol, so the module loader will refuse to @@ -347,3 +359,4 @@ struct vport_ops netdev_vport_ops = { */ typeof(br_should_route_hook) br_should_route_hook; EXPORT_SYMBOL(br_should_route_hook); +#endif -- 2.30.2