Fix "NOHZ: local_softirq_pending 08" message.
[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->pkt_type = PACKET_HOST;
31         skb->protocol = eth_type_trans(skb, netdev);
32         if (in_interrupt())
33                 netif_rx(skb);
34         else
35                 netif_rx_ni(skb);
36         netdev->last_rx = jiffies;
37         dp_dev->stats.rx_packets++;
38         dp_dev->stats.rx_bytes += len;
39         return len;
40 }
41
42 static int dp_dev_mac_addr(struct net_device *dev, void *p)
43 {
44         struct sockaddr *addr = p;
45
46         if (netif_running(dev))
47                 return -EBUSY;
48         if (!is_valid_ether_addr(addr->sa_data))
49                 return -EADDRNOTAVAIL;
50         memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
51         return 0;
52 }
53
54 static int dp_dev_xmit(struct sk_buff *skb, struct net_device *netdev)
55 {
56         struct dp_dev *dp_dev = dp_dev_priv(netdev);
57         struct datapath *dp;
58         rcu_read_lock();
59         dp = dp_dev->dp;
60         if (likely(dp != NULL)) {
61                 dp_dev->stats.tx_packets++;
62                 dp_dev->stats.tx_bytes += skb->len;
63                 skb_reset_mac_header(skb);
64                 fwd_port_input(dp->chain, skb, OFPP_LOCAL);
65         } else {
66                 dp_dev->stats.tx_dropped++;
67                 kfree_skb(skb);
68         }
69         rcu_read_unlock();
70         return 0;
71 }
72
73 static int dp_dev_open(struct net_device *netdev)
74 {
75         netif_start_queue(netdev);
76         return 0;
77 }
78
79 static int dp_dev_stop(struct net_device *netdev)
80 {
81         netif_stop_queue(netdev);
82         return 0;
83 }
84
85 static void
86 do_setup(struct net_device *netdev)
87 {
88         ether_setup(netdev);
89
90         netdev->get_stats = dp_dev_get_stats;
91         netdev->hard_start_xmit = dp_dev_xmit;
92         netdev->open = dp_dev_open;
93         netdev->stop = dp_dev_stop;
94         netdev->tx_queue_len = 0;
95         netdev->set_mac_address = dp_dev_mac_addr;
96
97         netdev->flags = IFF_BROADCAST | IFF_MULTICAST;
98
99         random_ether_addr(netdev->dev_addr);
100 }
101
102
103 int dp_dev_setup(struct datapath *dp)
104 {
105         struct dp_dev *dp_dev;
106         struct net_device *netdev;
107         char of_name[8];
108         int err;
109
110         snprintf(of_name, sizeof of_name, "of%d", dp->dp_idx);
111         netdev = alloc_netdev(sizeof(struct dp_dev), of_name, do_setup);
112         if (!netdev)
113                 return -ENOMEM;
114
115         err = register_netdev(netdev);
116         if (err) {
117                 free_netdev(netdev);
118                 return err;
119         }
120
121         dp_dev = dp_dev_priv(netdev);
122         dp_dev->dp = dp;
123         dp->netdev = netdev;
124         return 0;
125 }
126
127 void dp_dev_destroy(struct datapath *dp)
128 {
129         struct dp_dev *dp_dev = dp_dev_priv(dp->netdev);
130         dp_dev->dp = NULL;
131         synchronize_net();
132         unregister_netdev(dp->netdev);
133         free_netdev(dp->netdev);
134 }
135
136 int is_dp_dev(struct net_device *netdev) 
137 {
138         return netdev->open == dp_dev_open;
139 }