diff options
| author | Andy Zhou <azhou@nicira.com> | 2014-09-15 22:37:25 -0400 |
|---|---|---|
| committer | Pravin B Shelar <pshelar@nicira.com> | 2014-09-16 02:28:14 -0400 |
| commit | 971427f353f3c42c8dcef62e7124440df68eb809 (patch) | |
| tree | 7eed9ed50fe70cfc82ce33e12a13147ad0de580c /net/openvswitch | |
| parent | 32ae87ff795781b7ceffc44b7c694c1bb206a266 (diff) | |
openvswitch: Add recirc and hash action.
Recirc action allows a packet to reenter openvswitch processing.
currently openvswitch lookup flow for packet received and execute
set of actions on that packet, with help of recirc action we can
process/modify the packet and recirculate it back in openvswitch
for another pass.
OVS hash action calculates 5-tupple hash and set hash in flow-key
hash. This can be used along with recirculation for distributing
packets among different ports for bond devices.
For example:
OVS bonding can use following actions:
Match on: bond flow; Action: hash, recirc(id)
Match on: recirc-id == id and hash lower bits == a;
Action: output port_bond_a
Signed-off-by: Andy Zhou <azhou@nicira.com>
Acked-by: Jesse Gross <jesse@nicira.com>
Signed-off-by: Pravin B Shelar <pshelar@nicira.com>
Diffstat (limited to 'net/openvswitch')
| -rw-r--r-- | net/openvswitch/actions.c | 203 | ||||
| -rw-r--r-- | net/openvswitch/datapath.c | 11 | ||||
| -rw-r--r-- | net/openvswitch/datapath.h | 7 | ||||
| -rw-r--r-- | net/openvswitch/flow.c | 7 | ||||
| -rw-r--r-- | net/openvswitch/flow.h | 3 | ||||
| -rw-r--r-- | net/openvswitch/flow_netlink.c | 43 |
6 files changed, 262 insertions, 12 deletions
diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index c31bb80c984f..6932a42e41a2 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * Copyright (c) 2007-2013 Nicira, Inc. | 2 | * Copyright (c) 2007-2014 Nicira, Inc. |
| 3 | * | 3 | * |
| 4 | * This program is free software; you can redistribute it and/or | 4 | * This program is free software; you can redistribute it and/or |
| 5 | * modify it under the terms of version 2 of the GNU General Public | 5 | * modify it under the terms of version 2 of the GNU General Public |
| @@ -35,12 +35,78 @@ | |||
| 35 | #include <net/sctp/checksum.h> | 35 | #include <net/sctp/checksum.h> |
| 36 | 36 | ||
| 37 | #include "datapath.h" | 37 | #include "datapath.h" |
| 38 | #include "flow.h" | ||
| 38 | #include "vport.h" | 39 | #include "vport.h" |
| 39 | 40 | ||
| 40 | static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, | 41 | static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, |
| 41 | struct sw_flow_key *key, | 42 | struct sw_flow_key *key, |
| 42 | const struct nlattr *attr, int len); | 43 | const struct nlattr *attr, int len); |
| 43 | 44 | ||
| 45 | struct deferred_action { | ||
| 46 | struct sk_buff *skb; | ||
| 47 | const struct nlattr *actions; | ||
| 48 | |||
| 49 | /* Store pkt_key clone when creating deferred action. */ | ||
| 50 | struct sw_flow_key pkt_key; | ||
| 51 | }; | ||
| 52 | |||
| 53 | #define DEFERRED_ACTION_FIFO_SIZE 10 | ||
| 54 | struct action_fifo { | ||
| 55 | int head; | ||
| 56 | int tail; | ||
| 57 | /* Deferred action fifo queue storage. */ | ||
| 58 | struct deferred_action fifo[DEFERRED_ACTION_FIFO_SIZE]; | ||
| 59 | }; | ||
| 60 | |||
| 61 | static struct action_fifo __percpu *action_fifos; | ||
| 62 | static DEFINE_PER_CPU(int, exec_actions_level); | ||
| 63 | |||
| 64 | static void action_fifo_init(struct action_fifo *fifo) | ||
| 65 | { | ||
| 66 | fifo->head = 0; | ||
| 67 | fifo->tail = 0; | ||
| 68 | } | ||
| 69 | |||
| 70 | static bool action_fifo_is_empty(struct action_fifo *fifo) | ||
| 71 | { | ||
| 72 | return (fifo->head == fifo->tail); | ||
| 73 | } | ||
| 74 | |||
| 75 | static struct deferred_action *action_fifo_get(struct action_fifo *fifo) | ||
| 76 | { | ||
| 77 | if (action_fifo_is_empty(fifo)) | ||
| 78 | return NULL; | ||
| 79 | |||
| 80 | return &fifo->fifo[fifo->tail++]; | ||
| 81 | } | ||
| 82 | |||
| 83 | static struct deferred_action *action_fifo_put(struct action_fifo *fifo) | ||
| 84 | { | ||
| 85 | if (fifo->head >= DEFERRED_ACTION_FIFO_SIZE - 1) | ||
| 86 | return NULL; | ||
| 87 | |||
| 88 | return &fifo->fifo[fifo->head++]; | ||
| 89 | } | ||
| 90 | |||
| 91 | /* Return true if fifo is not full */ | ||
| 92 | static struct deferred_action *add_deferred_actions(struct sk_buff *skb, | ||
| 93 | struct sw_flow_key *key, | ||
| 94 | const struct nlattr *attr) | ||
| 95 | { | ||
| 96 | struct action_fifo *fifo; | ||
| 97 | struct deferred_action *da; | ||
| 98 | |||
| 99 | fifo = this_cpu_ptr(action_fifos); | ||
| 100 | da = action_fifo_put(fifo); | ||
| 101 | if (da) { | ||
| 102 | da->skb = skb; | ||
| 103 | da->actions = attr; | ||
| 104 | da->pkt_key = *key; | ||
| 105 | } | ||
| 106 | |||
| 107 | return da; | ||
| 108 | } | ||
| 109 | |||
| 44 | static int make_writable(struct sk_buff *skb, int write_len) | 110 | static int make_writable(struct sk_buff *skb, int write_len) |
| 45 | { | 111 | { |
| 46 | if (!pskb_may_pull(skb, write_len)) | 112 | if (!pskb_may_pull(skb, write_len)) |
| @@ -485,8 +551,29 @@ static int sample(struct datapath *dp, struct sk_buff *skb, | |||
| 485 | /* Skip the sample action when out of memory. */ | 551 | /* Skip the sample action when out of memory. */ |
| 486 | return 0; | 552 | return 0; |
| 487 | 553 | ||
| 488 | /* do_execute_actions() will consume the cloned skb. */ | 554 | if (!add_deferred_actions(skb, key, a)) { |
| 489 | return do_execute_actions(dp, skb, key, a, rem); | 555 | if (net_ratelimit()) |
| 556 | pr_warn("%s: deferred actions limit reached, dropping sample action\n", | ||
| 557 | ovs_dp_name(dp)); | ||
| 558 | |||
| 559 | kfree_skb(skb); | ||
| 560 | } | ||
| 561 | return 0; | ||
| 562 | } | ||
| 563 | |||
| 564 | static void execute_hash(struct sk_buff *skb, struct sw_flow_key *key, | ||
| 565 | const struct nlattr *attr) | ||
| 566 | { | ||
| 567 | struct ovs_action_hash *hash_act = nla_data(attr); | ||
| 568 | u32 hash = 0; | ||
| 569 | |||
| 570 | /* OVS_HASH_ALG_L4 is the only possible hash algorithm. */ | ||
| 571 | hash = skb_get_hash(skb); | ||
| 572 | hash = jhash_1word(hash, hash_act->hash_basis); | ||
| 573 | if (!hash) | ||
| 574 | hash = 0x1; | ||
| 575 | |||
| 576 | key->ovs_flow_hash = hash; | ||
| 490 | } | 577 | } |
| 491 | 578 | ||
| 492 | static int execute_set_action(struct sk_buff *skb, | 579 | static int execute_set_action(struct sk_buff *skb, |
| @@ -535,6 +622,44 @@ static int execute_set_action(struct sk_buff *skb, | |||
| 535 | return err; | 622 | return err; |
| 536 | } | 623 | } |
| 537 | 624 | ||
| 625 | static int execute_recirc(struct datapath *dp, struct sk_buff *skb, | ||
| 626 | struct sw_flow_key *key, | ||
| 627 | const struct nlattr *a, int rem) | ||
| 628 | { | ||
| 629 | struct deferred_action *da; | ||
| 630 | int err; | ||
| 631 | |||
| 632 | err = ovs_flow_key_update(skb, key); | ||
| 633 | if (err) | ||
| 634 | return err; | ||
| 635 | |||
| 636 | if (!last_action(a, rem)) { | ||
| 637 | /* Recirc action is the not the last action | ||
| 638 | * of the action list, need to clone the skb. | ||
| 639 | */ | ||
| 640 | skb = skb_clone(skb, GFP_ATOMIC); | ||
| 641 | |||
| 642 | /* Skip the recirc action when out of memory, but | ||
| 643 | * continue on with the rest of the action list. | ||
| 644 | */ | ||
| 645 | if (!skb) | ||
| 646 | return 0; | ||
| 647 | } | ||
| 648 | |||
| 649 | da = add_deferred_actions(skb, key, NULL); | ||
| 650 | if (da) { | ||
| 651 | da->pkt_key.recirc_id = nla_get_u32(a); | ||
| 652 | } else { | ||
| 653 | kfree_skb(skb); | ||
| 654 | |||
| 655 | if (net_ratelimit()) | ||
| 656 | pr_warn("%s: deferred action limit reached, drop recirc action\n", | ||
| 657 | ovs_dp_name(dp)); | ||
| 658 | } | ||
| 659 | |||
| 660 | return 0; | ||
| 661 | } | ||
| 662 | |||
| 538 | /* Execute a list of actions against 'skb'. */ | 663 | /* Execute a list of actions against 'skb'. */ |
| 539 | static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, | 664 | static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, |
| 540 | struct sw_flow_key *key, | 665 | struct sw_flow_key *key, |
| @@ -566,6 +691,10 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, | |||
| 566 | output_userspace(dp, skb, key, a); | 691 | output_userspace(dp, skb, key, a); |
| 567 | break; | 692 | break; |
| 568 | 693 | ||
| 694 | case OVS_ACTION_ATTR_HASH: | ||
| 695 | execute_hash(skb, key, a); | ||
| 696 | break; | ||
| 697 | |||
| 569 | case OVS_ACTION_ATTR_PUSH_VLAN: | 698 | case OVS_ACTION_ATTR_PUSH_VLAN: |
| 570 | err = push_vlan(skb, nla_data(a)); | 699 | err = push_vlan(skb, nla_data(a)); |
| 571 | if (unlikely(err)) /* skb already freed. */ | 700 | if (unlikely(err)) /* skb already freed. */ |
| @@ -576,6 +705,17 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, | |||
| 576 | err = pop_vlan(skb); | 705 | err = pop_vlan(skb); |
| 577 | break; | 706 | break; |
| 578 | 707 | ||
| 708 | case OVS_ACTION_ATTR_RECIRC: | ||
| 709 | err = execute_recirc(dp, skb, key, a, rem); | ||
| 710 | if (last_action(a, rem)) { | ||
| 711 | /* If this is the last action, the skb has | ||
| 712 | * been consumed or freed. | ||
| 713 | * Return immediately. | ||
| 714 | */ | ||
| 715 | return err; | ||
| 716 | } | ||
| 717 | break; | ||
| 718 | |||
| 579 | case OVS_ACTION_ATTR_SET: | 719 | case OVS_ACTION_ATTR_SET: |
| 580 | err = execute_set_action(skb, nla_data(a)); | 720 | err = execute_set_action(skb, nla_data(a)); |
| 581 | break; | 721 | break; |
| @@ -601,12 +741,63 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, | |||
| 601 | return 0; | 741 | return 0; |
| 602 | } | 742 | } |
| 603 | 743 | ||
| 744 | static void process_deferred_actions(struct datapath *dp) | ||
| 745 | { | ||
| 746 | struct action_fifo *fifo = this_cpu_ptr(action_fifos); | ||
| 747 | |||
| 748 | /* Do not touch the FIFO in case there is no deferred actions. */ | ||
| 749 | if (action_fifo_is_empty(fifo)) | ||
| 750 | return; | ||
| 751 | |||
| 752 | /* Finishing executing all deferred actions. */ | ||
| 753 | do { | ||
| 754 | struct deferred_action *da = action_fifo_get(fifo); | ||
| 755 | struct sk_buff *skb = da->skb; | ||
| 756 | struct sw_flow_key *key = &da->pkt_key; | ||
| 757 | const struct nlattr *actions = da->actions; | ||
| 758 | |||
| 759 | if (actions) | ||
| 760 | do_execute_actions(dp, skb, key, actions, | ||
| 761 | nla_len(actions)); | ||
| 762 | else | ||
| 763 | ovs_dp_process_packet(skb, key); | ||
| 764 | } while (!action_fifo_is_empty(fifo)); | ||
| 765 | |||
| 766 | /* Reset FIFO for the next packet. */ | ||
| 767 | action_fifo_init(fifo); | ||
| 768 | } | ||
| 769 | |||
| 604 | /* Execute a list of actions against 'skb'. */ | 770 | /* Execute a list of actions against 'skb'. */ |
| 605 | int ovs_execute_actions(struct datapath *dp, struct sk_buff *skb, | 771 | int ovs_execute_actions(struct datapath *dp, struct sk_buff *skb, |
| 606 | struct sw_flow_key *key) | 772 | struct sw_flow_key *key) |
| 607 | { | 773 | { |
| 608 | struct sw_flow_actions *acts = rcu_dereference(OVS_CB(skb)->flow->sf_acts); | 774 | int level = this_cpu_read(exec_actions_level); |
| 775 | struct sw_flow_actions *acts; | ||
| 776 | int err; | ||
| 777 | |||
| 778 | acts = rcu_dereference(OVS_CB(skb)->flow->sf_acts); | ||
| 779 | |||
| 780 | this_cpu_inc(exec_actions_level); | ||
| 781 | err = do_execute_actions(dp, skb, key, | ||
| 782 | acts->actions, acts->actions_len); | ||
| 783 | |||
| 784 | if (!level) | ||
| 785 | process_deferred_actions(dp); | ||
| 786 | |||
| 787 | this_cpu_dec(exec_actions_level); | ||
| 788 | return err; | ||
| 789 | } | ||
| 790 | |||
| 791 | int action_fifos_init(void) | ||
| 792 | { | ||
| 793 | action_fifos = alloc_percpu(struct action_fifo); | ||
| 794 | if (!action_fifos) | ||
| 795 | return -ENOMEM; | ||
| 609 | 796 | ||
| 610 | return do_execute_actions(dp, skb, key, | 797 | return 0; |
| 611 | acts->actions, acts->actions_len); | 798 | } |
| 799 | |||
| 800 | void action_fifos_exit(void) | ||
| 801 | { | ||
| 802 | free_percpu(action_fifos); | ||
| 612 | } | 803 | } |
diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index 7e0819919b8a..16cad14fa81e 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c | |||
| @@ -156,7 +156,7 @@ static struct datapath *get_dp(struct net *net, int dp_ifindex) | |||
| 156 | } | 156 | } |
| 157 | 157 | ||
| 158 | /* Must be called with rcu_read_lock or ovs_mutex. */ | 158 | /* Must be called with rcu_read_lock or ovs_mutex. */ |
| 159 | static const char *ovs_dp_name(const struct datapath *dp) | 159 | const char *ovs_dp_name(const struct datapath *dp) |
| 160 | { | 160 | { |
| 161 | struct vport *vport = ovs_vport_ovsl_rcu(dp, OVSP_LOCAL); | 161 | struct vport *vport = ovs_vport_ovsl_rcu(dp, OVSP_LOCAL); |
| 162 | return vport->ops->get_name(vport); | 162 | return vport->ops->get_name(vport); |
| @@ -2065,10 +2065,14 @@ static int __init dp_init(void) | |||
| 2065 | 2065 | ||
| 2066 | pr_info("Open vSwitch switching datapath\n"); | 2066 | pr_info("Open vSwitch switching datapath\n"); |
| 2067 | 2067 | ||
| 2068 | err = ovs_internal_dev_rtnl_link_register(); | 2068 | err = action_fifos_init(); |
| 2069 | if (err) | 2069 | if (err) |
| 2070 | goto error; | 2070 | goto error; |
| 2071 | 2071 | ||
| 2072 | err = ovs_internal_dev_rtnl_link_register(); | ||
| 2073 | if (err) | ||
| 2074 | goto error_action_fifos_exit; | ||
| 2075 | |||
| 2072 | err = ovs_flow_init(); | 2076 | err = ovs_flow_init(); |
| 2073 | if (err) | 2077 | if (err) |
| 2074 | goto error_unreg_rtnl_link; | 2078 | goto error_unreg_rtnl_link; |
| @@ -2101,6 +2105,8 @@ error_flow_exit: | |||
| 2101 | ovs_flow_exit(); | 2105 | ovs_flow_exit(); |
| 2102 | error_unreg_rtnl_link: | 2106 | error_unreg_rtnl_link: |
| 2103 | ovs_internal_dev_rtnl_link_unregister(); | 2107 | ovs_internal_dev_rtnl_link_unregister(); |
| 2108 | error_action_fifos_exit: | ||
| 2109 | action_fifos_exit(); | ||
| 2104 | error: | 2110 | error: |
| 2105 | return err; | 2111 | return err; |
| 2106 | } | 2112 | } |
| @@ -2114,6 +2120,7 @@ static void dp_cleanup(void) | |||
| 2114 | ovs_vport_exit(); | 2120 | ovs_vport_exit(); |
| 2115 | ovs_flow_exit(); | 2121 | ovs_flow_exit(); |
| 2116 | ovs_internal_dev_rtnl_link_unregister(); | 2122 | ovs_internal_dev_rtnl_link_unregister(); |
| 2123 | action_fifos_exit(); | ||
| 2117 | } | 2124 | } |
| 2118 | 2125 | ||
| 2119 | module_init(dp_init); | 2126 | module_init(dp_init); |
diff --git a/net/openvswitch/datapath.h b/net/openvswitch/datapath.h index 25b0e888cb27..ac3f3df96961 100644 --- a/net/openvswitch/datapath.h +++ b/net/openvswitch/datapath.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * Copyright (c) 2007-2012 Nicira, Inc. | 2 | * Copyright (c) 2007-2014 Nicira, Inc. |
| 3 | * | 3 | * |
| 4 | * This program is free software; you can redistribute it and/or | 4 | * This program is free software; you can redistribute it and/or |
| 5 | * modify it under the terms of version 2 of the GNU General Public | 5 | * modify it under the terms of version 2 of the GNU General Public |
| @@ -189,13 +189,18 @@ void ovs_dp_detach_port(struct vport *); | |||
| 189 | int ovs_dp_upcall(struct datapath *, struct sk_buff *, | 189 | int ovs_dp_upcall(struct datapath *, struct sk_buff *, |
| 190 | const struct dp_upcall_info *); | 190 | const struct dp_upcall_info *); |
| 191 | 191 | ||
| 192 | const char *ovs_dp_name(const struct datapath *dp); | ||
| 192 | struct sk_buff *ovs_vport_cmd_build_info(struct vport *, u32 pid, u32 seq, | 193 | struct sk_buff *ovs_vport_cmd_build_info(struct vport *, u32 pid, u32 seq, |
| 193 | u8 cmd); | 194 | u8 cmd); |
| 194 | 195 | ||
| 195 | int ovs_execute_actions(struct datapath *dp, struct sk_buff *skb, | 196 | int ovs_execute_actions(struct datapath *dp, struct sk_buff *skb, |
| 196 | struct sw_flow_key *); | 197 | struct sw_flow_key *); |
| 198 | |||
| 197 | void ovs_dp_notify_wq(struct work_struct *work); | 199 | void ovs_dp_notify_wq(struct work_struct *work); |
| 198 | 200 | ||
| 201 | int action_fifos_init(void); | ||
| 202 | void action_fifos_exit(void); | ||
| 203 | |||
| 199 | #define OVS_NLERR(fmt, ...) \ | 204 | #define OVS_NLERR(fmt, ...) \ |
| 200 | do { \ | 205 | do { \ |
| 201 | if (net_ratelimit()) \ | 206 | if (net_ratelimit()) \ |
diff --git a/net/openvswitch/flow.c b/net/openvswitch/flow.c index bf8442071d75..4010423f2831 100644 --- a/net/openvswitch/flow.c +++ b/net/openvswitch/flow.c | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * Copyright (c) 2007-2013 Nicira, Inc. | 2 | * Copyright (c) 2007-2014 Nicira, Inc. |
| 3 | * | 3 | * |
| 4 | * This program is free software; you can redistribute it and/or | 4 | * This program is free software; you can redistribute it and/or |
| 5 | * modify it under the terms of version 2 of the GNU General Public | 5 | * modify it under the terms of version 2 of the GNU General Public |
| @@ -606,6 +606,11 @@ static int key_extract(struct sk_buff *skb, struct sw_flow_key *key) | |||
| 606 | return 0; | 606 | return 0; |
| 607 | } | 607 | } |
| 608 | 608 | ||
| 609 | int ovs_flow_key_update(struct sk_buff *skb, struct sw_flow_key *key) | ||
| 610 | { | ||
| 611 | return key_extract(skb, key); | ||
| 612 | } | ||
| 613 | |||
| 609 | int ovs_flow_key_extract(struct ovs_key_ipv4_tunnel *tun_key, | 614 | int ovs_flow_key_extract(struct ovs_key_ipv4_tunnel *tun_key, |
| 610 | struct sk_buff *skb, struct sw_flow_key *key) | 615 | struct sk_buff *skb, struct sw_flow_key *key) |
| 611 | { | 616 | { |
diff --git a/net/openvswitch/flow.h b/net/openvswitch/flow.h index 3869a540365c..0f5db4ec565d 100644 --- a/net/openvswitch/flow.h +++ b/net/openvswitch/flow.h | |||
| @@ -72,6 +72,8 @@ struct sw_flow_key { | |||
| 72 | u32 skb_mark; /* SKB mark. */ | 72 | u32 skb_mark; /* SKB mark. */ |
| 73 | u16 in_port; /* Input switch port (or DP_MAX_PORTS). */ | 73 | u16 in_port; /* Input switch port (or DP_MAX_PORTS). */ |
| 74 | } __packed phy; /* Safe when right after 'tun_key'. */ | 74 | } __packed phy; /* Safe when right after 'tun_key'. */ |
| 75 | u32 ovs_flow_hash; /* Datapath computed hash value. */ | ||
| 76 | u32 recirc_id; /* Recirculation ID. */ | ||
| 75 | struct { | 77 | struct { |
| 76 | u8 src[ETH_ALEN]; /* Ethernet source address. */ | 78 | u8 src[ETH_ALEN]; /* Ethernet source address. */ |
| 77 | u8 dst[ETH_ALEN]; /* Ethernet destination address. */ | 79 | u8 dst[ETH_ALEN]; /* Ethernet destination address. */ |
| @@ -187,6 +189,7 @@ void ovs_flow_stats_get(const struct sw_flow *, struct ovs_flow_stats *, | |||
| 187 | void ovs_flow_stats_clear(struct sw_flow *); | 189 | void ovs_flow_stats_clear(struct sw_flow *); |
| 188 | u64 ovs_flow_used_time(unsigned long flow_jiffies); | 190 | u64 ovs_flow_used_time(unsigned long flow_jiffies); |
| 189 | 191 | ||
| 192 | int ovs_flow_key_update(struct sk_buff *skb, struct sw_flow_key *key); | ||
| 190 | int ovs_flow_key_extract(struct ovs_key_ipv4_tunnel *tun_key, | 193 | int ovs_flow_key_extract(struct ovs_key_ipv4_tunnel *tun_key, |
| 191 | struct sk_buff *skb, struct sw_flow_key *key); | 194 | struct sk_buff *skb, struct sw_flow_key *key); |
| 192 | /* Extract key from packet coming from userspace. */ | 195 | /* Extract key from packet coming from userspace. */ |
diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c index 630b320fbf3e..f4c8daa73965 100644 --- a/net/openvswitch/flow_netlink.c +++ b/net/openvswitch/flow_netlink.c | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * Copyright (c) 2007-2013 Nicira, Inc. | 2 | * Copyright (c) 2007-2014 Nicira, Inc. |
| 3 | * | 3 | * |
| 4 | * This program is free software; you can redistribute it and/or | 4 | * This program is free software; you can redistribute it and/or |
| 5 | * modify it under the terms of version 2 of the GNU General Public | 5 | * modify it under the terms of version 2 of the GNU General Public |
| @@ -251,6 +251,8 @@ static const int ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = { | |||
| 251 | [OVS_KEY_ATTR_ICMPV6] = sizeof(struct ovs_key_icmpv6), | 251 | [OVS_KEY_ATTR_ICMPV6] = sizeof(struct ovs_key_icmpv6), |
| 252 | [OVS_KEY_ATTR_ARP] = sizeof(struct ovs_key_arp), | 252 | [OVS_KEY_ATTR_ARP] = sizeof(struct ovs_key_arp), |
| 253 | [OVS_KEY_ATTR_ND] = sizeof(struct ovs_key_nd), | 253 | [OVS_KEY_ATTR_ND] = sizeof(struct ovs_key_nd), |
| 254 | [OVS_KEY_ATTR_RECIRC_ID] = sizeof(u32), | ||
| 255 | [OVS_KEY_ATTR_DP_HASH] = sizeof(u32), | ||
| 254 | [OVS_KEY_ATTR_TUNNEL] = -1, | 256 | [OVS_KEY_ATTR_TUNNEL] = -1, |
| 255 | }; | 257 | }; |
| 256 | 258 | ||
| @@ -454,6 +456,20 @@ static int ipv4_tun_to_nlattr(struct sk_buff *skb, | |||
| 454 | static int metadata_from_nlattrs(struct sw_flow_match *match, u64 *attrs, | 456 | static int metadata_from_nlattrs(struct sw_flow_match *match, u64 *attrs, |
| 455 | const struct nlattr **a, bool is_mask) | 457 | const struct nlattr **a, bool is_mask) |
| 456 | { | 458 | { |
| 459 | if (*attrs & (1 << OVS_KEY_ATTR_DP_HASH)) { | ||
| 460 | u32 hash_val = nla_get_u32(a[OVS_KEY_ATTR_DP_HASH]); | ||
| 461 | |||
| 462 | SW_FLOW_KEY_PUT(match, ovs_flow_hash, hash_val, is_mask); | ||
| 463 | *attrs &= ~(1 << OVS_KEY_ATTR_DP_HASH); | ||
| 464 | } | ||
| 465 | |||
| 466 | if (*attrs & (1 << OVS_KEY_ATTR_RECIRC_ID)) { | ||
| 467 | u32 recirc_id = nla_get_u32(a[OVS_KEY_ATTR_RECIRC_ID]); | ||
| 468 | |||
| 469 | SW_FLOW_KEY_PUT(match, recirc_id, recirc_id, is_mask); | ||
| 470 | *attrs &= ~(1 << OVS_KEY_ATTR_RECIRC_ID); | ||
| 471 | } | ||
| 472 | |||
| 457 | if (*attrs & (1 << OVS_KEY_ATTR_PRIORITY)) { | 473 | if (*attrs & (1 << OVS_KEY_ATTR_PRIORITY)) { |
| 458 | SW_FLOW_KEY_PUT(match, phy.priority, | 474 | SW_FLOW_KEY_PUT(match, phy.priority, |
| 459 | nla_get_u32(a[OVS_KEY_ATTR_PRIORITY]), is_mask); | 475 | nla_get_u32(a[OVS_KEY_ATTR_PRIORITY]), is_mask); |
| @@ -873,6 +889,12 @@ int ovs_nla_put_flow(const struct sw_flow_key *swkey, | |||
| 873 | struct nlattr *nla, *encap; | 889 | struct nlattr *nla, *encap; |
| 874 | bool is_mask = (swkey != output); | 890 | bool is_mask = (swkey != output); |
| 875 | 891 | ||
| 892 | if (nla_put_u32(skb, OVS_KEY_ATTR_RECIRC_ID, output->recirc_id)) | ||
| 893 | goto nla_put_failure; | ||
| 894 | |||
| 895 | if (nla_put_u32(skb, OVS_KEY_ATTR_DP_HASH, output->ovs_flow_hash)) | ||
| 896 | goto nla_put_failure; | ||
| 897 | |||
| 876 | if (nla_put_u32(skb, OVS_KEY_ATTR_PRIORITY, output->phy.priority)) | 898 | if (nla_put_u32(skb, OVS_KEY_ATTR_PRIORITY, output->phy.priority)) |
| 877 | goto nla_put_failure; | 899 | goto nla_put_failure; |
| 878 | 900 | ||
| @@ -1401,11 +1423,13 @@ int ovs_nla_copy_actions(const struct nlattr *attr, | |||
| 1401 | /* Expected argument lengths, (u32)-1 for variable length. */ | 1423 | /* Expected argument lengths, (u32)-1 for variable length. */ |
| 1402 | static const u32 action_lens[OVS_ACTION_ATTR_MAX + 1] = { | 1424 | static const u32 action_lens[OVS_ACTION_ATTR_MAX + 1] = { |
| 1403 | [OVS_ACTION_ATTR_OUTPUT] = sizeof(u32), | 1425 | [OVS_ACTION_ATTR_OUTPUT] = sizeof(u32), |
| 1426 | [OVS_ACTION_ATTR_RECIRC] = sizeof(u32), | ||
| 1404 | [OVS_ACTION_ATTR_USERSPACE] = (u32)-1, | 1427 | [OVS_ACTION_ATTR_USERSPACE] = (u32)-1, |
| 1405 | [OVS_ACTION_ATTR_PUSH_VLAN] = sizeof(struct ovs_action_push_vlan), | 1428 | [OVS_ACTION_ATTR_PUSH_VLAN] = sizeof(struct ovs_action_push_vlan), |
| 1406 | [OVS_ACTION_ATTR_POP_VLAN] = 0, | 1429 | [OVS_ACTION_ATTR_POP_VLAN] = 0, |
| 1407 | [OVS_ACTION_ATTR_SET] = (u32)-1, | 1430 | [OVS_ACTION_ATTR_SET] = (u32)-1, |
| 1408 | [OVS_ACTION_ATTR_SAMPLE] = (u32)-1 | 1431 | [OVS_ACTION_ATTR_SAMPLE] = (u32)-1, |
| 1432 | [OVS_ACTION_ATTR_HASH] = sizeof(struct ovs_action_hash) | ||
| 1409 | }; | 1433 | }; |
| 1410 | const struct ovs_action_push_vlan *vlan; | 1434 | const struct ovs_action_push_vlan *vlan; |
| 1411 | int type = nla_type(a); | 1435 | int type = nla_type(a); |
| @@ -1432,6 +1456,18 @@ int ovs_nla_copy_actions(const struct nlattr *attr, | |||
| 1432 | return -EINVAL; | 1456 | return -EINVAL; |
| 1433 | break; | 1457 | break; |
| 1434 | 1458 | ||
| 1459 | case OVS_ACTION_ATTR_HASH: { | ||
| 1460 | const struct ovs_action_hash *act_hash = nla_data(a); | ||
| 1461 | |||
| 1462 | switch (act_hash->hash_alg) { | ||
| 1463 | case OVS_HASH_ALG_L4: | ||
| 1464 | break; | ||
| 1465 | default: | ||
| 1466 | return -EINVAL; | ||
| 1467 | } | ||
| 1468 | |||
| 1469 | break; | ||
| 1470 | } | ||
| 1435 | 1471 | ||
| 1436 | case OVS_ACTION_ATTR_POP_VLAN: | 1472 | case OVS_ACTION_ATTR_POP_VLAN: |
| 1437 | break; | 1473 | break; |
| @@ -1444,6 +1480,9 @@ int ovs_nla_copy_actions(const struct nlattr *attr, | |||
| 1444 | return -EINVAL; | 1480 | return -EINVAL; |
| 1445 | break; | 1481 | break; |
| 1446 | 1482 | ||
| 1483 | case OVS_ACTION_ATTR_RECIRC: | ||
| 1484 | break; | ||
| 1485 | |||
| 1447 | case OVS_ACTION_ATTR_SET: | 1486 | case OVS_ACTION_ATTR_SET: |
| 1448 | err = validate_set(a, key, sfa, &skip_copy); | 1487 | err = validate_set(a, key, sfa, &skip_copy); |
| 1449 | if (err) | 1488 | if (err) |
