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
Unified · Split
| @@ -289,6 +289,9 @@ enum ovs_key_attr { · 288 unmodified lines | ||
| 289 | 289 | OVS_KEY_ATTR_TUNNEL, /* Nested set of ovs_tunnel attributes */ |
| 290 | 290 | OVS_KEY_ATTR_SCTP, /* struct ovs_key_sctp */ |
| 291 | 291 | OVS_KEY_ATTR_TCP_FLAGS, /* be16 TCP flags. */ |
| 292 | + OVS_KEY_ATTR_DP_HASH, /* u32 hash value. Value 0 indicates the hash | |
| 293 | + is not computed by the datapath. */ | |
| 294 | + OVS_KEY_ATTR_RECIRC_ID, /* u32 recirc id */ | |
| 292 | 295 | |
| 293 | 296 | #ifdef __KERNEL__ |
| 294 | 297 | OVS_KEY_ATTR_IPV4_TUNNEL, /* struct ovs_key_ipv4_tunnel */ |
| @@ -493,6 +496,27 @@ struct ovs_action_push_vlan { · 198 unmodified lines | ||
| 493 | 496 | __be16 vlan_tci; /* 802.1Q TCI (VLAN ID and priority). */ |
| 494 | 497 | }; |
| 495 | 498 | |
| 499 | +/* Data path hash algorithm for computing Datapath hash. | |
| 500 | + * | |
| 501 | + * The algorithm type only specifies the fields in a flow | |
| 502 | + * will be used as part of the hash. Each datapath is free | |
| 503 | + * to use its own hash algorithm. The hash value will be | |
| 504 | + * opaque to the user space daemon. | |
| 505 | + */ | |
| 506 | +enum ovs_hash_alg { | |
| 507 | + OVS_HASH_ALG_L4, | |
| 508 | +}; | |
| 509 | + | |
| 510 | +/* | |
| 511 | + * struct ovs_action_hash - %OVS_ACTION_ATTR_HASH action argument. | |
| 512 | + * @hash_alg: Algorithm used to compute hash prior to recirculation. | |
| 513 | + * @hash_basis: basis used for computing hash. | |
| 514 | + */ | |
| 515 | +struct ovs_action_hash { | |
| 516 | + uint32_t hash_alg; /* One of ovs_hash_alg. */ | |
| 517 | + uint32_t hash_basis; | |
| 518 | +}; | |
| 519 | + | |
| 496 | 520 | /** |
| 497 | 521 | * enum ovs_action_attr - Action types. |
| 498 | 522 | * |
| @@ -521,6 +545,8 @@ enum ovs_action_attr { · 22 unmodified lines | ||
| 521 | 545 | OVS_ACTION_ATTR_PUSH_VLAN, /* struct ovs_action_push_vlan. */ |
| 522 | 546 | OVS_ACTION_ATTR_POP_VLAN, /* No argument. */ |
| 523 | 547 | OVS_ACTION_ATTR_SAMPLE, /* Nested OVS_SAMPLE_ATTR_*. */ |
| 548 | + OVS_ACTION_ATTR_RECIRC, /* u32 recirc_id. */ | |
| 549 | + OVS_ACTION_ATTR_HASH, /* struct ovs_action_hash. */ | |
| 524 | 550 | __OVS_ACTION_ATTR_MAX |
| 525 | 551 | }; |
| 526 | 552 | |
| @@ -1,5 +1,5 @@ | ||
| 1 | 1 | /* |
| 2 | − * Copyright (c) 2007-2013 Nicira, Inc. | |
| 2 | + * Copyright (c) 2007-2014 Nicira, Inc. | |
| 3 | 3 | * |
| 4 | 4 | * This program is free software; you can redistribute it and/or |
| 5 | 5 | * modify it under the terms of version 2 of the GNU General Public |
| @@ -35,12 +35,78 @@ · 29 unmodified lines | ||
| 35 | 35 | #include <net/sctp/checksum.h> |
| 36 | 36 | |
| 37 | 37 | #include "datapath.h" |
| 38 | +#include "flow.h" | |
| 38 | 39 | #include "vport.h" |
| 39 | 40 | |
| 40 | 41 | static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, |
| 41 | 42 | struct sw_flow_key *key, |
| 42 | 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 | 110 | static int make_writable(struct sk_buff *skb, int write_len) |
| 45 | 111 | { |
| 46 | 112 | if (!pskb_may_pull(skb, write_len)) |
| @@ -485,8 +551,29 @@ static int sample(struct datapath *dp, struct sk_buff *skb, · 438 unmodified lines | ||
| 485 | 551 | /* Skip the sample action when out of memory. */ |
| 486 | 552 | return 0; |
| 487 | 553 | |
| 488 | − /* do_execute_actions() will consume the cloned skb. */ | |
| 489 | − return do_execute_actions(dp, skb, key, a, rem); | |
| 554 | + if (!add_deferred_actions(skb, key, a)) { | |
| 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 | 579 | static int execute_set_action(struct sk_buff *skb, |
| @@ -535,6 +622,44 @@ static int execute_set_action(struct sk_buff *skb, · 42 unmodified lines | ||
| 535 | 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 | 663 | /* Execute a list of actions against 'skb'. */ |
| 539 | 664 | static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, |
| 540 | 665 | struct sw_flow_key *key, |
| @@ -566,6 +691,10 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, · 25 unmodified lines | ||
| 566 | 691 | output_userspace(dp, skb, key, a); |
| 567 | 692 | break; |
| 568 | 693 | |
| 694 | + case OVS_ACTION_ATTR_HASH: | |
| 695 | + execute_hash(skb, key, a); | |
| 696 | + break; | |
| 697 | + | |
| 569 | 698 | case OVS_ACTION_ATTR_PUSH_VLAN: |
| 570 | 699 | err = push_vlan(skb, nla_data(a)); |
| 571 | 700 | if (unlikely(err)) /* skb already freed. */ |
| @@ -576,6 +705,17 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, · 4 unmodified lines | ||
| 576 | 705 | err = pop_vlan(skb); |
| 577 | 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 | 719 | case OVS_ACTION_ATTR_SET: |
| 580 | 720 | err = execute_set_action(skb, nla_data(a)); |
| 581 | 721 | break; |
| @@ -601,12 +741,63 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, · 19 unmodified lines | ||
| 601 | 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 | 770 | /* Execute a list of actions against 'skb'. */ |
| 605 | 771 | int ovs_execute_actions(struct datapath *dp, struct sk_buff *skb, |
| 606 | 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, | |
| 611 | − acts->actions, acts->actions_len); | |
| 797 | + return 0; | |
| 798 | +} | |
| 799 | + | |
| 800 | +void action_fifos_exit(void) | |
| 801 | +{ | |
| 802 | + free_percpu(action_fifos); | |
| 612 | 803 | } |
| @@ -156,7 +156,7 @@ static struct datapath *get_dp(struct net *net, int dp_ifindex) · 155 unmodified lines | ||
| 156 | 156 | } |
| 157 | 157 | |
| 158 | 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 | 161 | struct vport *vport = ovs_vport_ovsl_rcu(dp, OVSP_LOCAL); |
| 162 | 162 | return vport->ops->get_name(vport); |
| @@ -2065,10 +2065,14 @@ static int __init dp_init(void) · 1902 unmodified lines | ||
| 2065 | 2065 | |
| 2066 | 2066 | pr_info("Open vSwitch switching datapath\n"); |
| 2067 | 2067 | |
| 2068 | − err = ovs_internal_dev_rtnl_link_register(); | |
| 2068 | + err = action_fifos_init(); | |
| 2069 | 2069 | if (err) |
| 2070 | 2070 | goto error; |
| 2071 | 2071 | |
| 2072 | + err = ovs_internal_dev_rtnl_link_register(); | |
| 2073 | + if (err) | |
| 2074 | + goto error_action_fifos_exit; | |
| 2075 | + | |
| 2072 | 2076 | err = ovs_flow_init(); |
| 2073 | 2077 | if (err) |
| 2074 | 2078 | goto error_unreg_rtnl_link; |
| @@ -2101,6 +2105,8 @@ error_flow_exit: · 26 unmodified lines | ||
| 2101 | 2105 | ovs_flow_exit(); |
| 2102 | 2106 | error_unreg_rtnl_link: |
| 2103 | 2107 | ovs_internal_dev_rtnl_link_unregister(); |
| 2108 | +error_action_fifos_exit: | |
| 2109 | + action_fifos_exit(); | |
| 2104 | 2110 | error: |
| 2105 | 2111 | return err; |
| 2106 | 2112 | } |
| @@ -2114,6 +2120,7 @@ static void dp_cleanup(void) · 7 unmodified lines | ||
| 2114 | 2120 | ovs_vport_exit(); |
| 2115 | 2121 | ovs_flow_exit(); |
| 2116 | 2122 | ovs_internal_dev_rtnl_link_unregister(); |
| 2123 | + action_fifos_exit(); | |
| 2117 | 2124 | } |
| 2118 | 2125 | |
| 2119 | 2126 | module_init(dp_init); |
| @@ -1,5 +1,5 @@ | ||
| 1 | 1 | /* |
| 2 | − * Copyright (c) 2007-2012 Nicira, Inc. | |
| 2 | + * Copyright (c) 2007-2014 Nicira, Inc. | |
| 3 | 3 | * |
| 4 | 4 | * This program is free software; you can redistribute it and/or |
| 5 | 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 *); · 183 unmodified lines | ||
| 189 | 189 | int ovs_dp_upcall(struct datapath *, struct sk_buff *, |
| 190 | 190 | const struct dp_upcall_info *); |
| 191 | 191 | |
| 192 | +const char *ovs_dp_name(const struct datapath *dp); | |
| 192 | 193 | struct sk_buff *ovs_vport_cmd_build_info(struct vport *, u32 pid, u32 seq, |
| 193 | 194 | u8 cmd); |
| 194 | 195 | |
| 195 | 196 | int ovs_execute_actions(struct datapath *dp, struct sk_buff *skb, |
| 196 | 197 | struct sw_flow_key *); |
| 198 | + | |
| 197 | 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 | 204 | #define OVS_NLERR(fmt, ...) \ |
| 200 | 205 | do { \ |
| 201 | 206 | if (net_ratelimit()) \ |
| @@ -1,5 +1,5 @@ | ||
| 1 | 1 | /* |
| 2 | − * Copyright (c) 2007-2013 Nicira, Inc. | |
| 2 | + * Copyright (c) 2007-2014 Nicira, Inc. | |
| 3 | 3 | * |
| 4 | 4 | * This program is free software; you can redistribute it and/or |
| 5 | 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) · 600 unmodified lines | ||
| 606 | 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 | 614 | int ovs_flow_key_extract(struct ovs_key_ipv4_tunnel *tun_key, |
| 610 | 615 | struct sk_buff *skb, struct sw_flow_key *key) |
| 611 | 616 | { |
| @@ -72,6 +72,8 @@ struct sw_flow_key { · 71 unmodified lines | ||
| 72 | 72 | u32 skb_mark; /* SKB mark. */ |
| 73 | 73 | u16 in_port; /* Input switch port (or DP_MAX_PORTS). */ |
| 74 | 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 | 77 | struct { |
| 76 | 78 | u8 src[ETH_ALEN]; /* Ethernet source address. */ |
| 77 | 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 *, · 109 unmodified lines | ||
| 187 | 189 | void ovs_flow_stats_clear(struct sw_flow *); |
| 188 | 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 | 193 | int ovs_flow_key_extract(struct ovs_key_ipv4_tunnel *tun_key, |
| 191 | 194 | struct sk_buff *skb, struct sw_flow_key *key); |
| 192 | 195 | /* Extract key from packet coming from userspace. */ |
| @@ -1,5 +1,5 @@ | ||
| 1 | 1 | /* |
| 2 | − * Copyright (c) 2007-2013 Nicira, Inc. | |
| 2 | + * Copyright (c) 2007-2014 Nicira, Inc. | |
| 3 | 3 | * |
| 4 | 4 | * This program is free software; you can redistribute it and/or |
| 5 | 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] = { · 245 unmodified lines | ||
| 251 | 251 | [OVS_KEY_ATTR_ICMPV6] = sizeof(struct ovs_key_icmpv6), |
| 252 | 252 | [OVS_KEY_ATTR_ARP] = sizeof(struct ovs_key_arp), |
| 253 | 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 | 256 | [OVS_KEY_ATTR_TUNNEL] = -1, |
| 255 | 257 | }; |
| 256 | 258 | |
| @@ -454,6 +456,20 @@ static int ipv4_tun_to_nlattr(struct sk_buff *skb, · 197 unmodified lines | ||
| 454 | 456 | static int metadata_from_nlattrs(struct sw_flow_match *match, u64 *attrs, |
| 455 | 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 | 473 | if (*attrs & (1 << OVS_KEY_ATTR_PRIORITY)) { |
| 458 | 474 | SW_FLOW_KEY_PUT(match, phy.priority, |
| 459 | 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, · 413 unmodified lines | ||
| 873 | 889 | struct nlattr *nla, *encap; |
| 874 | 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 | 898 | if (nla_put_u32(skb, OVS_KEY_ATTR_PRIORITY, output->phy.priority)) |
| 877 | 899 | goto nla_put_failure; |
| 878 | 900 | |
| @@ -1401,11 +1423,13 @@ int ovs_nla_copy_actions(const struct nlattr *attr, · 522 unmodified lines | ||
| 1401 | 1423 | /* Expected argument lengths, (u32)-1 for variable length. */ |
| 1402 | 1424 | static const u32 action_lens[OVS_ACTION_ATTR_MAX + 1] = { |
| 1403 | 1425 | [OVS_ACTION_ATTR_OUTPUT] = sizeof(u32), |
| 1426 | + [OVS_ACTION_ATTR_RECIRC] = sizeof(u32), | |
| 1404 | 1427 | [OVS_ACTION_ATTR_USERSPACE] = (u32)-1, |
| 1405 | 1428 | [OVS_ACTION_ATTR_PUSH_VLAN] = sizeof(struct ovs_action_push_vlan), |
| 1406 | 1429 | [OVS_ACTION_ATTR_POP_VLAN] = 0, |
| 1407 | 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 | 1434 | const struct ovs_action_push_vlan *vlan; |
| 1411 | 1435 | int type = nla_type(a); |
| @@ -1432,6 +1456,18 @@ int ovs_nla_copy_actions(const struct nlattr *attr, · 20 unmodified lines | ||
| 1432 | 1456 | return -EINVAL; |
| 1433 | 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 | 1472 | case OVS_ACTION_ATTR_POP_VLAN: |
| 1437 | 1473 | break; |
| @@ -1444,6 +1480,9 @@ int ovs_nla_copy_actions(const struct nlattr *attr, · 6 unmodified lines | ||
| 1444 | 1480 | return -EINVAL; |
| 1445 | 1481 | break; |
| 1446 | 1482 | |
| 1483 | + case OVS_ACTION_ATTR_RECIRC: | |
| 1484 | + break; | |
| 1485 | + | |
| 1447 | 1486 | case OVS_ACTION_ATTR_SET: |
| 1448 | 1487 | err = validate_set(a, key, sfa, &skip_copy); |
| 1449 | 1488 | if (err) |