Properly synchronize dp_dev destruction.
[sliver-openvswitch.git] / datapath / dp_dev.c
1 #include <linux/kernel.h>
2 #include <linux/netdevice.h>
3 #include <linux/etherdevice.h>
4 #include <linux/rcupdate.h>
5 #include <linux/skbuff.h>
6
7 #include "datapath.h"
8 #include "forward.h"
9
10 struct dp_dev {
11         struct net_device_stats stats;
12         struct datapath *dp;
13 };
14
15 static struct dp_dev *dp_dev_priv(struct net_device *netdev) 
16 {
17         return netdev_priv(netdev);
18 }
19
20 static struct net_device_stats *dp_dev_get_stats(struct net_device *netdev)
21 {
22         struct dp_dev *dp_dev = dp_dev_priv(netdev);
23         return &dp_dev->stats;
24 }
25
26 int dp_dev_recv(struct net_device *netdev, struct sk_buff *skb) 
27 {
28         int len = skb->len;
29         struct dp_dev *dp_dev = dp_dev_priv(netdev);
30         skb->dev = netdev;
31         skb->pkt_type = PACKET_HOST;
32         skb->protocol = eth_type_trans(skb, netdev);
33         if (in_interrupt())
34                 netif_rx(skb);
35         else
36                 netif_rx_ni(skb);
37         netdev->last_rx = jiffies;
38         dp_dev->stats.rx_packets++;
39         dp_dev->stats.rx_bytes += len;
40         return len;
41 }
42
43 static int dp_dev_mac_addr(struct net_device *dev, void *p)
44 {
45         struct sockaddr *addr = p;
46
47         if (netif_running(dev))
48                 return -EBUSY;
49         if (!is_valid_ether_addr(addr->sa_data))
50                 return -EADDRNOTAVAIL;
51         memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
52         return 0;
53 }
54
55 static int dp_dev_xmit(struct sk_buff *skb, struct net_device *netdev)
56 {
57         struct dp_dev *dp_dev = dp_dev_priv(netdev);
58         struct datapath *dp = dp_dev->dp;
59
60         dp_dev->stats.tx_packets++;
61         dp_dev->stats.tx_bytes += skb->len;
62
63         skb_reset_mac_header(skb);
64         rcu_read_lock();
65         fwd_port_input(dp->chain, skb, OFPP_LOCAL);
66         rcu_read_unlock();
67
68         return 0;
69 }
70
71 static int dp_dev_open(struct net_device *netdev)
72 {
73         netif_start_queue(netdev);
74         return 0;
75 }
76
77 static int dp_dev_stop(struct net_device *netdev)
78 {
79         netif_stop_queue(netdev);
80         return 0;
81 }
82
83 static void
84 do_setup(struct net_device *netdev)
85 {
86         ether_setup(netdev);
87
88         netdev->get_stats = dp_dev_get_stats;
89         netdev->hard_start_xmit = dp_dev_xmit;
90         netdev->open = dp_dev_open;
91         netdev->stop = dp_dev_stop;
92         netdev->tx_queue_len = 0;
93         netdev->set_mac_address = dp_dev_mac_addr;
94
95         netdev->flags = IFF_BROADCAST | IFF_MULTICAST;
96
97         random_ether_addr(netdev->dev_addr);
98 }
99
100
101 int dp_dev_setup(struct datapath *dp)
102 {
103         struct dp_dev *dp_dev;
104         struct net_device *netdev;
105         char of_name[8];
106         int err;
107
108         snprintf(of_name, sizeof of_name, "of%d", dp->dp_idx);
109         netdev = alloc_netdev(sizeof(struct dp_dev), of_name, do_setup);
110         if (!netdev)
111                 return -ENOMEM;
112
113         err = register_netdev(netdev);
114         if (err) {
115                 free_netdev(netdev);
116                 return err;
117         }
118
119         dp_dev = dp_dev_priv(netdev);
120         dp_dev->dp = dp;
121         dp->netdev = netdev;
122         return 0;
123 }
124
125 void dp_dev_destroy(struct datapath *dp)
126 {
127         netif_tx_disable(dp->netdev);
128         synchronize_net();
129         unregister_netdev(dp->netdev);
130         free_netdev(dp->netdev);
131 }
132
133 int is_dp_dev(struct net_device *netdev) 
134 {
135         return netdev->open == dp_dev_open;
136 }