X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=datapath%2Fdp_dev.c;h=ec36361d683f77a595f0a54316d2881ab66c3c1a;hb=refs%2Fheads%2Ffor-nox%2F0.4;hp=02468eb486bf0880f3a0952704fb7e5377b943cc;hpb=d50ae013a29dceadb205b93bc4c1fbf3c2dea120;p=sliver-openvswitch.git diff --git a/datapath/dp_dev.c b/datapath/dp_dev.c index 02468eb48..ec36361d6 100644 --- a/datapath/dp_dev.c +++ b/datapath/dp_dev.c @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include "datapath.h" #include "forward.h" @@ -10,8 +12,11 @@ struct dp_dev { struct net_device_stats stats; struct datapath *dp; + struct sk_buff_head xmit_queue; + struct work_struct xmit_work; }; + static struct dp_dev *dp_dev_priv(struct net_device *netdev) { return netdev_priv(netdev); @@ -57,17 +62,54 @@ static int dp_dev_xmit(struct sk_buff *skb, struct net_device *netdev) struct dp_dev *dp_dev = dp_dev_priv(netdev); struct datapath *dp = dp_dev->dp; + /* By orphaning 'skb' we will screw up socket accounting slightly, but + * the effect is limited to the device queue length. If we don't + * do this, then the sk_buff will be destructed eventually, but it is + * harder to predict when. */ + skb_orphan(skb); + + /* We are going to modify 'skb', by sticking it on &dp_dev->xmit_queue, + * so we need to have our own clone. (At any rate, fwd_port_input() + * will need its own clone, so there's no benefit to queuing any other + * way.) */ + skb = skb_share_check(skb, GFP_ATOMIC); + if (!skb) + return 0; + dp_dev->stats.tx_packets++; dp_dev->stats.tx_bytes += skb->len; - skb_reset_mac_header(skb); - rcu_read_lock(); - fwd_port_input(dp->chain, skb, OFPP_LOCAL); - rcu_read_unlock(); + if (skb_queue_len(&dp_dev->xmit_queue) >= dp->netdev->tx_queue_len) { + /* Queue overflow. Stop transmitter. */ + netif_stop_queue(dp->netdev); + + /* We won't see all dropped packets individually, so overrun + * error is appropriate. */ + dp_dev->stats.tx_fifo_errors++; + } + skb_queue_tail(&dp_dev->xmit_queue, skb); + dp->netdev->trans_start = jiffies; + + schedule_work(&dp_dev->xmit_work); return 0; } +static void dp_dev_do_xmit(struct work_struct *work) +{ + struct dp_dev *dp_dev = container_of(work, struct dp_dev, xmit_work); + struct datapath *dp = dp_dev->dp; + struct sk_buff *skb; + + while ((skb = skb_dequeue(&dp_dev->xmit_queue)) != NULL) { + skb_reset_mac_header(skb); + rcu_read_lock(); + fwd_port_input(dp->chain, skb, dp->local_port); + rcu_read_unlock(); + } + netif_wake_queue(dp->netdev); +} + static int dp_dev_open(struct net_device *netdev) { netif_start_queue(netdev); @@ -80,6 +122,45 @@ static int dp_dev_stop(struct net_device *netdev) return 0; } +/* Check if the DMI UUID contains a Nicira mac address that should be + * used for this interface. The UUID is assumed to be RFC 4122 + * compliant. */ +static void +set_uuid_mac(struct net_device *netdev) +{ + const char *uuid = dmi_get_system_info(DMI_PRODUCT_UUID); + const char *uptr; + uint8_t mac[ETH_ALEN]; + int i; + + if (!uuid || *uuid == '\0' || strlen(uuid) != 36) + return; + + /* We are only interested version 1 UUIDs, since the last six bytes + * are an IEEE 802 MAC address. */ + if (uuid[14] != '1') + return; + + /* Pull out the embedded MAC address. The kernel's sscanf doesn't + * support field widths on hex digits, so we use this hack. */ + uptr = uuid + 24; + for (i=0; idev_addr, mac, ETH_ALEN); +} + static void do_setup(struct net_device *netdev) { @@ -89,12 +170,20 @@ do_setup(struct net_device *netdev) netdev->hard_start_xmit = dp_dev_xmit; netdev->open = dp_dev_open; netdev->stop = dp_dev_stop; - netdev->tx_queue_len = 0; + netdev->tx_queue_len = 100; netdev->set_mac_address = dp_dev_mac_addr; netdev->flags = IFF_BROADCAST | IFF_MULTICAST; random_ether_addr(netdev->dev_addr); + + /* Set the OUI to the Nicira one. */ + netdev->dev_addr[0] = 0x00; + netdev->dev_addr[1] = 0x23; + netdev->dev_addr[2] = 0x20; + + /* Set the top bits to indicate random Nicira address. */ + netdev->dev_addr[3] |= 0xc0; } @@ -116,16 +205,26 @@ int dp_dev_setup(struct datapath *dp) return err; } + /* For "of0", we check the DMI UUID to see if a Nicira mac address + * is available to use instead of the random one just generated. */ + if (dp->dp_idx == 0) + set_uuid_mac(netdev); + dp_dev = dp_dev_priv(netdev); dp_dev->dp = dp; + skb_queue_head_init(&dp_dev->xmit_queue); + INIT_WORK(&dp_dev->xmit_work, dp_dev_do_xmit); dp->netdev = netdev; return 0; } void dp_dev_destroy(struct datapath *dp) { + struct dp_dev *dp_dev = dp_dev_priv(dp->netdev); + netif_tx_disable(dp->netdev); synchronize_net(); + skb_queue_purge(&dp_dev->xmit_queue); unregister_netdev(dp->netdev); free_netdev(dp->netdev); }