diff options
-rw-r--r-- | include/linux/netdev_features.h | 8 | ||||
-rw-r--r-- | include/uapi/linux/openvswitch.h | 13 | ||||
-rw-r--r-- | net/openvswitch/actions.c | 50 | ||||
-rw-r--r-- | net/openvswitch/datapath.c | 26 | ||||
-rw-r--r-- | net/openvswitch/vport-internal_dev.c | 5 | ||||
-rw-r--r-- | net/openvswitch/vport.c | 101 | ||||
-rw-r--r-- | net/openvswitch/vport.h | 27 |
7 files changed, 201 insertions, 29 deletions
diff --git a/include/linux/netdev_features.h b/include/linux/netdev_features.h index d99800cbdcf3..dcfdecbfa0b7 100644 --- a/include/linux/netdev_features.h +++ b/include/linux/netdev_features.h | |||
@@ -176,4 +176,12 @@ enum { | |||
176 | NETIF_F_HW_VLAN_STAG_RX | \ | 176 | NETIF_F_HW_VLAN_STAG_RX | \ |
177 | NETIF_F_HW_VLAN_STAG_TX) | 177 | NETIF_F_HW_VLAN_STAG_TX) |
178 | 178 | ||
179 | #define NETIF_F_GSO_ENCAP_ALL (NETIF_F_GSO_GRE | \ | ||
180 | NETIF_F_GSO_GRE_CSUM | \ | ||
181 | NETIF_F_GSO_IPIP | \ | ||
182 | NETIF_F_GSO_SIT | \ | ||
183 | NETIF_F_GSO_UDP_TUNNEL | \ | ||
184 | NETIF_F_GSO_UDP_TUNNEL_CSUM | \ | ||
185 | NETIF_F_GSO_MPLS) | ||
186 | |||
179 | #endif /* _LINUX_NETDEV_FEATURES_H */ | 187 | #endif /* _LINUX_NETDEV_FEATURES_H */ |
diff --git a/include/uapi/linux/openvswitch.h b/include/uapi/linux/openvswitch.h index 0b979ee4bfc0..a794d1dd7b40 100644 --- a/include/uapi/linux/openvswitch.h +++ b/include/uapi/linux/openvswitch.h | |||
@@ -118,6 +118,9 @@ struct ovs_vport_stats { | |||
118 | /* Allow last Netlink attribute to be unaligned */ | 118 | /* Allow last Netlink attribute to be unaligned */ |
119 | #define OVS_DP_F_UNALIGNED (1 << 0) | 119 | #define OVS_DP_F_UNALIGNED (1 << 0) |
120 | 120 | ||
121 | /* Allow datapath to associate multiple Netlink PIDs to each vport */ | ||
122 | #define OVS_DP_F_VPORT_PIDS (1 << 1) | ||
123 | |||
121 | /* Fixed logical ports. */ | 124 | /* Fixed logical ports. */ |
122 | #define OVSP_LOCAL ((__u32)0) | 125 | #define OVSP_LOCAL ((__u32)0) |
123 | 126 | ||
@@ -203,9 +206,10 @@ enum ovs_vport_type { | |||
203 | * this is the name of the network device. Maximum length %IFNAMSIZ-1 bytes | 206 | * this is the name of the network device. Maximum length %IFNAMSIZ-1 bytes |
204 | * plus a null terminator. | 207 | * plus a null terminator. |
205 | * @OVS_VPORT_ATTR_OPTIONS: Vport-specific configuration information. | 208 | * @OVS_VPORT_ATTR_OPTIONS: Vport-specific configuration information. |
206 | * @OVS_VPORT_ATTR_UPCALL_PID: The Netlink socket in userspace that | 209 | * @OVS_VPORT_ATTR_UPCALL_PID: The array of Netlink socket pids in userspace |
207 | * OVS_PACKET_CMD_MISS upcalls will be directed to for packets received on | 210 | * among which OVS_PACKET_CMD_MISS upcalls will be distributed for packets |
208 | * this port. A value of zero indicates that upcalls should not be sent. | 211 | * received on this port. If this is a single-element array of value 0, |
212 | * upcalls should not be sent. | ||
209 | * @OVS_VPORT_ATTR_STATS: A &struct ovs_vport_stats giving statistics for | 213 | * @OVS_VPORT_ATTR_STATS: A &struct ovs_vport_stats giving statistics for |
210 | * packets sent or received through the vport. | 214 | * packets sent or received through the vport. |
211 | * | 215 | * |
@@ -228,7 +232,8 @@ enum ovs_vport_attr { | |||
228 | OVS_VPORT_ATTR_TYPE, /* u32 OVS_VPORT_TYPE_* constant. */ | 232 | OVS_VPORT_ATTR_TYPE, /* u32 OVS_VPORT_TYPE_* constant. */ |
229 | OVS_VPORT_ATTR_NAME, /* string name, up to IFNAMSIZ bytes long */ | 233 | OVS_VPORT_ATTR_NAME, /* string name, up to IFNAMSIZ bytes long */ |
230 | OVS_VPORT_ATTR_OPTIONS, /* nested attributes, varies by vport type */ | 234 | OVS_VPORT_ATTR_OPTIONS, /* nested attributes, varies by vport type */ |
231 | OVS_VPORT_ATTR_UPCALL_PID, /* u32 Netlink PID to receive upcalls */ | 235 | OVS_VPORT_ATTR_UPCALL_PID, /* array of u32 Netlink socket PIDs for */ |
236 | /* receiving upcalls */ | ||
232 | OVS_VPORT_ATTR_STATS, /* struct ovs_vport_stats */ | 237 | OVS_VPORT_ATTR_STATS, /* struct ovs_vport_stats */ |
233 | __OVS_VPORT_ATTR_MAX | 238 | __OVS_VPORT_ATTR_MAX |
234 | }; | 239 | }; |
diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index e70d8b18e962..fe5cda0deb39 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c | |||
@@ -38,7 +38,7 @@ | |||
38 | #include "vport.h" | 38 | #include "vport.h" |
39 | 39 | ||
40 | static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, | 40 | static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, |
41 | const struct nlattr *attr, int len, bool keep_skb); | 41 | const struct nlattr *attr, int len); |
42 | 42 | ||
43 | static int make_writable(struct sk_buff *skb, int write_len) | 43 | static int make_writable(struct sk_buff *skb, int write_len) |
44 | { | 44 | { |
@@ -434,11 +434,17 @@ static int output_userspace(struct datapath *dp, struct sk_buff *skb, | |||
434 | return ovs_dp_upcall(dp, skb, &upcall); | 434 | return ovs_dp_upcall(dp, skb, &upcall); |
435 | } | 435 | } |
436 | 436 | ||
437 | static bool last_action(const struct nlattr *a, int rem) | ||
438 | { | ||
439 | return a->nla_len == rem; | ||
440 | } | ||
441 | |||
437 | static int sample(struct datapath *dp, struct sk_buff *skb, | 442 | static int sample(struct datapath *dp, struct sk_buff *skb, |
438 | const struct nlattr *attr) | 443 | const struct nlattr *attr) |
439 | { | 444 | { |
440 | const struct nlattr *acts_list = NULL; | 445 | const struct nlattr *acts_list = NULL; |
441 | const struct nlattr *a; | 446 | const struct nlattr *a; |
447 | struct sk_buff *sample_skb; | ||
442 | int rem; | 448 | int rem; |
443 | 449 | ||
444 | for (a = nla_data(attr), rem = nla_len(attr); rem > 0; | 450 | for (a = nla_data(attr), rem = nla_len(attr); rem > 0; |
@@ -455,8 +461,34 @@ static int sample(struct datapath *dp, struct sk_buff *skb, | |||
455 | } | 461 | } |
456 | } | 462 | } |
457 | 463 | ||
458 | return do_execute_actions(dp, skb, nla_data(acts_list), | 464 | rem = nla_len(acts_list); |
459 | nla_len(acts_list), true); | 465 | a = nla_data(acts_list); |
466 | |||
467 | /* Actions list is either empty or only contains a single user-space | ||
468 | * action, the latter being a special case as it is the only known | ||
469 | * usage of the sample action. | ||
470 | * In these special cases don't clone the skb as there are no | ||
471 | * side-effects in the nested actions. | ||
472 | * Otherwise, clone in case the nested actions have side effects. | ||
473 | */ | ||
474 | if (likely(rem == 0 || (nla_type(a) == OVS_ACTION_ATTR_USERSPACE && | ||
475 | last_action(a, rem)))) { | ||
476 | sample_skb = skb; | ||
477 | skb_get(skb); | ||
478 | } else { | ||
479 | sample_skb = skb_clone(skb, GFP_ATOMIC); | ||
480 | if (!sample_skb) /* Skip sample action when out of memory. */ | ||
481 | return 0; | ||
482 | } | ||
483 | |||
484 | /* Note that do_execute_actions() never consumes skb. | ||
485 | * In the case where skb has been cloned above it is the clone that | ||
486 | * is consumed. Otherwise the skb_get(skb) call prevents | ||
487 | * consumption by do_execute_actions(). Thus, it is safe to simply | ||
488 | * return the error code and let the caller (also | ||
489 | * do_execute_actions()) free skb on error. | ||
490 | */ | ||
491 | return do_execute_actions(dp, sample_skb, a, rem); | ||
460 | } | 492 | } |
461 | 493 | ||
462 | static int execute_set_action(struct sk_buff *skb, | 494 | static int execute_set_action(struct sk_buff *skb, |
@@ -507,7 +539,7 @@ static int execute_set_action(struct sk_buff *skb, | |||
507 | 539 | ||
508 | /* Execute a list of actions against 'skb'. */ | 540 | /* Execute a list of actions against 'skb'. */ |
509 | static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, | 541 | static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, |
510 | const struct nlattr *attr, int len, bool keep_skb) | 542 | const struct nlattr *attr, int len) |
511 | { | 543 | { |
512 | /* Every output action needs a separate clone of 'skb', but the common | 544 | /* Every output action needs a separate clone of 'skb', but the common |
513 | * case is just a single output action, so that doing a clone and | 545 | * case is just a single output action, so that doing a clone and |
@@ -562,12 +594,9 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, | |||
562 | } | 594 | } |
563 | } | 595 | } |
564 | 596 | ||
565 | if (prev_port != -1) { | 597 | if (prev_port != -1) |
566 | if (keep_skb) | ||
567 | skb = skb_clone(skb, GFP_ATOMIC); | ||
568 | |||
569 | do_output(dp, skb, prev_port); | 598 | do_output(dp, skb, prev_port); |
570 | } else if (!keep_skb) | 599 | else |
571 | consume_skb(skb); | 600 | consume_skb(skb); |
572 | 601 | ||
573 | return 0; | 602 | return 0; |
@@ -579,6 +608,5 @@ int ovs_execute_actions(struct datapath *dp, struct sk_buff *skb) | |||
579 | struct sw_flow_actions *acts = rcu_dereference(OVS_CB(skb)->flow->sf_acts); | 608 | struct sw_flow_actions *acts = rcu_dereference(OVS_CB(skb)->flow->sf_acts); |
580 | 609 | ||
581 | OVS_CB(skb)->tun_key = NULL; | 610 | OVS_CB(skb)->tun_key = NULL; |
582 | return do_execute_actions(dp, skb, acts->actions, | 611 | return do_execute_actions(dp, skb, acts->actions, acts->actions_len); |
583 | acts->actions_len, false); | ||
584 | } | 612 | } |
diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index 20f59b62721a..3c461e1e4554 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; |
@@ -464,7 +464,8 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb, | |||
464 | upcall->dp_ifindex = dp_ifindex; | 464 | upcall->dp_ifindex = dp_ifindex; |
465 | 465 | ||
466 | nla = nla_nest_start(user_skb, OVS_PACKET_ATTR_KEY); | 466 | nla = nla_nest_start(user_skb, OVS_PACKET_ATTR_KEY); |
467 | ovs_nla_put_flow(upcall_info->key, upcall_info->key, user_skb); | 467 | err = ovs_nla_put_flow(upcall_info->key, upcall_info->key, user_skb); |
468 | BUG_ON(err); | ||
468 | nla_nest_end(user_skb, nla); | 469 | nla_nest_end(user_skb, nla); |
469 | 470 | ||
470 | if (upcall_info->userdata) | 471 | if (upcall_info->userdata) |
@@ -1373,7 +1374,7 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info) | |||
1373 | parms.options = NULL; | 1374 | parms.options = NULL; |
1374 | parms.dp = dp; | 1375 | parms.dp = dp; |
1375 | parms.port_no = OVSP_LOCAL; | 1376 | parms.port_no = OVSP_LOCAL; |
1376 | parms.upcall_portid = nla_get_u32(a[OVS_DP_ATTR_UPCALL_PID]); | 1377 | parms.upcall_portids = a[OVS_DP_ATTR_UPCALL_PID]; |
1377 | 1378 | ||
1378 | ovs_dp_change(dp, a); | 1379 | ovs_dp_change(dp, a); |
1379 | 1380 | ||
@@ -1632,8 +1633,8 @@ static int ovs_vport_cmd_fill_info(struct vport *vport, struct sk_buff *skb, | |||
1632 | 1633 | ||
1633 | if (nla_put_u32(skb, OVS_VPORT_ATTR_PORT_NO, vport->port_no) || | 1634 | 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) || | 1635 | 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)) || | 1636 | nla_put_string(skb, OVS_VPORT_ATTR_NAME, |
1636 | nla_put_u32(skb, OVS_VPORT_ATTR_UPCALL_PID, vport->upcall_portid)) | 1637 | vport->ops->get_name(vport))) |
1637 | goto nla_put_failure; | 1638 | goto nla_put_failure; |
1638 | 1639 | ||
1639 | ovs_vport_get_stats(vport, &vport_stats); | 1640 | ovs_vport_get_stats(vport, &vport_stats); |
@@ -1641,6 +1642,9 @@ static int ovs_vport_cmd_fill_info(struct vport *vport, struct sk_buff *skb, | |||
1641 | &vport_stats)) | 1642 | &vport_stats)) |
1642 | goto nla_put_failure; | 1643 | goto nla_put_failure; |
1643 | 1644 | ||
1645 | if (ovs_vport_get_upcall_portids(vport, skb)) | ||
1646 | goto nla_put_failure; | ||
1647 | |||
1644 | err = ovs_vport_get_options(vport, skb); | 1648 | err = ovs_vport_get_options(vport, skb); |
1645 | if (err == -EMSGSIZE) | 1649 | if (err == -EMSGSIZE) |
1646 | goto error; | 1650 | goto error; |
@@ -1762,7 +1766,7 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info) | |||
1762 | parms.options = a[OVS_VPORT_ATTR_OPTIONS]; | 1766 | parms.options = a[OVS_VPORT_ATTR_OPTIONS]; |
1763 | parms.dp = dp; | 1767 | parms.dp = dp; |
1764 | parms.port_no = port_no; | 1768 | parms.port_no = port_no; |
1765 | parms.upcall_portid = nla_get_u32(a[OVS_VPORT_ATTR_UPCALL_PID]); | 1769 | parms.upcall_portids = a[OVS_VPORT_ATTR_UPCALL_PID]; |
1766 | 1770 | ||
1767 | vport = new_vport(&parms); | 1771 | vport = new_vport(&parms); |
1768 | err = PTR_ERR(vport); | 1772 | err = PTR_ERR(vport); |
@@ -1812,8 +1816,14 @@ static int ovs_vport_cmd_set(struct sk_buff *skb, struct genl_info *info) | |||
1812 | goto exit_unlock_free; | 1816 | goto exit_unlock_free; |
1813 | } | 1817 | } |
1814 | 1818 | ||
1815 | if (a[OVS_VPORT_ATTR_UPCALL_PID]) | 1819 | |
1816 | vport->upcall_portid = nla_get_u32(a[OVS_VPORT_ATTR_UPCALL_PID]); | 1820 | if (a[OVS_VPORT_ATTR_UPCALL_PID]) { |
1821 | struct nlattr *ids = a[OVS_VPORT_ATTR_UPCALL_PID]; | ||
1822 | |||
1823 | err = ovs_vport_set_upcall_portids(vport, ids); | ||
1824 | if (err) | ||
1825 | goto exit_unlock_free; | ||
1826 | } | ||
1817 | 1827 | ||
1818 | err = ovs_vport_cmd_fill_info(vport, reply, info->snd_portid, | 1828 | err = ovs_vport_cmd_fill_info(vport, reply, info->snd_portid, |
1819 | info->snd_seq, 0, OVS_VPORT_CMD_NEW); | 1829 | info->snd_seq, 0, OVS_VPORT_CMD_NEW); |
diff --git a/net/openvswitch/vport-internal_dev.c b/net/openvswitch/vport-internal_dev.c index bd658555afdf..84516126e5f3 100644 --- a/net/openvswitch/vport-internal_dev.c +++ b/net/openvswitch/vport-internal_dev.c | |||
@@ -140,11 +140,14 @@ static void do_setup(struct net_device *netdev) | |||
140 | netdev->tx_queue_len = 0; | 140 | netdev->tx_queue_len = 0; |
141 | 141 | ||
142 | netdev->features = NETIF_F_LLTX | NETIF_F_SG | NETIF_F_FRAGLIST | | 142 | netdev->features = NETIF_F_LLTX | NETIF_F_SG | NETIF_F_FRAGLIST | |
143 | NETIF_F_HIGHDMA | NETIF_F_HW_CSUM | NETIF_F_GSO_SOFTWARE; | 143 | NETIF_F_HIGHDMA | NETIF_F_HW_CSUM | |
144 | NETIF_F_GSO_SOFTWARE | NETIF_F_GSO_ENCAP_ALL; | ||
144 | 145 | ||
145 | netdev->vlan_features = netdev->features; | 146 | netdev->vlan_features = netdev->features; |
147 | netdev->hw_enc_features = netdev->features; | ||
146 | netdev->features |= NETIF_F_HW_VLAN_CTAG_TX; | 148 | netdev->features |= NETIF_F_HW_VLAN_CTAG_TX; |
147 | netdev->hw_features = netdev->features & ~NETIF_F_LLTX; | 149 | netdev->hw_features = netdev->features & ~NETIF_F_LLTX; |
150 | |||
148 | eth_hw_addr_random(netdev); | 151 | eth_hw_addr_random(netdev); |
149 | } | 152 | } |
150 | 153 | ||
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 | /** |