diff options
author | Jarno Rajahalme <jrajahalme@nicira.com> | 2014-05-05 18:22:25 -0400 |
---|---|---|
committer | Pravin B Shelar <pshelar@nicira.com> | 2014-05-22 19:27:36 -0400 |
commit | 893f139b9a6c00c097b9082a90f3041cfb3a0d20 (patch) | |
tree | bcf6105306b58fe3ad44b12dc0ef51862d889bbe /net/openvswitch | |
parent | 37bdc87ba00dadd0156db77ba48224d042202435 (diff) |
openvswitch: Minimize ovs_flow_cmd_new|set critical sections.
Signed-off-by: Jarno Rajahalme <jrajahalme@nicira.com>
Signed-off-by: Pravin B Shelar <pshelar@nicira.com>
Diffstat (limited to 'net/openvswitch')
-rw-r--r-- | net/openvswitch/datapath.c | 192 |
1 files changed, 116 insertions, 76 deletions
diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index 22665a6144fc..6bc9d94c8df2 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c | |||
@@ -796,8 +796,7 @@ static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info) | |||
796 | { | 796 | { |
797 | struct nlattr **a = info->attrs; | 797 | struct nlattr **a = info->attrs; |
798 | struct ovs_header *ovs_header = info->userhdr; | 798 | struct ovs_header *ovs_header = info->userhdr; |
799 | struct sw_flow_key key, masked_key; | 799 | struct sw_flow *flow, *new_flow; |
800 | struct sw_flow *flow; | ||
801 | struct sw_flow_mask mask; | 800 | struct sw_flow_mask mask; |
802 | struct sk_buff *reply; | 801 | struct sk_buff *reply; |
803 | struct datapath *dp; | 802 | struct datapath *dp; |
@@ -805,61 +804,77 @@ static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info) | |||
805 | struct sw_flow_match match; | 804 | struct sw_flow_match match; |
806 | int error; | 805 | int error; |
807 | 806 | ||
808 | /* Extract key. */ | 807 | /* Must have key and actions. */ |
809 | error = -EINVAL; | 808 | error = -EINVAL; |
810 | if (!a[OVS_FLOW_ATTR_KEY]) | 809 | if (!a[OVS_FLOW_ATTR_KEY]) |
811 | goto error; | 810 | goto error; |
811 | if (!a[OVS_FLOW_ATTR_ACTIONS]) | ||
812 | goto error; | ||
812 | 813 | ||
813 | ovs_match_init(&match, &key, &mask); | 814 | /* Most of the time we need to allocate a new flow, do it before |
815 | * locking. | ||
816 | */ | ||
817 | new_flow = ovs_flow_alloc(); | ||
818 | if (IS_ERR(new_flow)) { | ||
819 | error = PTR_ERR(new_flow); | ||
820 | goto error; | ||
821 | } | ||
822 | |||
823 | /* Extract key. */ | ||
824 | ovs_match_init(&match, &new_flow->unmasked_key, &mask); | ||
814 | error = ovs_nla_get_match(&match, | 825 | error = ovs_nla_get_match(&match, |
815 | a[OVS_FLOW_ATTR_KEY], a[OVS_FLOW_ATTR_MASK]); | 826 | a[OVS_FLOW_ATTR_KEY], a[OVS_FLOW_ATTR_MASK]); |
816 | if (error) | 827 | if (error) |
817 | goto error; | 828 | goto err_kfree_flow; |
818 | 829 | ||
819 | /* Validate actions. */ | 830 | ovs_flow_mask_key(&new_flow->key, &new_flow->unmasked_key, &mask); |
820 | error = -EINVAL; | ||
821 | if (!a[OVS_FLOW_ATTR_ACTIONS]) | ||
822 | goto error; | ||
823 | 831 | ||
832 | /* Validate actions. */ | ||
824 | acts = ovs_nla_alloc_flow_actions(nla_len(a[OVS_FLOW_ATTR_ACTIONS])); | 833 | acts = ovs_nla_alloc_flow_actions(nla_len(a[OVS_FLOW_ATTR_ACTIONS])); |
825 | error = PTR_ERR(acts); | 834 | error = PTR_ERR(acts); |
826 | if (IS_ERR(acts)) | 835 | if (IS_ERR(acts)) |
827 | goto error; | 836 | goto err_kfree_flow; |
828 | 837 | ||
829 | ovs_flow_mask_key(&masked_key, &key, &mask); | 838 | error = ovs_nla_copy_actions(a[OVS_FLOW_ATTR_ACTIONS], &new_flow->key, |
830 | error = ovs_nla_copy_actions(a[OVS_FLOW_ATTR_ACTIONS], | 839 | 0, &acts); |
831 | &masked_key, 0, &acts); | ||
832 | if (error) { | 840 | if (error) { |
833 | OVS_NLERR("Flow actions may not be safe on all matching packets.\n"); | 841 | OVS_NLERR("Flow actions may not be safe on all matching packets.\n"); |
834 | goto err_kfree; | 842 | goto err_kfree_acts; |
843 | } | ||
844 | |||
845 | reply = ovs_flow_cmd_alloc_info(acts, info, false); | ||
846 | if (IS_ERR(reply)) { | ||
847 | error = PTR_ERR(reply); | ||
848 | goto err_kfree_acts; | ||
835 | } | 849 | } |
836 | 850 | ||
837 | ovs_lock(); | 851 | ovs_lock(); |
838 | dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex); | 852 | dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex); |
839 | error = -ENODEV; | 853 | if (unlikely(!dp)) { |
840 | if (!dp) | 854 | error = -ENODEV; |
841 | goto err_unlock_ovs; | 855 | goto err_unlock_ovs; |
842 | 856 | } | |
843 | /* Check if this is a duplicate flow */ | 857 | /* Check if this is a duplicate flow */ |
844 | flow = ovs_flow_tbl_lookup(&dp->table, &key); | 858 | flow = ovs_flow_tbl_lookup(&dp->table, &new_flow->unmasked_key); |
845 | if (!flow) { | 859 | if (likely(!flow)) { |
846 | /* Allocate flow. */ | 860 | rcu_assign_pointer(new_flow->sf_acts, acts); |
847 | flow = ovs_flow_alloc(); | ||
848 | if (IS_ERR(flow)) { | ||
849 | error = PTR_ERR(flow); | ||
850 | goto err_unlock_ovs; | ||
851 | } | ||
852 | |||
853 | flow->key = masked_key; | ||
854 | flow->unmasked_key = key; | ||
855 | rcu_assign_pointer(flow->sf_acts, acts); | ||
856 | 861 | ||
857 | /* Put flow in bucket. */ | 862 | /* Put flow in bucket. */ |
858 | error = ovs_flow_tbl_insert(&dp->table, flow, &mask); | 863 | error = ovs_flow_tbl_insert(&dp->table, new_flow, &mask); |
859 | if (error) { | 864 | if (unlikely(error)) { |
860 | acts = NULL; | 865 | acts = NULL; |
861 | goto err_flow_free; | 866 | goto err_unlock_ovs; |
862 | } | 867 | } |
868 | |||
869 | if (unlikely(reply)) { | ||
870 | error = ovs_flow_cmd_fill_info(new_flow, | ||
871 | ovs_header->dp_ifindex, | ||
872 | reply, info->snd_portid, | ||
873 | info->snd_seq, 0, | ||
874 | OVS_FLOW_CMD_NEW); | ||
875 | BUG_ON(error < 0); | ||
876 | } | ||
877 | ovs_unlock(); | ||
863 | } else { | 878 | } else { |
864 | struct sw_flow_actions *old_acts; | 879 | struct sw_flow_actions *old_acts; |
865 | 880 | ||
@@ -869,39 +884,45 @@ static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info) | |||
869 | * request. We also accept NLM_F_EXCL in case that bug ever | 884 | * request. We also accept NLM_F_EXCL in case that bug ever |
870 | * gets fixed. | 885 | * gets fixed. |
871 | */ | 886 | */ |
872 | error = -EEXIST; | 887 | if (unlikely(info->nlhdr->nlmsg_flags & (NLM_F_CREATE |
873 | if (info->nlhdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_EXCL)) | 888 | | NLM_F_EXCL))) { |
889 | error = -EEXIST; | ||
874 | goto err_unlock_ovs; | 890 | goto err_unlock_ovs; |
875 | 891 | } | |
876 | /* The unmasked key has to be the same for flow updates. */ | 892 | /* The unmasked key has to be the same for flow updates. */ |
877 | if (!ovs_flow_cmp_unmasked_key(flow, &match)) | 893 | if (unlikely(!ovs_flow_cmp_unmasked_key(flow, &match))) { |
894 | error = -EEXIST; | ||
878 | goto err_unlock_ovs; | 895 | goto err_unlock_ovs; |
879 | 896 | } | |
880 | /* Update actions. */ | 897 | /* Update actions. */ |
881 | old_acts = ovsl_dereference(flow->sf_acts); | 898 | old_acts = ovsl_dereference(flow->sf_acts); |
882 | rcu_assign_pointer(flow->sf_acts, acts); | 899 | rcu_assign_pointer(flow->sf_acts, acts); |
883 | ovs_nla_free_flow_actions(old_acts); | ||
884 | } | ||
885 | 900 | ||
886 | reply = ovs_flow_cmd_build_info(flow, ovs_header->dp_ifindex, | 901 | if (unlikely(reply)) { |
887 | info, OVS_FLOW_CMD_NEW, false); | 902 | error = ovs_flow_cmd_fill_info(flow, |
888 | ovs_unlock(); | 903 | ovs_header->dp_ifindex, |
904 | reply, info->snd_portid, | ||
905 | info->snd_seq, 0, | ||
906 | OVS_FLOW_CMD_NEW); | ||
907 | BUG_ON(error < 0); | ||
908 | } | ||
909 | ovs_unlock(); | ||
889 | 910 | ||
890 | if (reply) { | 911 | ovs_nla_free_flow_actions(old_acts); |
891 | if (!IS_ERR(reply)) | 912 | ovs_flow_free(new_flow, false); |
892 | ovs_notify(&dp_flow_genl_family, reply, info); | ||
893 | else | ||
894 | netlink_set_err(sock_net(skb->sk)->genl_sock, 0, 0, | ||
895 | PTR_ERR(reply)); | ||
896 | } | 913 | } |
914 | |||
915 | if (reply) | ||
916 | ovs_notify(&dp_flow_genl_family, reply, info); | ||
897 | return 0; | 917 | return 0; |
898 | 918 | ||
899 | err_flow_free: | ||
900 | ovs_flow_free(flow, false); | ||
901 | err_unlock_ovs: | 919 | err_unlock_ovs: |
902 | ovs_unlock(); | 920 | ovs_unlock(); |
903 | err_kfree: | 921 | kfree_skb(reply); |
922 | err_kfree_acts: | ||
904 | kfree(acts); | 923 | kfree(acts); |
924 | err_kfree_flow: | ||
925 | ovs_flow_free(new_flow, false); | ||
905 | error: | 926 | error: |
906 | return error; | 927 | return error; |
907 | } | 928 | } |
@@ -915,7 +936,7 @@ static int ovs_flow_cmd_set(struct sk_buff *skb, struct genl_info *info) | |||
915 | struct sw_flow_mask mask; | 936 | struct sw_flow_mask mask; |
916 | struct sk_buff *reply = NULL; | 937 | struct sk_buff *reply = NULL; |
917 | struct datapath *dp; | 938 | struct datapath *dp; |
918 | struct sw_flow_actions *acts = NULL; | 939 | struct sw_flow_actions *old_acts = NULL, *acts = NULL; |
919 | struct sw_flow_match match; | 940 | struct sw_flow_match match; |
920 | int error; | 941 | int error; |
921 | 942 | ||
@@ -942,56 +963,75 @@ static int ovs_flow_cmd_set(struct sk_buff *skb, struct genl_info *info) | |||
942 | &masked_key, 0, &acts); | 963 | &masked_key, 0, &acts); |
943 | if (error) { | 964 | if (error) { |
944 | OVS_NLERR("Flow actions may not be safe on all matching packets.\n"); | 965 | OVS_NLERR("Flow actions may not be safe on all matching packets.\n"); |
945 | goto err_kfree; | 966 | goto err_kfree_acts; |
967 | } | ||
968 | } | ||
969 | |||
970 | /* Can allocate before locking if have acts. */ | ||
971 | if (acts) { | ||
972 | reply = ovs_flow_cmd_alloc_info(acts, info, false); | ||
973 | if (IS_ERR(reply)) { | ||
974 | error = PTR_ERR(reply); | ||
975 | goto err_kfree_acts; | ||
946 | } | 976 | } |
947 | } | 977 | } |
948 | 978 | ||
949 | ovs_lock(); | 979 | ovs_lock(); |
950 | dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex); | 980 | dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex); |
951 | error = -ENODEV; | 981 | if (unlikely(!dp)) { |
952 | if (!dp) | 982 | error = -ENODEV; |
953 | goto err_unlock_ovs; | 983 | goto err_unlock_ovs; |
954 | 984 | } | |
955 | /* Check that the flow exists. */ | 985 | /* Check that the flow exists. */ |
956 | flow = ovs_flow_tbl_lookup(&dp->table, &key); | 986 | flow = ovs_flow_tbl_lookup(&dp->table, &key); |
957 | error = -ENOENT; | 987 | if (unlikely(!flow)) { |
958 | if (!flow) | 988 | error = -ENOENT; |
959 | goto err_unlock_ovs; | 989 | goto err_unlock_ovs; |
960 | 990 | } | |
961 | /* The unmasked key has to be the same for flow updates. */ | 991 | /* The unmasked key has to be the same for flow updates. */ |
962 | error = -EEXIST; | 992 | if (unlikely(!ovs_flow_cmp_unmasked_key(flow, &match))) { |
963 | if (!ovs_flow_cmp_unmasked_key(flow, &match)) | 993 | error = -EEXIST; |
964 | goto err_unlock_ovs; | 994 | goto err_unlock_ovs; |
965 | 995 | } | |
966 | /* Update actions, if present. */ | 996 | /* Update actions, if present. */ |
967 | if (acts) { | 997 | if (likely(acts)) { |
968 | struct sw_flow_actions *old_acts; | ||
969 | |||
970 | old_acts = ovsl_dereference(flow->sf_acts); | 998 | old_acts = ovsl_dereference(flow->sf_acts); |
971 | rcu_assign_pointer(flow->sf_acts, acts); | 999 | rcu_assign_pointer(flow->sf_acts, acts); |
972 | ovs_nla_free_flow_actions(old_acts); | 1000 | |
1001 | if (unlikely(reply)) { | ||
1002 | error = ovs_flow_cmd_fill_info(flow, | ||
1003 | ovs_header->dp_ifindex, | ||
1004 | reply, info->snd_portid, | ||
1005 | info->snd_seq, 0, | ||
1006 | OVS_FLOW_CMD_NEW); | ||
1007 | BUG_ON(error < 0); | ||
1008 | } | ||
1009 | } else { | ||
1010 | /* Could not alloc without acts before locking. */ | ||
1011 | reply = ovs_flow_cmd_build_info(flow, ovs_header->dp_ifindex, | ||
1012 | info, OVS_FLOW_CMD_NEW, false); | ||
1013 | if (unlikely(IS_ERR(reply))) { | ||
1014 | error = PTR_ERR(reply); | ||
1015 | goto err_unlock_ovs; | ||
1016 | } | ||
973 | } | 1017 | } |
974 | 1018 | ||
975 | reply = ovs_flow_cmd_build_info(flow, ovs_header->dp_ifindex, | ||
976 | info, OVS_FLOW_CMD_NEW, false); | ||
977 | /* Clear stats. */ | 1019 | /* Clear stats. */ |
978 | if (a[OVS_FLOW_ATTR_CLEAR]) | 1020 | if (a[OVS_FLOW_ATTR_CLEAR]) |
979 | ovs_flow_stats_clear(flow); | 1021 | ovs_flow_stats_clear(flow); |
980 | ovs_unlock(); | 1022 | ovs_unlock(); |
981 | 1023 | ||
982 | if (reply) { | 1024 | if (reply) |
983 | if (!IS_ERR(reply)) | 1025 | ovs_notify(&dp_flow_genl_family, reply, info); |
984 | ovs_notify(&dp_flow_genl_family, reply, info); | 1026 | if (old_acts) |
985 | else | 1027 | ovs_nla_free_flow_actions(old_acts); |
986 | genl_set_err(&dp_flow_genl_family, sock_net(skb->sk), 0, | ||
987 | 0, PTR_ERR(reply)); | ||
988 | } | ||
989 | 1028 | ||
990 | return 0; | 1029 | return 0; |
991 | 1030 | ||
992 | err_unlock_ovs: | 1031 | err_unlock_ovs: |
993 | ovs_unlock(); | 1032 | ovs_unlock(); |
994 | err_kfree: | 1033 | kfree_skb(reply); |
1034 | err_kfree_acts: | ||
995 | kfree(acts); | 1035 | kfree(acts); |
996 | error: | 1036 | error: |
997 | return error; | 1037 | return error; |