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) |