diff options
author | David S. Miller <davem@davemloft.net> | 2014-07-25 02:18:21 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-07-25 02:18:21 -0400 |
commit | 9239079080fe364189d13020441c46f9f0e0071d (patch) | |
tree | 913a82ae0c7968736e6e2ef6211b744cd4787383 /net | |
parent | 10f29808605118bee8666d4b2c776c07d04cdc0a (diff) | |
parent | d9e0ecb81417c34ef8c02a6880d23c362300cda0 (diff) |
Merge branch 'net_next_ovs' of git://git.kernel.org/pub/scm/linux/kernel/git/pshelar/openvswitch
Pravin B Shelar says:
====================
Open vSwitch
Following patches adds three features to OVS
1. Add fairness to upcall processing.
2. Hash action.
3. Enable Tunnel GSO features.
Rest of patches are bug fixes related to patches from same series.
v2 series changes first patch according to comment from Dave Miller.
v3 series changes first patch according to comment from Nikolay Aleksandrov.
v4 series update recirc patch commit msg.
v5 series resolve conflict with net-next, updated recic action patch.
v6 series sends all patches.
v7 series drop recirc patches.
v8 series checkpatch fix
v9 series drop HASH action patch. update sample action commit msg.
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-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 |
5 files changed, 184 insertions, 25 deletions
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 | /** |