]>
Commit | Line | Data |
---|---|---|
4fe64886 JG |
1 | /* |
2 | * Copyright (c) 2010 Nicira Networks. | |
3 | * Distributed under the terms of the GNU GPL version 2. | |
4 | * | |
5 | * Significant portions of this file may be copied from parts of the Linux | |
6 | * kernel, by Linus Torvalds and others. | |
7 | */ | |
8 | ||
9 | #include <linux/dcache.h> | |
10 | #include <linux/kernel.h> | |
11 | #include <linux/list.h> | |
e33adfd0 | 12 | #include <linux/rtnetlink.h> |
4fe64886 JG |
13 | |
14 | #include "datapath.h" | |
15 | #include "vport.h" | |
16 | #include "vport-generic.h" | |
17 | ||
18 | struct device_config { | |
19 | struct rcu_head rcu; | |
20 | ||
dd851cbb | 21 | char peer_name[IFNAMSIZ]; |
4fe64886 JG |
22 | unsigned char eth_addr[ETH_ALEN]; |
23 | unsigned int mtu; | |
24 | }; | |
25 | ||
26 | struct patch_vport { | |
5b68fb42 JG |
27 | struct rcu_head rcu; |
28 | ||
4fe64886 JG |
29 | char name[IFNAMSIZ]; |
30 | ||
31 | /* Protected by RTNL lock. */ | |
4fe64886 JG |
32 | struct hlist_node hash_node; |
33 | ||
e1040c77 JG |
34 | struct vport __rcu *peer; |
35 | struct device_config __rcu *devconf; | |
4fe64886 JG |
36 | }; |
37 | ||
4fe64886 JG |
38 | /* Protected by RTNL lock. */ |
39 | static struct hlist_head *peer_table; | |
40 | #define PEER_HASH_BUCKETS 256 | |
41 | ||
7237e4f4 BP |
42 | static void update_peers(const char *name, struct vport *); |
43 | ||
fceb2a5b | 44 | static inline struct patch_vport *patch_vport_priv(const struct vport *vport) |
4fe64886 JG |
45 | { |
46 | return vport_priv(vport); | |
47 | } | |
48 | ||
49 | /* RCU callback. */ | |
fceb2a5b | 50 | static void free_config(struct rcu_head *rcu) |
4fe64886 JG |
51 | { |
52 | struct device_config *c = container_of(rcu, struct device_config, rcu); | |
53 | kfree(c); | |
54 | } | |
55 | ||
fceb2a5b JG |
56 | static void assign_config_rcu(struct vport *vport, |
57 | struct device_config *new_config) | |
4fe64886 JG |
58 | { |
59 | struct patch_vport *patch_vport = patch_vport_priv(vport); | |
60 | struct device_config *old_config; | |
61 | ||
e33adfd0 | 62 | old_config = rtnl_dereference(patch_vport->devconf); |
4fe64886 JG |
63 | rcu_assign_pointer(patch_vport->devconf, new_config); |
64 | call_rcu(&old_config->rcu, free_config); | |
65 | } | |
66 | ||
fceb2a5b | 67 | static struct hlist_head *hash_bucket(const char *name) |
4fe64886 JG |
68 | { |
69 | unsigned int hash = full_name_hash(name, strlen(name)); | |
70 | return &peer_table[hash & (PEER_HASH_BUCKETS - 1)]; | |
71 | } | |
72 | ||
fceb2a5b | 73 | static int patch_init(void) |
4fe64886 JG |
74 | { |
75 | peer_table = kzalloc(PEER_HASH_BUCKETS * sizeof(struct hlist_head), | |
76 | GFP_KERNEL); | |
77 | if (!peer_table) | |
78 | return -ENOMEM; | |
79 | ||
80 | return 0; | |
81 | } | |
82 | ||
fceb2a5b | 83 | static void patch_exit(void) |
4fe64886 JG |
84 | { |
85 | kfree(peer_table); | |
86 | } | |
87 | ||
dd851cbb JP |
88 | static int set_config(struct vport *vport, const void *config, |
89 | struct device_config *devconf) | |
4fe64886 JG |
90 | { |
91 | struct patch_vport *patch_vport = patch_vport_priv(vport); | |
92 | char peer_name[IFNAMSIZ]; | |
4fe64886 | 93 | |
c3827f61 | 94 | strlcpy(peer_name, config, IFNAMSIZ); |
4fe64886 JG |
95 | |
96 | if (!strcmp(patch_vport->name, peer_name)) | |
97 | return -EINVAL; | |
98 | ||
dd851cbb | 99 | strcpy(devconf->peer_name, peer_name); |
4fe64886 | 100 | |
4fe64886 JG |
101 | return 0; |
102 | } | |
103 | ||
94903c98 | 104 | static struct vport *patch_create(const struct vport_parms *parms) |
4fe64886 JG |
105 | { |
106 | struct vport *vport; | |
107 | struct patch_vport *patch_vport; | |
dd851cbb | 108 | const char *peer_name; |
4fe64886 JG |
109 | int err; |
110 | ||
e779d8d9 | 111 | vport = vport_alloc(sizeof(struct patch_vport), &patch_vport_ops, parms); |
4fe64886 JG |
112 | if (IS_ERR(vport)) { |
113 | err = PTR_ERR(vport); | |
114 | goto error; | |
115 | } | |
116 | ||
117 | patch_vport = patch_vport_priv(vport); | |
118 | ||
94903c98 | 119 | strcpy(patch_vport->name, parms->name); |
4fe64886 | 120 | |
4fe64886 JG |
121 | patch_vport->devconf = kmalloc(sizeof(struct device_config), GFP_KERNEL); |
122 | if (!patch_vport->devconf) { | |
123 | err = -ENOMEM; | |
124 | goto error_free_vport; | |
125 | } | |
126 | ||
dd851cbb JP |
127 | err = set_config(vport, parms->config, patch_vport->devconf); |
128 | if (err) | |
129 | goto error_free_devconf; | |
130 | ||
4fe64886 | 131 | vport_gen_rand_ether_addr(patch_vport->devconf->eth_addr); |
fd2a9392 | 132 | |
6068bd7c JG |
133 | /* Make the default MTU fairly large so that it doesn't become the |
134 | * bottleneck on systems using jumbo frames. */ | |
fd2a9392 | 135 | patch_vport->devconf->mtu = 65535; |
4fe64886 | 136 | |
dd851cbb JP |
137 | peer_name = patch_vport->devconf->peer_name; |
138 | hlist_add_head(&patch_vport->hash_node, hash_bucket(peer_name)); | |
7237e4f4 | 139 | |
dd851cbb | 140 | rcu_assign_pointer(patch_vport->peer, vport_locate(peer_name)); |
7237e4f4 BP |
141 | update_peers(patch_vport->name, vport); |
142 | ||
4fe64886 JG |
143 | return vport; |
144 | ||
dd851cbb JP |
145 | error_free_devconf: |
146 | kfree(patch_vport->devconf); | |
4fe64886 JG |
147 | error_free_vport: |
148 | vport_free(vport); | |
149 | error: | |
150 | return ERR_PTR(err); | |
151 | } | |
152 | ||
c3827f61 | 153 | static int patch_modify(struct vport *vport, struct odp_port *port) |
4fe64886 | 154 | { |
dd851cbb JP |
155 | struct patch_vport *patch_vport = patch_vport_priv(vport); |
156 | struct device_config *devconf; | |
157 | int err; | |
e779d8d9 | 158 | |
f827d749 JG |
159 | devconf = kmemdup(rtnl_dereference(patch_vport->devconf), |
160 | sizeof(struct device_config), GFP_KERNEL); | |
dd851cbb JP |
161 | if (!devconf) { |
162 | err = -ENOMEM; | |
163 | goto error; | |
e779d8d9 | 164 | } |
dd851cbb JP |
165 | |
166 | err = set_config(vport, port->config, devconf); | |
167 | if (err) | |
168 | goto error_free; | |
169 | ||
170 | assign_config_rcu(vport, devconf); | |
171 | ||
172 | hlist_del(&patch_vport->hash_node); | |
173 | ||
174 | rcu_assign_pointer(patch_vport->peer, vport_locate(devconf->peer_name)); | |
175 | hlist_add_head(&patch_vport->hash_node, hash_bucket(devconf->peer_name)); | |
176 | ||
177 | return 0; | |
178 | ||
179 | error_free: | |
180 | kfree(devconf); | |
181 | error: | |
e779d8d9 | 182 | return err; |
4fe64886 JG |
183 | } |
184 | ||
5b68fb42 JG |
185 | static void free_port_rcu(struct rcu_head *rcu) |
186 | { | |
187 | struct patch_vport *patch_vport = container_of(rcu, | |
39872c70 | 188 | struct patch_vport, rcu); |
5b68fb42 | 189 | |
39872c70 | 190 | kfree((struct device_config __force *)patch_vport->devconf); |
5b68fb42 JG |
191 | vport_free(vport_from_priv(patch_vport)); |
192 | } | |
193 | ||
fceb2a5b | 194 | static int patch_destroy(struct vport *vport) |
4fe64886 JG |
195 | { |
196 | struct patch_vport *patch_vport = patch_vport_priv(vport); | |
197 | ||
7237e4f4 | 198 | update_peers(patch_vport->name, NULL); |
7237e4f4 | 199 | hlist_del(&patch_vport->hash_node); |
5b68fb42 | 200 | call_rcu(&patch_vport->rcu, free_port_rcu); |
4fe64886 JG |
201 | |
202 | return 0; | |
203 | } | |
204 | ||
fceb2a5b | 205 | static void update_peers(const char *name, struct vport *vport) |
4fe64886 JG |
206 | { |
207 | struct hlist_head *bucket = hash_bucket(name); | |
208 | struct patch_vport *peer_vport; | |
209 | struct hlist_node *node; | |
210 | ||
dd851cbb JP |
211 | hlist_for_each_entry(peer_vport, node, bucket, hash_node) { |
212 | const char *peer_name; | |
213 | ||
214 | peer_name = rtnl_dereference(peer_vport->devconf)->peer_name; | |
215 | if (!strcmp(peer_name, name)) | |
4fe64886 | 216 | rcu_assign_pointer(peer_vport->peer, vport); |
dd851cbb | 217 | } |
4fe64886 JG |
218 | } |
219 | ||
fceb2a5b | 220 | static int patch_set_mtu(struct vport *vport, int mtu) |
4fe64886 JG |
221 | { |
222 | struct patch_vport *patch_vport = patch_vport_priv(vport); | |
223 | struct device_config *devconf; | |
224 | ||
f827d749 JG |
225 | devconf = kmemdup(rtnl_dereference(patch_vport->devconf), |
226 | sizeof(struct device_config), GFP_KERNEL); | |
4fe64886 JG |
227 | if (!devconf) |
228 | return -ENOMEM; | |
229 | ||
230 | devconf->mtu = mtu; | |
231 | assign_config_rcu(vport, devconf); | |
232 | ||
233 | return 0; | |
234 | } | |
235 | ||
fceb2a5b | 236 | static int patch_set_addr(struct vport *vport, const unsigned char *addr) |
4fe64886 JG |
237 | { |
238 | struct patch_vport *patch_vport = patch_vport_priv(vport); | |
239 | struct device_config *devconf; | |
240 | ||
f827d749 JG |
241 | devconf = kmemdup(rtnl_dereference(patch_vport->devconf), |
242 | sizeof(struct device_config), GFP_KERNEL); | |
4fe64886 JG |
243 | if (!devconf) |
244 | return -ENOMEM; | |
245 | ||
246 | memcpy(devconf->eth_addr, addr, ETH_ALEN); | |
247 | assign_config_rcu(vport, devconf); | |
248 | ||
249 | return 0; | |
250 | } | |
251 | ||
252 | ||
fceb2a5b | 253 | static const char *patch_get_name(const struct vport *vport) |
4fe64886 JG |
254 | { |
255 | const struct patch_vport *patch_vport = patch_vport_priv(vport); | |
256 | return patch_vport->name; | |
257 | } | |
258 | ||
fceb2a5b | 259 | static const unsigned char *patch_get_addr(const struct vport *vport) |
4fe64886 JG |
260 | { |
261 | const struct patch_vport *patch_vport = patch_vport_priv(vport); | |
e33adfd0 | 262 | return rcu_dereference_rtnl(patch_vport->devconf)->eth_addr; |
4fe64886 JG |
263 | } |
264 | ||
dd851cbb JP |
265 | static void patch_get_config(const struct vport *vport, void *config) |
266 | { | |
267 | const struct patch_vport *patch_vport = patch_vport_priv(vport); | |
268 | const char *peer_name; | |
269 | ||
270 | peer_name = rcu_dereference_rtnl(patch_vport->devconf)->peer_name; | |
271 | strlcpy(config, peer_name, VPORT_CONFIG_SIZE); | |
272 | } | |
273 | ||
fceb2a5b | 274 | static int patch_get_mtu(const struct vport *vport) |
4fe64886 JG |
275 | { |
276 | const struct patch_vport *patch_vport = patch_vport_priv(vport); | |
e33adfd0 | 277 | return rcu_dereference_rtnl(patch_vport->devconf)->mtu; |
4fe64886 JG |
278 | } |
279 | ||
fceb2a5b | 280 | static int patch_send(struct vport *vport, struct sk_buff *skb) |
4fe64886 JG |
281 | { |
282 | struct patch_vport *patch_vport = patch_vport_priv(vport); | |
283 | struct vport *peer = rcu_dereference(patch_vport->peer); | |
284 | int skb_len = skb->len; | |
285 | ||
286 | if (!peer) { | |
287 | kfree_skb(skb); | |
288 | vport_record_error(vport, VPORT_E_TX_DROPPED); | |
289 | ||
290 | return 0; | |
291 | } | |
292 | ||
293 | vport_receive(peer, skb); | |
294 | return skb_len; | |
295 | } | |
296 | ||
b279fccf | 297 | const struct vport_ops patch_vport_ops = { |
4fe64886 JG |
298 | .type = "patch", |
299 | .flags = VPORT_F_GEN_STATS, | |
300 | .init = patch_init, | |
301 | .exit = patch_exit, | |
302 | .create = patch_create, | |
303 | .modify = patch_modify, | |
304 | .destroy = patch_destroy, | |
4fe64886 JG |
305 | .set_mtu = patch_set_mtu, |
306 | .set_addr = patch_set_addr, | |
307 | .get_name = patch_get_name, | |
308 | .get_addr = patch_get_addr, | |
dd851cbb | 309 | .get_config = patch_get_config, |
4fe64886 JG |
310 | .get_dev_flags = vport_gen_get_dev_flags, |
311 | .is_running = vport_gen_is_running, | |
312 | .get_operstate = vport_gen_get_operstate, | |
313 | .get_mtu = patch_get_mtu, | |
314 | .send = patch_send, | |
315 | }; |