summaryrefslogtreecommitdiff
path: root/datapath/vport-internal_dev.c
diff options
context:
space:
mode:
authorJesse Gross <jesse@nicira.com>2010-07-29 15:59:36 -0700
committerJesse Gross <jesse@nicira.com>2010-07-30 13:44:30 -0700
commit63db6ec35ddc2432dda059f277257886c2d1ba8f (patch)
treea9b9aee4af1d442b274a19b43ff35391b4d737cf /datapath/vport-internal_dev.c
parentcc98976af50fec5643a372aa9c7a4c4e565e939e (diff)
downloadopenvswitch-63db6ec35ddc2432dda059f277257886c2d1ba8f.tar.gz
vport-internal: Set vport to NULL when detaching.
'struct net_device' is refcounted and can stick around for quite a while if someone is still holding a reference to it. However, we free the vport that it is attached to in the next RCU grace period after detach. This assigns the vport to NULL on detach and adds appropriate checks.
Diffstat (limited to 'datapath/vport-internal_dev.c')
-rw-r--r--datapath/vport-internal_dev.c47
1 files changed, 27 insertions, 20 deletions
diff --git a/datapath/vport-internal_dev.c b/datapath/vport-internal_dev.c
index eca2a60b4..6cbfdf80d 100644
--- a/datapath/vport-internal_dev.c
+++ b/datapath/vport-internal_dev.c
@@ -19,7 +19,7 @@
#include "vport-netdev.h"
struct internal_dev {
- struct vport *vport;
+ struct vport *attached_vport, *vport;
struct net_device_stats stats;
};
@@ -70,11 +70,12 @@ static int internal_dev_mac_addr(struct net_device *dev, void *p)
/* Called with rcu_read_lock and bottom-halves disabled. */
static int internal_dev_xmit(struct sk_buff *skb, struct net_device *netdev)
{
- struct vport *vport = internal_dev_get_vport(netdev);
+ struct internal_dev *internal_dev = internal_dev_priv(netdev);
+ struct vport *vport = rcu_dereference(internal_dev->vport);
/* We need our own clone. */
skb = skb_share_check(skb, GFP_ATOMIC);
- if (!skb) {
+ if (unlikely(!skb)) {
vport_record_error(vport, VPORT_E_RX_DROPPED);
return 0;
}
@@ -102,9 +103,15 @@ static int internal_dev_stop(struct net_device *netdev)
static void internal_dev_getinfo(struct net_device *netdev,
struct ethtool_drvinfo *info)
{
- struct dp_port *dp_port = vport_get_dp_port(internal_dev_get_vport(netdev));
+ struct vport *vport = internal_dev_get_vport(netdev);
+ struct dp_port *dp_port;
strcpy(info->driver, "openvswitch");
+
+ if (!vport)
+ return;
+
+ dp_port = vport_get_dp_port(vport);
if (dp_port)
sprintf(info->bus_info, "%d.%d", dp_port->dp->dp_idx, dp_port->port_no);
}
@@ -122,14 +129,18 @@ static struct ethtool_ops internal_dev_ethtool_ops = {
static int internal_dev_change_mtu(struct net_device *netdev, int new_mtu)
{
- struct dp_port *dp_port = vport_get_dp_port(internal_dev_get_vport(netdev));
+ struct vport *vport = internal_dev_get_vport(netdev);
if (new_mtu < 68)
return -EINVAL;
- if (dp_port) {
- if (new_mtu > dp_min_mtu(dp_port->dp))
- return -EINVAL;
+ if (vport) {
+ struct dp_port *dp_port = vport_get_dp_port(vport);
+
+ if (dp_port) {
+ if (new_mtu > dp_min_mtu(dp_port->dp))
+ return -EINVAL;
+ }
}
netdev->mtu = new_mtu;
@@ -211,7 +222,7 @@ static struct vport *internal_dev_create(const char *name,
}
internal_dev = internal_dev_priv(netdev_vport->dev);
- internal_dev->vport = vport;
+ rcu_assign_pointer(internal_dev->vport, vport);
err = register_netdevice(netdev_vport->dev);
if (err)
@@ -240,13 +251,11 @@ static int internal_dev_destroy(struct vport *vport)
static int internal_dev_attach(struct vport *vport)
{
struct netdev_vport *netdev_vport = netdev_vport_priv(vport);
+ struct internal_dev *internal_dev = internal_dev_priv(netdev_vport->dev);
+ rcu_assign_pointer(internal_dev->attached_vport, internal_dev->vport);
dev_set_promiscuity(netdev_vport->dev, 1);
-
- /* It would make sense to assign dev->br_port here too, but
- * that causes packets received on internal ports to get caught
- * in netdev_frame_hook(). In turn netdev_frame_hook() can reject them
- * back to the network stack, but that's a waste of time. */
+ netif_start_queue(netdev_vport->dev);
return 0;
}
@@ -254,13 +263,11 @@ static int internal_dev_attach(struct vport *vport)
static int internal_dev_detach(struct vport *vport)
{
struct netdev_vport *netdev_vport = netdev_vport_priv(vport);
+ struct internal_dev *internal_dev = internal_dev_priv(netdev_vport->dev);
+ netif_stop_queue(netdev_vport->dev);
dev_set_promiscuity(netdev_vport->dev, -1);
-
- /* Make sure that no packets arrive from now on, since
- * internal_dev_xmit() will try to find itself through
- * p->dp->ports[], and we're about to set that to null. */
- netif_tx_disable(netdev_vport->dev);
+ rcu_assign_pointer(internal_dev->attached_vport, NULL);
return 0;
}
@@ -322,5 +329,5 @@ int is_internal_vport(const struct vport *vport)
struct vport *internal_dev_get_vport(struct net_device *netdev)
{
struct internal_dev *internal_dev = internal_dev_priv(netdev);
- return rcu_dereference(internal_dev->vport);
+ return rcu_dereference(internal_dev->attached_vport);
}