diff options
-rw-r--r-- | include/uapi/linux/openvswitch.h | 4 | ||||
-rw-r--r-- | net/openvswitch/datapath.c | 14 |
2 files changed, 11 insertions, 7 deletions
diff --git a/include/uapi/linux/openvswitch.h b/include/uapi/linux/openvswitch.h index 970553cbbc8e..0b979ee4bfc0 100644 --- a/include/uapi/linux/openvswitch.h +++ b/include/uapi/linux/openvswitch.h | |||
@@ -395,7 +395,9 @@ struct ovs_key_nd { | |||
395 | * @OVS_FLOW_ATTR_ACTIONS: Nested %OVS_ACTION_ATTR_* attributes specifying | 395 | * @OVS_FLOW_ATTR_ACTIONS: Nested %OVS_ACTION_ATTR_* attributes specifying |
396 | * the actions to take for packets that match the key. Always present in | 396 | * the actions to take for packets that match the key. Always present in |
397 | * notifications. Required for %OVS_FLOW_CMD_NEW requests, optional for | 397 | * notifications. Required for %OVS_FLOW_CMD_NEW requests, optional for |
398 | * %OVS_FLOW_CMD_SET requests. | 398 | * %OVS_FLOW_CMD_SET requests. An %OVS_FLOW_CMD_SET without |
399 | * %OVS_FLOW_ATTR_ACTIONS will not modify the actions. To clear the actions, | ||
400 | * an %OVS_FLOW_ATTR_ACTIONS without any nested attributes must be given. | ||
399 | * @OVS_FLOW_ATTR_STATS: &struct ovs_flow_stats giving statistics for this | 401 | * @OVS_FLOW_ATTR_STATS: &struct ovs_flow_stats giving statistics for this |
400 | * flow. Present in notifications if the stats would be nonzero. Ignored in | 402 | * flow. Present in notifications if the stats would be nonzero. Ignored in |
401 | * requests. | 403 | * requests. |
diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index 8867d7e2d65b..90a1e5e66287 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c | |||
@@ -810,6 +810,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) | |||
810 | goto err_kfree; | 810 | goto err_kfree; |
811 | } | 811 | } |
812 | } else if (info->genlhdr->cmd == OVS_FLOW_CMD_NEW) { | 812 | } else if (info->genlhdr->cmd == OVS_FLOW_CMD_NEW) { |
813 | /* OVS_FLOW_CMD_NEW must have actions. */ | ||
813 | error = -EINVAL; | 814 | error = -EINVAL; |
814 | goto error; | 815 | goto error; |
815 | } | 816 | } |
@@ -849,8 +850,6 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) | |||
849 | reply = ovs_flow_cmd_build_info(flow, dp, info, OVS_FLOW_CMD_NEW); | 850 | reply = ovs_flow_cmd_build_info(flow, dp, info, OVS_FLOW_CMD_NEW); |
850 | } else { | 851 | } else { |
851 | /* We found a matching flow. */ | 852 | /* We found a matching flow. */ |
852 | struct sw_flow_actions *old_acts; | ||
853 | |||
854 | /* Bail out if we're not allowed to modify an existing flow. | 853 | /* Bail out if we're not allowed to modify an existing flow. |
855 | * We accept NLM_F_CREATE in place of the intended NLM_F_EXCL | 854 | * We accept NLM_F_CREATE in place of the intended NLM_F_EXCL |
856 | * because Generic Netlink treats the latter as a dump | 855 | * because Generic Netlink treats the latter as a dump |
@@ -866,11 +865,14 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) | |||
866 | if (!ovs_flow_cmp_unmasked_key(flow, &match)) | 865 | if (!ovs_flow_cmp_unmasked_key(flow, &match)) |
867 | goto err_unlock_ovs; | 866 | goto err_unlock_ovs; |
868 | 867 | ||
869 | /* Update actions. */ | 868 | /* Update actions, if present. */ |
870 | old_acts = ovsl_dereference(flow->sf_acts); | 869 | if (acts) { |
871 | rcu_assign_pointer(flow->sf_acts, acts); | 870 | struct sw_flow_actions *old_acts; |
872 | ovs_nla_free_flow_actions(old_acts); | ||
873 | 871 | ||
872 | old_acts = ovsl_dereference(flow->sf_acts); | ||
873 | rcu_assign_pointer(flow->sf_acts, acts); | ||
874 | ovs_nla_free_flow_actions(old_acts); | ||
875 | } | ||
874 | reply = ovs_flow_cmd_build_info(flow, dp, info, OVS_FLOW_CMD_NEW); | 876 | reply = ovs_flow_cmd_build_info(flow, dp, info, OVS_FLOW_CMD_NEW); |
875 | 877 | ||
876 | /* Clear stats. */ | 878 | /* Clear stats. */ |