X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=datapath%2Fvport-netdev.c;h=215a47e49ca78bd9aa1bfbf4cb6f124a4065735b;hb=57e8d2bdc174cb24e2ae474ea805c7b7308d68a5;hp=727194e3e767fd1671b7cc53254265c0fc453d9b;hpb=1fc7083dc49130065f23555d4812420ed3901677;p=sliver-openvswitch.git diff --git a/datapath/vport-netdev.c b/datapath/vport-netdev.c index 727194e3e..215a47e49 100644 --- a/datapath/vport-netdev.c +++ b/datapath/vport-netdev.c @@ -25,10 +25,10 @@ #include #include #include +#include #include -#include "checksum.h" #include "datapath.h" #include "vlan.h" #include "vport-internal_dev.h" @@ -63,7 +63,8 @@ static rx_handler_result_t netdev_frame_hook(struct sk_buff **pskb) return RX_HANDLER_CONSUMED; } -#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) || \ + defined HAVE_RHEL_OVS_HOOK /* Called with rcu_read_lock and bottom-halves disabled. */ static struct sk_buff *netdev_frame_hook(struct sk_buff *skb) { @@ -78,7 +79,7 @@ static struct sk_buff *netdev_frame_hook(struct sk_buff *skb) return NULL; } -#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32) /* * Used as br_handle_frame_hook. (Cannot run bridge at the same time, even on * different set of devices!) @@ -90,40 +91,49 @@ static struct sk_buff *netdev_frame_hook(struct net_bridge_port *p, netdev_port_receive((struct vport *)p, skb); return NULL; } -#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -/* - * Used as br_handle_frame_hook. (Cannot run bridge at the same time, even on - * different set of devices!) - */ -/* Called with rcu_read_lock and bottom-halves disabled. */ -static int netdev_frame_hook(struct net_bridge_port *p, struct sk_buff **pskb) -{ - netdev_port_receive((struct vport *)p, *pskb); - return 1; -} #else #error #endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) || \ + defined HAVE_RHEL_OVS_HOOK static int netdev_init(void) { return 0; } static void netdev_exit(void) { } #else -static int netdev_init(void) +static int port_count; + +static void netdev_init(void) { + port_count++; + if (port_count > 1) + return; + /* Hook into callback used by the bridge to intercept packets. * Parasites we are. */ br_handle_frame_hook = netdev_frame_hook; - return 0; + return; } static void netdev_exit(void) { + port_count--; + if (port_count > 0) + return; + br_handle_frame_hook = NULL; } #endif +static struct net_device *get_dpdev(struct datapath *dp) +{ + struct vport *local; + + local = ovs_vport_ovsl(dp, OVSP_LOCAL); + BUG_ON(!local); + return netdev_vport_priv(local)->dev; +} + static struct vport *netdev_create(const struct vport_parms *parms) { struct vport *vport; @@ -152,19 +162,28 @@ static struct vport *netdev_create(const struct vport_parms *parms) goto error_put; } + rtnl_lock(); + err = netdev_master_upper_dev_link(netdev_vport->dev, + get_dpdev(vport->dp)); + if (err) + goto error_unlock; + err = netdev_rx_handler_register(netdev_vport->dev, netdev_frame_hook, vport); if (err) - goto error_put; + goto error_master_upper_dev_unlink; dev_set_promiscuity(netdev_vport->dev, 1); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) - dev_disable_lro(netdev_vport->dev); -#endif netdev_vport->dev->priv_flags |= IFF_OVS_DATAPATH; + rtnl_unlock(); + netdev_init(); return vport; +error_master_upper_dev_unlink: + netdev_upper_dev_unlink(netdev_vport->dev, get_dpdev(vport->dp)); +error_unlock: + rtnl_unlock(); error_put: dev_put(netdev_vport->dev); error_free_vport: @@ -186,9 +205,13 @@ static void netdev_destroy(struct vport *vport) { struct netdev_vport *netdev_vport = netdev_vport_priv(vport); + netdev_exit(); + rtnl_lock(); netdev_vport->dev->priv_flags &= ~IFF_OVS_DATAPATH; netdev_rx_handler_unregister(netdev_vport->dev); + netdev_upper_dev_unlink(netdev_vport->dev, get_dpdev(vport->dp)); dev_set_promiscuity(netdev_vport->dev, -1); + rtnl_unlock(); call_rcu(&netdev_vport->rcu, free_port_rcu); } @@ -199,12 +222,6 @@ const char *ovs_netdev_get_name(const struct vport *vport) return netdev_vport->dev->name; } -int ovs_netdev_get_ifindex(const struct vport *vport) -{ - const struct netdev_vport *netdev_vport = netdev_vport_priv(vport); - return netdev_vport->dev->ifindex; -} - /* Must be called with rcu_read_lock. */ static void netdev_port_receive(struct vport *vport, struct sk_buff *skb) { @@ -223,13 +240,9 @@ static void netdev_port_receive(struct vport *vport, struct sk_buff *skb) return; skb_push(skb, ETH_HLEN); + ovs_skb_postpush_rcsum(skb, skb->data, ETH_HLEN); - if (unlikely(compute_ip_summed(skb, false))) - goto error; - - vlan_copy_skb_tci(skb); - - ovs_vport_receive(vport, skb); + ovs_vport_receive(vport, skb, NULL); return; error: @@ -269,11 +282,10 @@ static int netdev_send(struct vport *vport, struct sk_buff *skb) net_warn_ratelimited("%s: dropped over-mtu packet: %d > %d\n", netdev_vport->dev->name, packet_length(skb), mtu); - goto error; + goto drop; } skb->dev = netdev_vport->dev; - forward_ip_summed(skb, true); if (vlan_tx_tag_present(skb) && !dev_supports_vlan_tx(skb->dev)) { int features; @@ -290,19 +302,15 @@ static int netdev_send(struct vport *vport, struct sk_buff *skb) nskb = skb_gso_segment(skb, features); if (!nskb) { if (unlikely(skb_cloned(skb) && - pskb_expand_head(skb, 0, 0, GFP_ATOMIC))) { - kfree_skb(skb); - return 0; - } + pskb_expand_head(skb, 0, 0, GFP_ATOMIC))) + goto drop; skb_shinfo(skb)->gso_type &= ~SKB_GSO_DODGY; goto tag; } - if (IS_ERR(nskb)) { - kfree_skb(skb); - return 0; - } + if (IS_ERR(nskb)) + goto drop; consume_skb(skb); skb = nskb; @@ -311,7 +319,7 @@ static int netdev_send(struct vport *vport, struct sk_buff *skb) nskb = skb->next; skb->next = NULL; - skb = __vlan_put_tag(skb, vlan_tx_tag_get(skb)); + skb = __vlan_put_tag(skb, skb->vlan_proto, vlan_tx_tag_get(skb)); if (likely(skb)) { len += skb->len; vlan_set_tci(skb, 0); @@ -325,7 +333,7 @@ static int netdev_send(struct vport *vport, struct sk_buff *skb) } tag: - skb = __vlan_put_tag(skb, vlan_tx_tag_get(skb)); + skb = __vlan_put_tag(skb, skb->vlan_proto, vlan_tx_tag_get(skb)); if (unlikely(!skb)) return 0; vlan_set_tci(skb, 0); @@ -336,22 +344,26 @@ tag: return len; -error: +drop: kfree_skb(skb); - ovs_vport_record_error(vport, VPORT_E_TX_DROPPED); return 0; } /* Returns null if this device is not attached to a datapath. */ struct vport *ovs_netdev_get_vport(struct net_device *dev) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) || \ + defined HAVE_RHEL_OVS_HOOK #if IFF_OVS_DATAPATH != 0 if (likely(dev->priv_flags & IFF_OVS_DATAPATH)) #else if (likely(rcu_access_pointer(dev->rx_handler) == netdev_frame_hook)) #endif +#ifdef HAVE_RHEL_OVS_HOOK + return (struct vport *)rcu_dereference_rtnl(dev->ax25_ptr); +#else return (struct vport *)rcu_dereference_rtnl(dev->rx_handler_data); +#endif else return NULL; #else @@ -361,17 +373,14 @@ struct vport *ovs_netdev_get_vport(struct net_device *dev) const struct vport_ops ovs_netdev_vport_ops = { .type = OVS_VPORT_TYPE_NETDEV, - .flags = VPORT_F_REQUIRED, - .init = netdev_init, - .exit = netdev_exit, .create = netdev_create, .destroy = netdev_destroy, .get_name = ovs_netdev_get_name, - .get_ifindex = ovs_netdev_get_ifindex, .send = netdev_send, }; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) && \ + !defined HAVE_RHEL_OVS_HOOK /* * Enforces, mutual exclusion with the Linux bridge module, by declaring and * exporting br_should_route_hook. Because the bridge module also exports the