diff options
author | Alex Wang <alexw@nicira.com> | 2014-07-17 18:14:13 -0400 |
---|---|---|
committer | Pravin B Shelar <pshelar@nicira.com> | 2014-07-24 04:15:04 -0400 |
commit | 5cd667b0a4567048bb555927d6ee564f4e5620a9 (patch) | |
tree | 34fc874b8e4e7ad420c25712bcc62039ede0337a /net | |
parent | f6e675324481c56b358091ddb446b2c95a8e047b (diff) |
openvswitch: Allow each vport to have an array of 'port_id's.
In order to allow handlers directly read upcalls from datapath,
we need to support per-handler netlink socket for each vport in
datapath. This commit makes this happen. Also, it is guaranteed
to be backward compatible with previous branch.
Signed-off-by: Alex Wang <alexw@nicira.com>
Acked-by: Thomas Graf <tgraf@redhat.com>
Signed-off-by: Pravin B Shelar <pshelar@nicira.com>
Diffstat (limited to 'net')
-rw-r--r-- | net/openvswitch/datapath.c | 23 | ||||
-rw-r--r-- | net/openvswitch/vport.c | 101 | ||||
-rw-r--r-- | net/openvswitch/vport.h | 27 |
3 files changed, 139 insertions, 12 deletions
diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index 20f59b62721a..65a8e5c089e4 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c | |||
@@ -266,7 +266,7 @@ void ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb) | |||
266 | upcall.cmd = OVS_PACKET_CMD_MISS; | 266 | upcall.cmd = OVS_PACKET_CMD_MISS; |
267 | upcall.key = &key; | 267 | upcall.key = &key; |
268 | upcall.userdata = NULL; | 268 | upcall.userdata = NULL; |
269 | upcall.portid = p->upcall_portid; | 269 | upcall.portid = ovs_vport_find_upcall_portid(p, skb); |
270 | ovs_dp_upcall(dp, skb, &upcall); | 270 | ovs_dp_upcall(dp, skb, &upcall); |
271 | consume_skb(skb); | 271 | consume_skb(skb); |
272 | stats_counter = &stats->n_missed; | 272 | stats_counter = &stats->n_missed; |
@@ -1373,7 +1373,7 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info) | |||
1373 | parms.options = NULL; | 1373 | parms.options = NULL; |
1374 | parms.dp = dp; | 1374 | parms.dp = dp; |
1375 | parms.port_no = OVSP_LOCAL; | 1375 | parms.port_no = OVSP_LOCAL; |
1376 | parms.upcall_portid = nla_get_u32(a[OVS_DP_ATTR_UPCALL_PID]); | 1376 | parms.upcall_portids = a[OVS_DP_ATTR_UPCALL_PID]; |
1377 | 1377 | ||
1378 | ovs_dp_change(dp, a); | 1378 | ovs_dp_change(dp, a); |
1379 | 1379 | ||
@@ -1632,8 +1632,8 @@ static int ovs_vport_cmd_fill_info(struct vport *vport, struct sk_buff *skb, | |||
1632 | 1632 | ||
1633 | if (nla_put_u32(skb, OVS_VPORT_ATTR_PORT_NO, vport->port_no) || | 1633 | if (nla_put_u32(skb, OVS_VPORT_ATTR_PORT_NO, vport->port_no) || |
1634 | nla_put_u32(skb, OVS_VPORT_ATTR_TYPE, vport->ops->type) || | 1634 | nla_put_u32(skb, OVS_VPORT_ATTR_TYPE, vport->ops->type) || |
1635 | nla_put_string(skb, OVS_VPORT_ATTR_NAME, vport->ops->get_name(vport)) || | 1635 | nla_put_string(skb, OVS_VPORT_ATTR_NAME, |
1636 | nla_put_u32(skb, OVS_VPORT_ATTR_UPCALL_PID, vport->upcall_portid)) | 1636 | vport->ops->get_name(vport))) |
1637 | goto nla_put_failure; | 1637 | goto nla_put_failure; |
1638 | 1638 | ||
1639 | ovs_vport_get_stats(vport, &vport_stats); | 1639 | ovs_vport_get_stats(vport, &vport_stats); |
@@ -1641,6 +1641,9 @@ static int ovs_vport_cmd_fill_info(struct vport *vport, struct sk_buff *skb, | |||
1641 | &vport_stats)) | 1641 | &vport_stats)) |
1642 | goto nla_put_failure; | 1642 | goto nla_put_failure; |
1643 | 1643 | ||
1644 | if (ovs_vport_get_upcall_portids(vport, skb)) | ||
1645 | goto nla_put_failure; | ||
1646 | |||
1644 | err = ovs_vport_get_options(vport, skb); | 1647 | err = ovs_vport_get_options(vport, skb); |
1645 | if (err == -EMSGSIZE) | 1648 | if (err == -EMSGSIZE) |
1646 | goto error; | 1649 | goto error; |
@@ -1762,7 +1765,7 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info) | |||
1762 | parms.options = a[OVS_VPORT_ATTR_OPTIONS]; | 1765 | parms.options = a[OVS_VPORT_ATTR_OPTIONS]; |
1763 | parms.dp = dp; | 1766 | parms.dp = dp; |
1764 | parms.port_no = port_no; | 1767 | parms.port_no = port_no; |
1765 | parms.upcall_portid = nla_get_u32(a[OVS_VPORT_ATTR_UPCALL_PID]); | 1768 | parms.upcall_portids = a[OVS_VPORT_ATTR_UPCALL_PID]; |
1766 | 1769 | ||
1767 | vport = new_vport(&parms); | 1770 | vport = new_vport(&parms); |
1768 | err = PTR_ERR(vport); | 1771 | err = PTR_ERR(vport); |
@@ -1812,8 +1815,14 @@ static int ovs_vport_cmd_set(struct sk_buff *skb, struct genl_info *info) | |||
1812 | goto exit_unlock_free; | 1815 | goto exit_unlock_free; |
1813 | } | 1816 | } |
1814 | 1817 | ||
1815 | if (a[OVS_VPORT_ATTR_UPCALL_PID]) | 1818 | |
1816 | vport->upcall_portid = nla_get_u32(a[OVS_VPORT_ATTR_UPCALL_PID]); | 1819 | if (a[OVS_VPORT_ATTR_UPCALL_PID]) { |
1820 | struct nlattr *ids = a[OVS_VPORT_ATTR_UPCALL_PID]; | ||
1821 | |||
1822 | err = ovs_vport_set_upcall_portids(vport, ids); | ||
1823 | if (err) | ||
1824 | goto exit_unlock_free; | ||
1825 | } | ||
1817 | 1826 | ||
1818 | err = ovs_vport_cmd_fill_info(vport, reply, info->snd_portid, | 1827 | err = ovs_vport_cmd_fill_info(vport, reply, info->snd_portid, |
1819 | info->snd_seq, 0, OVS_VPORT_CMD_NEW); | 1828 | info->snd_seq, 0, OVS_VPORT_CMD_NEW); |
diff --git a/net/openvswitch/vport.c b/net/openvswitch/vport.c index 42c0f4a0b78c..702fb21bfe15 100644 --- a/net/openvswitch/vport.c +++ b/net/openvswitch/vport.c | |||
@@ -134,10 +134,12 @@ struct vport *ovs_vport_alloc(int priv_size, const struct vport_ops *ops, | |||
134 | 134 | ||
135 | vport->dp = parms->dp; | 135 | vport->dp = parms->dp; |
136 | vport->port_no = parms->port_no; | 136 | vport->port_no = parms->port_no; |
137 | vport->upcall_portid = parms->upcall_portid; | ||
138 | vport->ops = ops; | 137 | vport->ops = ops; |
139 | INIT_HLIST_NODE(&vport->dp_hash_node); | 138 | INIT_HLIST_NODE(&vport->dp_hash_node); |
140 | 139 | ||
140 | if (ovs_vport_set_upcall_portids(vport, parms->upcall_portids)) | ||
141 | return ERR_PTR(-EINVAL); | ||
142 | |||
141 | vport->percpu_stats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); | 143 | vport->percpu_stats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); |
142 | if (!vport->percpu_stats) { | 144 | if (!vport->percpu_stats) { |
143 | kfree(vport); | 145 | kfree(vport); |
@@ -161,6 +163,10 @@ struct vport *ovs_vport_alloc(int priv_size, const struct vport_ops *ops, | |||
161 | */ | 163 | */ |
162 | void ovs_vport_free(struct vport *vport) | 164 | void ovs_vport_free(struct vport *vport) |
163 | { | 165 | { |
166 | /* vport is freed from RCU callback or error path, Therefore | ||
167 | * it is safe to use raw dereference. | ||
168 | */ | ||
169 | kfree(rcu_dereference_raw(vport->upcall_portids)); | ||
164 | free_percpu(vport->percpu_stats); | 170 | free_percpu(vport->percpu_stats); |
165 | kfree(vport); | 171 | kfree(vport); |
166 | } | 172 | } |
@@ -327,6 +333,99 @@ int ovs_vport_get_options(const struct vport *vport, struct sk_buff *skb) | |||
327 | } | 333 | } |
328 | 334 | ||
329 | /** | 335 | /** |
336 | * ovs_vport_set_upcall_portids - set upcall portids of @vport. | ||
337 | * | ||
338 | * @vport: vport to modify. | ||
339 | * @ids: new configuration, an array of port ids. | ||
340 | * | ||
341 | * Sets the vport's upcall_portids to @ids. | ||
342 | * | ||
343 | * Returns 0 if successful, -EINVAL if @ids is zero length or cannot be parsed | ||
344 | * as an array of U32. | ||
345 | * | ||
346 | * Must be called with ovs_mutex. | ||
347 | */ | ||
348 | int ovs_vport_set_upcall_portids(struct vport *vport, struct nlattr *ids) | ||
349 | { | ||
350 | struct vport_portids *old, *vport_portids; | ||
351 | |||
352 | if (!nla_len(ids) || nla_len(ids) % sizeof(u32)) | ||
353 | return -EINVAL; | ||
354 | |||
355 | old = ovsl_dereference(vport->upcall_portids); | ||
356 | |||
357 | vport_portids = kmalloc(sizeof(*vport_portids) + nla_len(ids), | ||
358 | GFP_KERNEL); | ||
359 | if (!vport_portids) | ||
360 | return -ENOMEM; | ||
361 | |||
362 | vport_portids->n_ids = nla_len(ids) / sizeof(u32); | ||
363 | vport_portids->rn_ids = reciprocal_value(vport_portids->n_ids); | ||
364 | nla_memcpy(vport_portids->ids, ids, nla_len(ids)); | ||
365 | |||
366 | rcu_assign_pointer(vport->upcall_portids, vport_portids); | ||
367 | |||
368 | if (old) | ||
369 | kfree_rcu(old, rcu); | ||
370 | return 0; | ||
371 | } | ||
372 | |||
373 | /** | ||
374 | * ovs_vport_get_upcall_portids - get the upcall_portids of @vport. | ||
375 | * | ||
376 | * @vport: vport from which to retrieve the portids. | ||
377 | * @skb: sk_buff where portids should be appended. | ||
378 | * | ||
379 | * Retrieves the configuration of the given vport, appending the | ||
380 | * %OVS_VPORT_ATTR_UPCALL_PID attribute which is the array of upcall | ||
381 | * portids to @skb. | ||
382 | * | ||
383 | * Returns 0 if successful, -EMSGSIZE if @skb has insufficient room. | ||
384 | * If an error occurs, @skb is left unmodified. Must be called with | ||
385 | * ovs_mutex or rcu_read_lock. | ||
386 | */ | ||
387 | int ovs_vport_get_upcall_portids(const struct vport *vport, | ||
388 | struct sk_buff *skb) | ||
389 | { | ||
390 | struct vport_portids *ids; | ||
391 | |||
392 | ids = rcu_dereference_ovsl(vport->upcall_portids); | ||
393 | |||
394 | if (vport->dp->user_features & OVS_DP_F_VPORT_PIDS) | ||
395 | return nla_put(skb, OVS_VPORT_ATTR_UPCALL_PID, | ||
396 | ids->n_ids * sizeof(u32), (void *)ids->ids); | ||
397 | else | ||
398 | return nla_put_u32(skb, OVS_VPORT_ATTR_UPCALL_PID, ids->ids[0]); | ||
399 | } | ||
400 | |||
401 | /** | ||
402 | * ovs_vport_find_upcall_portid - find the upcall portid to send upcall. | ||
403 | * | ||
404 | * @vport: vport from which the missed packet is received. | ||
405 | * @skb: skb that the missed packet was received. | ||
406 | * | ||
407 | * Uses the skb_get_hash() to select the upcall portid to send the | ||
408 | * upcall. | ||
409 | * | ||
410 | * Returns the portid of the target socket. Must be called with rcu_read_lock. | ||
411 | */ | ||
412 | u32 ovs_vport_find_upcall_portid(const struct vport *p, struct sk_buff *skb) | ||
413 | { | ||
414 | struct vport_portids *ids; | ||
415 | u32 ids_index; | ||
416 | u32 hash; | ||
417 | |||
418 | ids = rcu_dereference(p->upcall_portids); | ||
419 | |||
420 | if (ids->n_ids == 1 && ids->ids[0] == 0) | ||
421 | return 0; | ||
422 | |||
423 | hash = skb_get_hash(skb); | ||
424 | ids_index = hash - ids->n_ids * reciprocal_divide(hash, ids->rn_ids); | ||
425 | return ids->ids[ids_index]; | ||
426 | } | ||
427 | |||
428 | /** | ||
330 | * ovs_vport_receive - pass up received packet to the datapath for processing | 429 | * ovs_vport_receive - pass up received packet to the datapath for processing |
331 | * | 430 | * |
332 | * @vport: vport that received the packet | 431 | * @vport: vport that received the packet |
diff --git a/net/openvswitch/vport.h b/net/openvswitch/vport.h index 8d721e62f388..35f89d84b45e 100644 --- a/net/openvswitch/vport.h +++ b/net/openvswitch/vport.h | |||
@@ -23,6 +23,7 @@ | |||
23 | #include <linux/list.h> | 23 | #include <linux/list.h> |
24 | #include <linux/netlink.h> | 24 | #include <linux/netlink.h> |
25 | #include <linux/openvswitch.h> | 25 | #include <linux/openvswitch.h> |
26 | #include <linux/reciprocal_div.h> | ||
26 | #include <linux/skbuff.h> | 27 | #include <linux/skbuff.h> |
27 | #include <linux/spinlock.h> | 28 | #include <linux/spinlock.h> |
28 | #include <linux/u64_stats_sync.h> | 29 | #include <linux/u64_stats_sync.h> |
@@ -52,6 +53,10 @@ void ovs_vport_get_stats(struct vport *, struct ovs_vport_stats *); | |||
52 | int ovs_vport_set_options(struct vport *, struct nlattr *options); | 53 | int ovs_vport_set_options(struct vport *, struct nlattr *options); |
53 | int ovs_vport_get_options(const struct vport *, struct sk_buff *); | 54 | int ovs_vport_get_options(const struct vport *, struct sk_buff *); |
54 | 55 | ||
56 | int ovs_vport_set_upcall_portids(struct vport *, struct nlattr *pids); | ||
57 | int ovs_vport_get_upcall_portids(const struct vport *, struct sk_buff *); | ||
58 | u32 ovs_vport_find_upcall_portid(const struct vport *, struct sk_buff *); | ||
59 | |||
55 | int ovs_vport_send(struct vport *, struct sk_buff *); | 60 | int ovs_vport_send(struct vport *, struct sk_buff *); |
56 | 61 | ||
57 | /* The following definitions are for implementers of vport devices: */ | 62 | /* The following definitions are for implementers of vport devices: */ |
@@ -62,13 +67,27 @@ struct vport_err_stats { | |||
62 | u64 tx_dropped; | 67 | u64 tx_dropped; |
63 | u64 tx_errors; | 68 | u64 tx_errors; |
64 | }; | 69 | }; |
70 | /** | ||
71 | * struct vport_portids - array of netlink portids of a vport. | ||
72 | * must be protected by rcu. | ||
73 | * @rn_ids: The reciprocal value of @n_ids. | ||
74 | * @rcu: RCU callback head for deferred destruction. | ||
75 | * @n_ids: Size of @ids array. | ||
76 | * @ids: Array storing the Netlink socket pids to be used for packets received | ||
77 | * on this port that miss the flow table. | ||
78 | */ | ||
79 | struct vport_portids { | ||
80 | struct reciprocal_value rn_ids; | ||
81 | struct rcu_head rcu; | ||
82 | u32 n_ids; | ||
83 | u32 ids[]; | ||
84 | }; | ||
65 | 85 | ||
66 | /** | 86 | /** |
67 | * struct vport - one port within a datapath | 87 | * struct vport - one port within a datapath |
68 | * @rcu: RCU callback head for deferred destruction. | 88 | * @rcu: RCU callback head for deferred destruction. |
69 | * @dp: Datapath to which this port belongs. | 89 | * @dp: Datapath to which this port belongs. |
70 | * @upcall_portid: The Netlink port to use for packets received on this port that | 90 | * @upcall_portids: RCU protected 'struct vport_portids'. |
71 | * miss the flow table. | ||
72 | * @port_no: Index into @dp's @ports array. | 91 | * @port_no: Index into @dp's @ports array. |
73 | * @hash_node: Element in @dev_table hash table in vport.c. | 92 | * @hash_node: Element in @dev_table hash table in vport.c. |
74 | * @dp_hash_node: Element in @datapath->ports hash table in datapath.c. | 93 | * @dp_hash_node: Element in @datapath->ports hash table in datapath.c. |
@@ -80,7 +99,7 @@ struct vport_err_stats { | |||
80 | struct vport { | 99 | struct vport { |
81 | struct rcu_head rcu; | 100 | struct rcu_head rcu; |
82 | struct datapath *dp; | 101 | struct datapath *dp; |
83 | u32 upcall_portid; | 102 | struct vport_portids __rcu *upcall_portids; |
84 | u16 port_no; | 103 | u16 port_no; |
85 | 104 | ||
86 | struct hlist_node hash_node; | 105 | struct hlist_node hash_node; |
@@ -111,7 +130,7 @@ struct vport_parms { | |||
111 | /* For ovs_vport_alloc(). */ | 130 | /* For ovs_vport_alloc(). */ |
112 | struct datapath *dp; | 131 | struct datapath *dp; |
113 | u16 port_no; | 132 | u16 port_no; |
114 | u32 upcall_portid; | 133 | struct nlattr *upcall_portids; |
115 | }; | 134 | }; |
116 | 135 | ||
117 | /** | 136 | /** |