'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.
if (is_internal_dev(dev))
vport = internal_dev_get_vport(dev);
if (is_internal_dev(dev))
vport = internal_dev_get_vport(dev);
vport = netdev_get_vport(dev);
vport = netdev_get_vport(dev);
- if (!vport)
- return NOTIFY_DONE;
- }
+ if (!vport)
+ return NOTIFY_DONE;
p = vport_get_dp_port(vport);
p = vport_get_dp_port(vport);
struct datapath *sysfs_get_dp(struct net_device *netdev)
{
struct datapath *sysfs_get_dp(struct net_device *netdev)
{
- return vport_get_dp_port(internal_dev_get_vport(netdev))->dp;
-}
+ struct vport *vport = internal_dev_get_vport(netdev);
+ struct dp_port *dp_port;
+
+ if (!vport)
+ return NULL;
+ dp_port = vport_get_dp_port(vport);
+ if (!dp_port)
+ return NULL;
+
+ return dp_port->dp;
+}
/*
* Common code for storing bridge parameters.
*/
/*
* Common code for storing bridge parameters.
*/
const char *buf, size_t len,
void (*set)(struct datapath *, unsigned long))
{
const char *buf, size_t len,
void (*set)(struct datapath *, unsigned long))
{
- struct datapath *dp = sysfs_get_dp(to_net_dev(d));
char *endp;
unsigned long val;
char *endp;
unsigned long val;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
* xxx ignore the request.
*/
if (val != 0) {
* xxx ignore the request.
*/
if (val != 0) {
- printk("%s: xxx writing dp parms not supported yet!\n",
- dp_name(dp));
+ struct datapath *dp;
+
+ rcu_read_lock();
+
+ dp = sysfs_get_dp(to_net_dev(d));
+ if (dp)
+ printk("%s: xxx writing dp parms not supported yet!\n",
+ dp_name(dp));
+ else
+ result = -ENODEV;
+
+ rcu_read_unlock();
const char *buf,
size_t len)
{
const char *buf,
size_t len)
{
- struct datapath *dp = sysfs_get_dp(to_net_dev(d));
+ struct datapath *dp;
+ ssize_t result = len;
+
+ rcu_read_lock();
- printk("%s: xxx attempt to set_stp_state()\n", dp_name(dp));
+ dp = sysfs_get_dp(to_net_dev(d));
+ if (dp)
+ printk("%s: xxx attempt to set_stp_state()\n", dp_name(dp));
+ else
+ result = -ENODEV;
+ rcu_read_unlock();
+
+ return result;
}
static INTERNAL_DEVICE_ATTR(stp_state, S_IRUGO | S_IWUSR, show_stp_state,
store_stp_state);
}
static INTERNAL_DEVICE_ATTR(stp_state, S_IRUGO | S_IWUSR, show_stp_state,
store_stp_state);
static ssize_t show_bridge_id(DEVICE_PARAMS, char *buf)
{
static ssize_t show_bridge_id(DEVICE_PARAMS, char *buf)
{
- struct datapath *dp = sysfs_get_dp(to_net_dev(d));
- const unsigned char *addr = vport_get_addr(dp->ports[ODPP_LOCAL]->vport);
+ struct vport *vport;
+ ssize_t result;
+
+ rcu_read_lock();
+
+ vport = internal_dev_get_vport(to_net_dev(d));
+ if (vport) {
+ const unsigned char *addr;
+
+ addr = vport_get_addr(vport);
+ result = sprintf(buf, "%.2x%.2x.%.2x%.2x%.2x%.2x%.2x%.2x\n",
+ 0, 0, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
+ } else
+ result = -ENODEV;
- /* xxx Do we need a lock of some sort? */
- return sprintf(buf, "%.2x%.2x.%.2x%.2x%.2x%.2x%.2x%.2x\n",
- 0, 0, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
+ rcu_read_unlock();
+
+ return result;
}
static INTERNAL_DEVICE_ATTR(bridge_id, S_IRUGO, show_bridge_id, NULL);
}
static INTERNAL_DEVICE_ATTR(bridge_id, S_IRUGO, show_bridge_id, NULL);
static ssize_t store_group_addr(DEVICE_PARAMS,
const char *buf, size_t len)
{
static ssize_t store_group_addr(DEVICE_PARAMS,
const char *buf, size_t len)
{
- struct datapath *dp = sysfs_get_dp(to_net_dev(d));
+ struct datapath *dp;
+ ssize_t result = len;
+
+ rcu_read_lock();
+
+ dp = sysfs_get_dp(to_net_dev(d));
+ if (dp)
+ printk("%s: xxx attempt to store_group_addr()\n", dp_name(dp));
+ else
+ result = -ENODEV;
+
+ rcu_read_unlock();
- printk("%s: xxx attempt to store_group_addr()\n", dp_name(dp));
- return len;
}
static INTERNAL_DEVICE_ATTR(group_addr, S_IRUGO | S_IWUSR,
}
static INTERNAL_DEVICE_ATTR(group_addr, S_IRUGO | S_IWUSR,
#include "vport-netdev.h"
struct internal_dev {
#include "vport-netdev.h"
struct internal_dev {
+ struct vport *attached_vport, *vport;
struct net_device_stats stats;
};
struct net_device_stats stats;
};
/* Called with rcu_read_lock and bottom-halves disabled. */
static int internal_dev_xmit(struct sk_buff *skb, struct net_device *netdev)
{
/* 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);
/* We need our own clone. */
skb = skb_share_check(skb, GFP_ATOMIC);
vport_record_error(vport, VPORT_E_RX_DROPPED);
return 0;
}
vport_record_error(vport, VPORT_E_RX_DROPPED);
return 0;
}
static void internal_dev_getinfo(struct net_device *netdev,
struct ethtool_drvinfo *info)
{
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");
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);
}
if (dp_port)
sprintf(info->bus_info, "%d.%d", dp_port->dp->dp_idx, dp_port->port_no);
}
static int internal_dev_change_mtu(struct net_device *netdev, int new_mtu)
{
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 (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;
+ }
}
internal_dev = internal_dev_priv(netdev_vport->dev);
}
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)
err = register_netdevice(netdev_vport->dev);
if (err)
static int internal_dev_attach(struct vport *vport)
{
struct netdev_vport *netdev_vport = netdev_vport_priv(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);
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);
static int internal_dev_detach(struct vport *vport)
{
struct netdev_vport *netdev_vport = netdev_vport_priv(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);
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);
struct vport *internal_dev_get_vport(struct net_device *netdev)
{
struct internal_dev *internal_dev = internal_dev_priv(netdev);
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);