Following patch adds sampling action which takes probability and set of actions as arguments. When probability is hit, actions are executed for given packet. Due to this change kernel does not need to know anything about sFlow sampling.
sFlow is defined as SAMPLING Action with probability (sFlow sampling probability) and USERSPACE action as argument. USERSPACE action's data is used as cookie. sFlow uses this cookie to store output-port, no-of-output ports and vlan-id. sample-pool is calculated by using dp stats. Signed-off-by: Pravin Shelar <pshe...@nicira.com> --- datapath/actions.c | 68 ++++++------ datapath/datapath.c | 73 ++++++++----- datapath/datapath.h | 10 -- datapath/vport.c | 1 - datapath/vport.h | 2 - include/openvswitch/datapath-protocol.h | 27 +++-- lib/dpif-linux.c | 56 ---------- lib/dpif-netdev.c | 2 - lib/dpif-provider.h | 19 ---- lib/dpif.c | 39 +------- lib/dpif.h | 6 - lib/odp-util.c | 45 ++++++++ ofproto/ofproto-dpif-sflow.c | 51 ++++------ ofproto/ofproto-dpif-sflow.h | 4 +- ofproto/ofproto-dpif.c | 174 ++++++++++++++++++++++++++++--- ofproto/ofproto.h | 20 ++++ 16 files changed, 344 insertions(+), 253 deletions(-) diff --git a/datapath/actions.c b/datapath/actions.c index 8aec438..3178bdd 100644 --- a/datapath/actions.c +++ b/datapath/actions.c @@ -229,12 +229,41 @@ static int output_userspace(struct datapath *dp, struct sk_buff *skb, u64 arg) upcall.cmd = OVS_PACKET_CMD_ACTION; upcall.key = &OVS_CB(skb)->flow->key; upcall.userdata = arg; - upcall.sample_pool = 0; - upcall.actions = NULL; - upcall.actions_len = 0; return dp_upcall(dp, skb, &upcall); } +static int sample(struct datapath *dp, struct sk_buff *skb, + const struct nlattr *a) +{ + u32 probability; + const struct nlattr *sample_args, *nested_act; + int rem, err; + + rem = nla_len(a); + + /* First arg: probability */ + sample_args = nla_data(a); + memcpy(&probability, nla_data(sample_args), sizeof (probability)); + if (net_random() >= probability) + return 0; + + /* Second arg: actions to execute */ + sample_args = nla_next(sample_args, &rem); + /* Second arg data contains actions to execute */ + nla_for_each_nested(nested_act, sample_args, rem) { + switch (nla_type(nested_act)) { + case OVS_ACTION_ATTR_USERSPACE: + err = output_userspace(dp, skb, + nla_get_u64(nested_act)); + return err; + break; + default: + return -EINVAL; + } + } + return -EINVAL; +} + /* Execute a list of actions against 'skb'. */ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, struct sw_flow_actions *acts) @@ -258,6 +287,9 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, } switch (nla_type(a)) { + case ODP_ACTION_ATTR_SAMPLING: + err = sample(dp, skb, a); + break; case OVS_ACTION_ATTR_OUTPUT: prev_port = nla_get_u32(a); break; @@ -327,33 +359,6 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, return 0; } -static void sflow_sample(struct datapath *dp, struct sk_buff *skb, - struct sw_flow_actions *acts) -{ - struct sk_buff *nskb; - struct vport *p = OVS_CB(skb)->vport; - struct dp_upcall_info upcall; - - if (unlikely(!p)) - return; - - atomic_inc(&p->sflow_pool); - if (net_random() >= dp->sflow_probability) - return; - - nskb = skb_clone(skb, GFP_ATOMIC); - if (unlikely(!nskb)) - return; - - upcall.cmd = OVS_PACKET_CMD_SAMPLE; - upcall.key = &OVS_CB(skb)->flow->key; - upcall.userdata = 0; - upcall.sample_pool = atomic_read(&p->sflow_pool); - upcall.actions = acts->actions; - upcall.actions_len = acts->actions_len; - dp_upcall(dp, nskb, &upcall); -} - /* Execute a list of actions against 'skb'. */ int execute_actions(struct datapath *dp, struct sk_buff *skb) { @@ -371,9 +376,6 @@ int execute_actions(struct datapath *dp, struct sk_buff *skb) goto out_loop; } - /* Really execute actions. */ - if (dp->sflow_probability) - sflow_sample(dp, skb, acts); OVS_CB(skb)->tun_id = 0; error = do_execute_actions(dp, skb, acts); diff --git a/datapath/datapath.c b/datapath/datapath.c index 0b6e2e5..c6ab8a8 100644 --- a/datapath/datapath.c +++ b/datapath/datapath.c @@ -303,9 +303,6 @@ void dp_process_received_packet(struct vport *p, struct sk_buff *skb) upcall.cmd = OVS_PACKET_CMD_MISS; upcall.key = &key; upcall.userdata = 0; - upcall.sample_pool = 0; - upcall.actions = NULL; - upcall.actions_len = 0; dp_upcall(dp, skb, &upcall); stats_counter_off = offsetof(struct dp_stats_percpu, n_missed); goto out; @@ -466,10 +463,6 @@ static int queue_userspace_packets(struct datapath *dp, struct sk_buff *skb, len += nla_total_size(FLOW_BUFSIZE); if (upcall_info->userdata) len += nla_total_size(8); - if (upcall_info->sample_pool) - len += nla_total_size(4); - if (upcall_info->actions_len) - len += nla_total_size(upcall_info->actions_len); user_skb = genlmsg_new(len, GFP_ATOMIC); if (!user_skb) { @@ -486,16 +479,6 @@ static int queue_userspace_packets(struct datapath *dp, struct sk_buff *skb, if (upcall_info->userdata) nla_put_u64(user_skb, OVS_PACKET_ATTR_USERDATA, upcall_info->userdata); - if (upcall_info->sample_pool) - nla_put_u32(user_skb, OVS_PACKET_ATTR_SAMPLE_POOL, upcall_info->sample_pool); - if (upcall_info->actions_len) { - const struct nlattr *actions = upcall_info->actions; - u32 actions_len = upcall_info->actions_len; - - nla = nla_nest_start(user_skb, OVS_PACKET_ATTR_ACTIONS); - memcpy(__skb_put(user_skb, actions_len), actions, actions_len); - nla_nest_end(user_skb, nla); - } nla = __nla_reserve(user_skb, OVS_PACKET_ATTR_PACKET, skb->len); if (skb->ip_summed == CHECKSUM_PARTIAL) @@ -544,10 +527,50 @@ static int flush_flows(int dp_ifindex) return 0; } +static int validate_sample(const struct nlattr *a) +{ + const struct nlattr *sample_args, *nested_act; + int rem; + + rem = nla_len(a); + + /* First arg: probability */ + sample_args = nla_data(a); + if (nla_type(sample_args) != OVS_SAMPLE_ATTR_PROBABILITY) + return -EINVAL; + + if (nla_len(sample_args) != sizeof (unsigned int)) + return -EINVAL; + + /* Second arg: actions to execute */ + if (!nla_ok(sample_args, rem)) + return -EINVAL; + + sample_args = nla_next(sample_args, &rem); + if (nla_type(sample_args) != OVS_SAMPLE_ATTR_ACTIONS) + return -EINVAL; + + /* Second arg data contains actions to execute */ + nla_for_each_nested(nested_act, sample_args, rem) { + switch (nla_type(nested_act)) { + case OVS_ACTION_ATTR_USERSPACE: + if (nla_len(nested_act) != 8) + return -EINVAL; + return 0; + break; + default: + /* at this moment userspace is only + * supported action */ + return -EINVAL; + } + } + return -EINVAL; +} + static int validate_actions(const struct nlattr *attr) { const struct nlattr *a; - int rem; + int rem, err; nla_for_each_nested(a, attr, rem) { static const u32 action_lens[OVS_ACTION_ATTR_MAX + 1] = { @@ -565,6 +588,7 @@ static int validate_actions(const struct nlattr *attr) [OVS_ACTION_ATTR_SET_TUNNEL] = 8, [OVS_ACTION_ATTR_SET_PRIORITY] = 4, [OVS_ACTION_ATTR_POP_PRIORITY] = 0, + [ODP_ACTION_ATTR_SAMPLING] = 24, }; int type = nla_type(a); @@ -586,7 +610,11 @@ static int validate_actions(const struct nlattr *attr) case OVS_ACTION_ATTR_SET_TUNNEL: case OVS_ACTION_ATTR_SET_PRIORITY: case OVS_ACTION_ATTR_POP_PRIORITY: - /* No validation needed. */ + break; + case ODP_ACTION_ATTR_SAMPLING: + err = validate_sample(a); + if (err) + return err; break; case OVS_ACTION_ATTR_OUTPUT: @@ -1223,7 +1251,6 @@ static const struct nla_policy datapath_policy[OVS_DP_ATTR_MAX + 1] = { [OVS_DP_ATTR_NAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 }, #endif [OVS_DP_ATTR_IPV4_FRAGS] = { .type = NLA_U32 }, - [OVS_DP_ATTR_SAMPLING] = { .type = NLA_U32 }, }; static struct genl_family dp_datapath_genl_family = { @@ -1266,15 +1293,11 @@ static int ovs_dp_cmd_fill_info(struct datapath *dp, struct sk_buff *skb, NLA_PUT_U32(skb, OVS_DP_ATTR_IPV4_FRAGS, dp->drop_frags ? OVS_DP_FRAG_DROP : OVS_DP_FRAG_ZERO); - if (dp->sflow_probability) - NLA_PUT_U32(skb, OVS_DP_ATTR_SAMPLING, dp->sflow_probability); - nla = nla_nest_start(skb, OVS_DP_ATTR_MCGROUPS); if (!nla) goto nla_put_failure; NLA_PUT_U32(skb, OVS_PACKET_CMD_MISS, packet_mc_group(dp, OVS_PACKET_CMD_MISS)); NLA_PUT_U32(skb, OVS_PACKET_CMD_ACTION, packet_mc_group(dp, OVS_PACKET_CMD_ACTION)); - NLA_PUT_U32(skb, OVS_PACKET_CMD_SAMPLE, packet_mc_group(dp, OVS_PACKET_CMD_SAMPLE)); nla_nest_end(skb, nla); return genlmsg_end(skb, ovs_header); @@ -1338,8 +1361,6 @@ static void change_datapath(struct datapath *dp, struct nlattr *a[OVS_DP_ATTR_MA { if (a[OVS_DP_ATTR_IPV4_FRAGS]) dp->drop_frags = nla_get_u32(a[OVS_DP_ATTR_IPV4_FRAGS]) == OVS_DP_FRAG_DROP; - if (a[OVS_DP_ATTR_SAMPLING]) - dp->sflow_probability = nla_get_u32(a[OVS_DP_ATTR_SAMPLING]); } static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info) diff --git a/datapath/datapath.h b/datapath/datapath.h index d14f974..b0ed3da 100644 --- a/datapath/datapath.h +++ b/datapath/datapath.h @@ -69,9 +69,6 @@ struct dp_stats_percpu { * @port_list: List of all ports in @ports in arbitrary order. RTNL required * to iterate or modify. * @stats_percpu: Per-CPU datapath statistics. - * @sflow_probability: Number of packets out of UINT_MAX to sample to the - * %OVS_PACKET_CMD_SAMPLE multicast group, e.g. (@sflow_probability/UINT_MAX) - * is the probability of sampling a given packet. * * Context: See the comment on locking at the top of datapath.c for additional * locking information. @@ -93,9 +90,6 @@ struct datapath { /* Stats. */ struct dp_stats_percpu __percpu *stats_percpu; - - /* sFlow Sampling */ - unsigned int sflow_probability; }; /** @@ -130,7 +124,6 @@ struct ovs_skb_cb { * @cmd: One of %OVS_PACKET_CMD_*. * @key: Becomes %OVS_PACKET_ATTR_KEY. Must be nonnull. * @userdata: Becomes %OVS_PACKET_ATTR_USERDATA if nonzero. - * @sample_pool: Becomes %OVS_PACKET_ATTR_SAMPLE_POOL if nonzero. * @actions: Becomes %OVS_PACKET_ATTR_ACTIONS if nonnull. * @actions_len: Number of bytes in @actions. */ @@ -138,9 +131,6 @@ struct dp_upcall_info { u8 cmd; const struct sw_flow_key *key; u64 userdata; - u32 sample_pool; - const struct nlattr *actions; - u32 actions_len; }; extern struct notifier_block dp_device_notifier; diff --git a/datapath/vport.c b/datapath/vport.c index 2b5a0b4..fc0c3a1 100644 --- a/datapath/vport.c +++ b/datapath/vport.c @@ -176,7 +176,6 @@ struct vport *vport_alloc(int priv_size, const struct vport_ops *ops, const stru vport->dp = parms->dp; vport->port_no = parms->port_no; - atomic_set(&vport->sflow_pool, 0); vport->ops = ops; /* Initialize kobject for bridge. This will be added as diff --git a/datapath/vport.h b/datapath/vport.h index 388ba1c..a92d320 100644 --- a/datapath/vport.h +++ b/datapath/vport.h @@ -82,7 +82,6 @@ struct vport_err_stats { * &struct vport. (We keep this around so that we can delete it if the * device gets renamed.) Set to the null string when no link exists. * @node: Element in @dp's @port_list. - * @sflow_pool: Number of packets that were candidates for sFlow sampling, * regardless of whether they were actually chosen and sent down to userspace. * @hash_node: Element in @dev_table hash table in vport.c. * @ops: Class structure. @@ -101,7 +100,6 @@ struct vport { struct kobject kobj; char linkname[IFNAMSIZ]; struct list_head node; - atomic_t sflow_pool; struct hlist_node hash_node; const struct vport_ops *ops; diff --git a/include/openvswitch/datapath-protocol.h b/include/openvswitch/datapath-protocol.h index 535aab3..2b81dff 100644 --- a/include/openvswitch/datapath-protocol.h +++ b/include/openvswitch/datapath-protocol.h @@ -91,10 +91,6 @@ struct ovs_header { * @OVS_DP_ATTR_IPV4_FRAGS: One of %OVS_DP_FRAG_*. Always present in * notifications. May be included in %OVS_DP_NEW or %OVS_DP_SET requests to * change the fragment handling policy. - * @OVS_DP_ATTR_SAMPLING: 32-bit fraction of packets to sample with - * @OVS_PACKET_CMD_SAMPLE. A value of 0 samples no packets, a value of - * %UINT32_MAX samples all packets, and intermediate values sample intermediate - * fractions of packets. * @OVS_DP_ATTR_MCGROUPS: Nested attributes with multicast groups. Each nested * attribute has a %OVS_PACKET_CMD_* type with a 32-bit value giving the * Generic Netlink multicast group number used for sending this datapath's @@ -108,7 +104,6 @@ enum ovs_datapath_attr { OVS_DP_ATTR_NAME, /* name of dp_ifindex netdev */ OVS_DP_ATTR_STATS, /* struct ovs_dp_stats */ OVS_DP_ATTR_IPV4_FRAGS, /* 32-bit enum ovs_frag_handling */ - OVS_DP_ATTR_SAMPLING, /* 32-bit fraction of packets to sample. */ OVS_DP_ATTR_MCGROUPS, /* Nested attributes with multicast groups. */ __OVS_DP_ATTR_MAX }; @@ -147,7 +142,6 @@ enum ovs_packet_cmd { /* Kernel-to-user notifications. */ OVS_PACKET_CMD_MISS, /* Flow table miss. */ OVS_PACKET_CMD_ACTION, /* OVS_ACTION_ATTR_USERSPACE action. */ - OVS_PACKET_CMD_SAMPLE, /* Sampled packet. */ /* User commands. */ OVS_PACKET_CMD_EXECUTE /* Apply actions to a packet. */ @@ -167,11 +161,6 @@ enum ovs_packet_cmd { * @OVS_PACKET_ATTR_USERDATA: Present for an %OVS_PACKET_CMD_ACTION * notification if the %OVS_ACTION_ATTR_USERSPACE, action's argument was * nonzero. - * @OVS_PACKET_ATTR_SAMPLE_POOL: Present for %OVS_PACKET_CMD_SAMPLE. Contains - * the number of packets processed so far that were candidates for sampling. - * @OVS_PACKET_ATTR_ACTIONS: Present for %OVS_PACKET_CMD_SAMPLE. Contains a - * copy of the actions applied to the packet, as nested %OVS_ACTION_ATTR_* - * attributes. * * These attributes follow the &struct ovs_header within the Generic Netlink * payload for %OVS_PACKET_* commands. @@ -181,7 +170,6 @@ enum ovs_packet_attr { OVS_PACKET_ATTR_PACKET, /* Packet data. */ OVS_PACKET_ATTR_KEY, /* Nested OVS_KEY_ATTR_* attributes. */ OVS_PACKET_ATTR_USERDATA, /* u64 OVS_ACTION_ATTR_USERSPACE arg. */ - OVS_PACKET_ATTR_SAMPLE_POOL, /* # sampling candidate packets so far. */ OVS_PACKET_ATTR_ACTIONS, /* Nested OVS_ACTION_ATTR_* attributes. */ __OVS_PACKET_ATTR_MAX }; @@ -407,6 +395,20 @@ enum ovs_flow_attr { #define OVS_FLOW_ATTR_MAX (__OVS_FLOW_ATTR_MAX - 1) +/** + * enum ovs_sample_attr - attributes for ODP_ACTION_ATTR_SAMPLING + * @OVS_SAMPLE_ATTR_PROBABILITY: Prabability for sampling event. + * @OVS_SAMPLE_ATTR_ACTIONS: Set of actions to execute in sampling event. + * actions are passed as nested attributes. + */ +enum ovs_sample_attr { + OVS_SAMPLE_ATTR_UNSPEC, + OVS_SAMPLE_ATTR_PROBABILITY, + OVS_SAMPLE_ATTR_ACTIONS, + __OVS_SAMPLE_ATTR_MAX, +}; + + /* Action types. */ enum ovs_action_type { OVS_ACTION_ATTR_UNSPEC, @@ -424,6 +426,7 @@ enum ovs_action_type { OVS_ACTION_ATTR_SET_TUNNEL, /* Set the encapsulating tunnel ID. */ OVS_ACTION_ATTR_SET_PRIORITY, /* Set skb->priority. */ OVS_ACTION_ATTR_POP_PRIORITY, /* Restore original skb->priority. */ + ODP_ACTION_ATTR_SAMPLING, /* Sampling action */ __OVS_ACTION_ATTR_MAX }; diff --git a/lib/dpif-linux.c b/lib/dpif-linux.c index da48c68..05aa898 100644 --- a/lib/dpif-linux.c +++ b/lib/dpif-linux.c @@ -75,7 +75,6 @@ struct dpif_linux_dp { const char *name; /* OVS_DP_ATTR_NAME. */ struct ovs_dp_stats stats; /* OVS_DP_ATTR_STATS. */ enum ovs_frag_handling ipv4_frags; /* OVS_DP_ATTR_IPV4_FRAGS. */ - const uint32_t *sampling; /* OVS_DP_ATTR_SAMPLING. */ uint32_t mcgroups[DPIF_N_UC_TYPES]; /* OVS_DP_ATTR_MCGROUPS. */ }; @@ -897,35 +896,6 @@ dpif_linux_recv_set_mask(struct dpif *dpif_, int listen_mask) } static int -dpif_linux_get_sflow_probability(const struct dpif *dpif_, - uint32_t *probability) -{ - struct dpif_linux_dp dp; - struct ofpbuf *buf; - int error; - - error = dpif_linux_dp_get(dpif_, &dp, &buf); - if (!error) { - *probability = dp.sampling ? *dp.sampling : 0; - ofpbuf_delete(buf); - } - return error; -} - -static int -dpif_linux_set_sflow_probability(struct dpif *dpif_, uint32_t probability) -{ - struct dpif_linux *dpif = dpif_linux_cast(dpif_); - struct dpif_linux_dp dp; - - dpif_linux_dp_init(&dp); - dp.cmd = OVS_DP_CMD_SET; - dp.dp_ifindex = dpif->dp_ifindex; - dp.sampling = &probability; - return dpif_linux_dp_transact(&dp, NULL, NULL); -} - -static int dpif_linux_queue_to_priority(const struct dpif *dpif OVS_UNUSED, uint32_t queue_id, uint32_t *priority) { @@ -950,8 +920,6 @@ parse_odp_packet(struct ofpbuf *buf, struct dpif_upcall *upcall, /* OVS_PACKET_CMD_ACTION only. */ [OVS_PACKET_ATTR_USERDATA] = { .type = NL_A_U64, .optional = true }, - /* OVS_PACKET_CMD_SAMPLE only. */ - [OVS_PACKET_ATTR_SAMPLE_POOL] = { .type = NL_A_U32, .optional = true }, [OVS_PACKET_ATTR_ACTIONS] = { .type = NL_A_NESTED, .optional = true }, }; @@ -976,7 +944,6 @@ parse_odp_packet(struct ofpbuf *buf, struct dpif_upcall *upcall, type = (genl->cmd == OVS_PACKET_CMD_MISS ? DPIF_UC_MISS : genl->cmd == OVS_PACKET_CMD_ACTION ? DPIF_UC_ACTION - : genl->cmd == OVS_PACKET_CMD_SAMPLE ? DPIF_UC_SAMPLE : -1); if (type < 0) { return EINVAL; @@ -992,14 +959,6 @@ parse_odp_packet(struct ofpbuf *buf, struct dpif_upcall *upcall, upcall->userdata = (a[OVS_PACKET_ATTR_USERDATA] ? nl_attr_get_u64(a[OVS_PACKET_ATTR_USERDATA]) : 0); - upcall->sample_pool = (a[OVS_PACKET_ATTR_SAMPLE_POOL] - ? nl_attr_get_u32(a[OVS_PACKET_ATTR_SAMPLE_POOL]) - : 0); - if (a[OVS_PACKET_ATTR_ACTIONS]) { - upcall->actions = (void *) nl_attr_get(a[OVS_PACKET_ATTR_ACTIONS]); - upcall->actions_len = nl_attr_get_size(a[OVS_PACKET_ATTR_ACTIONS]); - } - *dp_ifindex = ovs_header->dp_ifindex; return 0; @@ -1091,8 +1050,6 @@ const struct dpif_class dpif_linux_class = { dpif_linux_execute, dpif_linux_recv_get_mask, dpif_linux_recv_set_mask, - dpif_linux_get_sflow_probability, - dpif_linux_set_sflow_probability, dpif_linux_queue_to_priority, dpif_linux_recv, dpif_linux_recv_wait, @@ -1403,7 +1360,6 @@ dpif_linux_dp_from_ofpbuf(struct dpif_linux_dp *dp, const struct ofpbuf *buf) .max_len = sizeof(struct ovs_dp_stats), .optional = true }, [OVS_DP_ATTR_IPV4_FRAGS] = { .type = NL_A_U32, .optional = true }, - [OVS_DP_ATTR_SAMPLING] = { .type = NL_A_U32, .optional = true }, [OVS_DP_ATTR_MCGROUPS] = { .type = NL_A_NESTED, .optional = true }, }; @@ -1438,15 +1394,11 @@ dpif_linux_dp_from_ofpbuf(struct dpif_linux_dp *dp, const struct ofpbuf *buf) if (a[OVS_DP_ATTR_IPV4_FRAGS]) { dp->ipv4_frags = nl_attr_get_u32(a[OVS_DP_ATTR_IPV4_FRAGS]); } - if (a[OVS_DP_ATTR_SAMPLING]) { - dp->sampling = nl_attr_get(a[OVS_DP_ATTR_SAMPLING]); - } if (a[OVS_DP_ATTR_MCGROUPS]) { static const struct nl_policy ovs_mcgroup_policy[] = { [OVS_PACKET_CMD_MISS] = { .type = NL_A_U32, .optional = true }, [OVS_PACKET_CMD_ACTION] = { .type = NL_A_U32, .optional = true }, - [OVS_PACKET_CMD_SAMPLE] = { .type = NL_A_U32, .optional = true }, }; struct nlattr *mcgroups[ARRAY_SIZE(ovs_mcgroup_policy)]; @@ -1464,10 +1416,6 @@ dpif_linux_dp_from_ofpbuf(struct dpif_linux_dp *dp, const struct ofpbuf *buf) dp->mcgroups[DPIF_UC_ACTION] = nl_attr_get_u32(mcgroups[OVS_PACKET_CMD_ACTION]); } - if (mcgroups[OVS_PACKET_CMD_SAMPLE]) { - dp->mcgroups[DPIF_UC_SAMPLE] - = nl_attr_get_u32(mcgroups[OVS_PACKET_CMD_SAMPLE]); - } } return 0; @@ -1494,10 +1442,6 @@ dpif_linux_dp_to_ofpbuf(const struct dpif_linux_dp *dp, struct ofpbuf *buf) if (dp->ipv4_frags) { nl_msg_put_u32(buf, OVS_DP_ATTR_IPV4_FRAGS, dp->ipv4_frags); } - - if (dp->sampling) { - nl_msg_put_u32(buf, OVS_DP_ATTR_SAMPLING, *dp->sampling); - } } /* Clears 'dp' to "empty" values. */ diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c index d0a50f3..93b2cba 100644 --- a/lib/dpif-netdev.c +++ b/lib/dpif-netdev.c @@ -1371,8 +1371,6 @@ const struct dpif_class dpif_netdev_class = { dpif_netdev_execute, dpif_netdev_recv_get_mask, dpif_netdev_recv_set_mask, - NULL, /* get_sflow_probability */ - NULL, /* set_sflow_probability */ NULL, /* queue_to_priority */ dpif_netdev_recv, dpif_netdev_recv_wait, diff --git a/lib/dpif-provider.h b/lib/dpif-provider.h index c6c39da..558b891 100644 --- a/lib/dpif-provider.h +++ b/lib/dpif-provider.h @@ -303,25 +303,6 @@ struct dpif_class { * called. */ int (*recv_set_mask)(struct dpif *dpif, int listen_mask); - /* Retrieves 'dpif''s sFlow sampling probability into '*probability'. - * Return value is 0 or a positive errno value. EOPNOTSUPP indicates that - * the datapath does not support sFlow, as does a null pointer. - * - * '*probability' is expressed as the number of packets out of UINT_MAX to - * sample, e.g. probability/UINT_MAX is the probability of sampling a given - * packet. */ - int (*get_sflow_probability)(const struct dpif *dpif, - uint32_t *probability); - - /* Sets 'dpif''s sFlow sampling probability to 'probability'. Return value - * is 0 or a positive errno value. EOPNOTSUPP indicates that the datapath - * does not support sFlow, as does a null pointer. - * - * 'probability' is expressed as the number of packets out of UINT_MAX to - * sample, e.g. probability/UINT_MAX is the probability of sampling a given - * packet. */ - int (*set_sflow_probability)(struct dpif *dpif, uint32_t probability); - /* Translates OpenFlow queue ID 'queue_id' (in host byte order) into a * priority value for use in the OVS_ACTION_ATTR_SET_PRIORITY action in * '*priority'. */ diff --git a/lib/dpif.c b/lib/dpif.c index 8cf7cfe..103b298 100644 --- a/lib/dpif.c +++ b/lib/dpif.c @@ -959,7 +959,6 @@ dpif_upcall_type_to_string(enum dpif_upcall_type type) switch (type) { case DPIF_UC_MISS: return "miss"; case DPIF_UC_ACTION: return "action"; - case DPIF_UC_SAMPLE: return "sample"; case DPIF_N_UC_TYPES: default: return "<unknown>"; } } @@ -968,8 +967,7 @@ static bool OVS_UNUSED is_valid_listen_mask(int listen_mask) { return !(listen_mask & ~((1u << DPIF_UC_MISS) | - (1u << DPIF_UC_ACTION) | - (1u << DPIF_UC_SAMPLE))); + (1u << DPIF_UC_ACTION))); } /* Retrieves 'dpif''s "listen mask" into '*listen_mask'. A 1-bit of value 2**X @@ -1004,41 +1002,6 @@ dpif_recv_set_mask(struct dpif *dpif, int listen_mask) return error; } -/* Retrieve the sFlow sampling probability. '*probability' is expressed as the - * number of packets out of UINT_MAX to sample, e.g. probability/UINT_MAX is - * the probability of sampling a given packet. - * - * Returns 0 if successful, otherwise a positive errno value. EOPNOTSUPP - * indicates that 'dpif' does not support sFlow sampling. */ -int -dpif_get_sflow_probability(const struct dpif *dpif, uint32_t *probability) -{ - int error = (dpif->dpif_class->get_sflow_probability - ? dpif->dpif_class->get_sflow_probability(dpif, probability) - : EOPNOTSUPP); - if (error) { - *probability = 0; - } - log_operation(dpif, "get_sflow_probability", error); - return error; -} - -/* Set the sFlow sampling probability. 'probability' is expressed as the - * number of packets out of UINT_MAX to sample, e.g. probability/UINT_MAX is - * the probability of sampling a given packet. - * - * Returns 0 if successful, otherwise a positive errno value. EOPNOTSUPP - * indicates that 'dpif' does not support sFlow sampling. */ -int -dpif_set_sflow_probability(struct dpif *dpif, uint32_t probability) -{ - int error = (dpif->dpif_class->set_sflow_probability - ? dpif->dpif_class->set_sflow_probability(dpif, probability) - : EOPNOTSUPP); - log_operation(dpif, "set_sflow_probability", error); - return error; -} - /* Polls for an upcall from 'dpif'. If successful, stores the upcall into * '*upcall'. Only upcalls of the types selected with dpif_recv_set_mask() * member function will ordinarily be received (but if a message type is diff --git a/lib/dpif.h b/lib/dpif.h index 1f35206..a9c4600 100644 --- a/lib/dpif.h +++ b/lib/dpif.h @@ -156,7 +156,6 @@ int dpif_execute(struct dpif *, enum dpif_upcall_type { DPIF_UC_MISS, /* Miss in flow table. */ DPIF_UC_ACTION, /* OVS_ACTION_ATTR_USERSPACE action. */ - DPIF_UC_SAMPLE, /* Packet sampling. */ DPIF_N_UC_TYPES }; @@ -178,11 +177,6 @@ struct dpif_upcall { /* DPIF_UC_ACTION only. */ uint64_t userdata; /* Argument to OVS_ACTION_ATTR_USERSPACE. */ - - /* DPIF_UC_SAMPLE only. */ - uint32_t sample_pool; /* # of sampling candidate packets so far. */ - struct nlattr *actions; /* Associated flow actions. */ - size_t actions_len; }; int dpif_recv_get_mask(const struct dpif *, int *listen_mask); diff --git a/lib/odp-util.c b/lib/odp-util.c index 2830fe8..f377f7a 100644 --- a/lib/odp-util.c +++ b/lib/odp-util.c @@ -61,6 +61,7 @@ odp_action_len(uint16_t type) case OVS_ACTION_ATTR_SET_TUNNEL: return 8; case OVS_ACTION_ATTR_SET_PRIORITY: return 4; case OVS_ACTION_ATTR_POP_PRIORITY: return 0; + case ODP_ACTION_ATTR_SAMPLING: return 24; case OVS_ACTION_ATTR_UNSPEC: case __OVS_ACTION_ATTR_MAX: @@ -89,6 +90,47 @@ format_generic_odp_action(struct ds *ds, const struct nlattr *a) } } +static void format_odp_sample_action(struct ds *ds, const struct nlattr *a) +{ + struct nlattr *prob, *userdata, *nla_acts; + uint64_t *arg; + uint32_t *probability; + + ds_put_cstr(ds, "sample"); + prob = (struct nlattr *) nl_attr_find_nested(a, OVS_SAMPLE_ATTR_PROBABILITY); + if (prob == NULL) { + ds_put_cstr(ds, "(No probability attribute)"); + return; + } + + probability = (uint32_t *) nl_attr_get_unspec(prob, sizeof(*probability)); + if (probability == NULL) { + ds_put_cstr(ds, "(No probability assigned)"); + return; + } + + nla_acts = (struct nlattr *) nl_attr_find_nested(a, OVS_SAMPLE_ATTR_ACTIONS); + if (nla_acts == NULL) { + ds_put_cstr(ds, "(No actions for sampling)"); + return; + } + + userdata = (struct nlattr *) nl_attr_find_nested(nla_acts, + OVS_ACTION_ATTR_USERSPACE); + if (userdata == NULL) { + ds_put_cstr(ds, "(No userspace action)"); + return; + } + + arg = (uint64_t *) nl_attr_get_unspec(userdata, sizeof(*arg)); + if (arg == NULL) { + ds_put_cstr(ds, "(No userspace data)"); + return; + } + ds_put_format(ds, "(sample=%"PRIu32", ", (UINT32_MAX / *probability)); + ds_put_format(ds, "actions(userspace(%"PRIx64")))", *arg); +} + void format_odp_action(struct ds *ds, const struct nlattr *a) { @@ -152,6 +194,9 @@ format_odp_action(struct ds *ds, const struct nlattr *a) case OVS_ACTION_ATTR_POP_PRIORITY: ds_put_cstr(ds, "pop_priority"); break; + case ODP_ACTION_ATTR_SAMPLING: + format_odp_sample_action(ds, a); + break; default: format_generic_odp_action(ds, a); break; diff --git a/ofproto/ofproto-dpif-sflow.c b/ofproto/ofproto-dpif-sflow.c index d41b7da..719905c 100644 --- a/ofproto/ofproto-dpif-sflow.c +++ b/ofproto/ofproto-dpif-sflow.c @@ -45,7 +45,6 @@ struct dpif_sflow_port { }; struct dpif_sflow { - struct ofproto *ofproto; struct collectors *collectors; SFLAgent *sflow_agent; struct ofproto_sflow_options *options; @@ -267,9 +266,6 @@ dpif_sflow_clear(struct dpif_sflow *ds) ds->collectors = NULL; ofproto_sflow_options_destroy(ds->options); ds->options = NULL; - - /* Turn off sampling to save CPU cycles. */ - dpif_set_sflow_probability(ds->dpif, 0); } bool @@ -290,6 +286,11 @@ dpif_sflow_create(struct dpif *dpif) return ds; } +int dpif_sflow_get_probability(const struct dpif_sflow *ds) +{ + return MAX(1, UINT32_MAX / ds->options->sampling_rate); +} + void dpif_sflow_destroy(struct dpif_sflow *ds) { @@ -455,10 +456,6 @@ dpif_sflow_set_options(struct dpif_sflow *ds, sfl_receiver_set_sFlowRcvrOwner(receiver, "Open vSwitch sFlow"); sfl_receiver_set_sFlowRcvrTimeout(receiver, 0xffffffff); - /* Set the sampling_rate down in the datapath. */ - dpif_set_sflow_probability(ds->dpif, - MAX(1, UINT32_MAX / options->sampling_rate)); - /* Add samplers and pollers for the currently known ports. */ HMAP_FOR_EACH (dsp, hmap_node, &ds->ports) { dpif_sflow_add_poller(ds, dsp, dsp->odp_port); @@ -476,22 +473,24 @@ dpif_sflow_odp_port_to_ifindex(const struct dpif_sflow *ds, void dpif_sflow_received(struct dpif_sflow *ds, const struct dpif_upcall *upcall, - const struct flow *flow) + const struct flow *flow, uint32_t sample_pool) { SFL_FLOW_SAMPLE_TYPE fs; SFLFlow_sample_element hdrElem; SFLSampled_header *header; SFLFlow_sample_element switchElem; SFLSampler *sampler; - unsigned int left; - struct nlattr *a; + struct userdata_cookie data; size_t n_outputs; + uint16_t tci; /* Build a flow sample */ memset(&fs, 0, sizeof fs); fs.input = dpif_sflow_odp_port_to_ifindex(ds, flow->in_port); fs.output = 0; /* Filled in correctly below. */ - fs.sample_pool = upcall->sample_pool; + + fs.sample_pool = sample_pool; + memcpy(&data, &upcall->userdata, sizeof data); /* We are going to give it to the sampler that represents this input port. * By implementing "ingress-only" sampling like this we ensure that we @@ -527,26 +526,16 @@ dpif_sflow_received(struct dpif_sflow *ds, const struct dpif_upcall *upcall, switchElem.flowType.sw.dst_vlan = switchElem.flowType.sw.src_vlan; switchElem.flowType.sw.dst_priority = switchElem.flowType.sw.src_priority; - /* Figure out the output ports. */ n_outputs = 0; - NL_ATTR_FOR_EACH_UNSAFE (a, left, upcall->actions, upcall->actions_len) { - ovs_be16 tci; - - switch (nl_attr_type(a)) { - case OVS_ACTION_ATTR_OUTPUT: - fs.output = dpif_sflow_odp_port_to_ifindex(ds, nl_attr_get_u32(a)); - n_outputs++; - break; - - case OVS_ACTION_ATTR_SET_DL_TCI: - tci = nl_attr_get_be16(a); - switchElem.flowType.sw.dst_vlan = vlan_tci_to_vid(tci); - switchElem.flowType.sw.dst_priority = vlan_tci_to_pcp(tci); - break; - - default: - break; - } + if (data.output) { + fs.output = dpif_sflow_odp_port_to_ifindex(ds, data.output); + n_outputs = data.n_output; + } + + tci = data.tci; + if (tci) { + switchElem.flowType.sw.dst_vlan = vlan_tci_to_vid(tci); + switchElem.flowType.sw.dst_priority = vlan_tci_to_pcp(tci); } /* Set output port, as defined by http://www.sflow.org/sflow_version_5.txt diff --git a/ofproto/ofproto-dpif-sflow.h b/ofproto/ofproto-dpif-sflow.h index c7c8b5a..374c381 100644 --- a/ofproto/ofproto-dpif-sflow.h +++ b/ofproto/ofproto-dpif-sflow.h @@ -27,6 +27,8 @@ struct flow; struct ofproto_sflow_options; struct dpif_sflow *dpif_sflow_create(struct dpif *); +int dpif_sflow_get_probability(const struct dpif_sflow *); + void dpif_sflow_destroy(struct dpif_sflow *); void dpif_sflow_set_options(struct dpif_sflow *, const struct ofproto_sflow_options *); @@ -41,6 +43,6 @@ void dpif_sflow_run(struct dpif_sflow *); void dpif_sflow_wait(struct dpif_sflow *); void dpif_sflow_received(struct dpif_sflow *, const struct dpif_upcall *, - const struct flow *); + const struct flow *, uint32_t); #endif /* ofproto/ofproto-dpif-sflow.h */ diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index f09c230..fa70be6 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -189,6 +189,9 @@ struct action_xlate_ctx { struct flow base_flow; /* Flow at the last commit. */ uint32_t base_priority; /* Priority at the last commit. */ uint8_t table_id; /* OpenFlow table ID where flow was found. */ + uint16_t sflow_odp_port; /* Output port for composing sFlow action */ + uint16_t sflow_n_outputs; /* Number of output port, for sFlow */ + uint16_t dst_tci; }; static void action_xlate_ctx_init(struct action_xlate_ctx *, @@ -431,8 +434,7 @@ construct(struct ofproto *ofproto_, int *n_tablesp) error = dpif_recv_set_mask(ofproto->dpif, ((1u << DPIF_UC_MISS) | - (1u << DPIF_UC_ACTION) | - (1u << DPIF_UC_SAMPLE))); + (1u << DPIF_UC_ACTION))); if (error) { VLOG_ERR("failed to listen on datapath %s: %s", name, strerror(error)); dpif_close(ofproto->dpif); @@ -764,6 +766,7 @@ set_sflow(struct ofproto *ofproto_, { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); struct dpif_sflow *ds = ofproto->sflow; + if (sflow_options) { if (!ds) { struct ofport_dpif *ofport; @@ -773,11 +776,15 @@ set_sflow(struct ofproto *ofproto_, dpif_sflow_add_port(ds, ofport->odp_port, netdev_get_name(ofport->up.netdev)); } + flush(ofproto_); } dpif_sflow_set_options(ds, sflow_options); } else { dpif_sflow_destroy(ds); - ofproto->sflow = NULL; + if (ofproto->sflow) { + flush(ofproto_); + ofproto->sflow = NULL; + } } return 0; } @@ -1706,23 +1713,41 @@ handle_miss_upcall(struct ofproto_dpif *ofproto, struct dpif_upcall *upcall) } static void -handle_upcall(struct ofproto_dpif *ofproto, struct dpif_upcall *upcall) +handle_userspace_upcall(struct ofproto_dpif *ofproto, + struct dpif_upcall *upcall) { struct flow flow; + struct userdata_cookie *data; + struct ovs_dp_stats s; - switch (upcall->type) { - case DPIF_UC_ACTION: - COVERAGE_INC(ofproto_dpif_ctlr_action); - odp_flow_key_to_flow(upcall->key, upcall->key_len, &flow); - send_packet_in(ofproto, upcall, &flow, false); - break; + data = (struct userdata_cookie *) &upcall->userdata; - case DPIF_UC_SAMPLE: + if (data->type == OVS_USERDATA_SFLOW) { if (ofproto->sflow) { + dpif_get_dp_stats(ofproto->dpif, &s); odp_flow_key_to_flow(upcall->key, upcall->key_len, &flow); - dpif_sflow_received(ofproto->sflow, upcall, &flow); + dpif_sflow_received(ofproto->sflow, upcall, &flow, + (s.n_hit + s.n_missed)); } ofpbuf_delete(upcall->packet); + + return; + } + if (data->type == OVS_USERDATA_CONTROLLER) { + COVERAGE_INC(ofproto_dpif_ctlr_action); + odp_flow_key_to_flow(upcall->key, upcall->key_len, &flow); + send_packet_in(ofproto, upcall, &flow, false); + return; + } + +} + +static void +handle_upcall(struct ofproto_dpif *ofproto, struct dpif_upcall *upcall) +{ + switch (upcall->type) { + case DPIF_UC_ACTION: + handle_userspace_upcall(ofproto, upcall); break; case DPIF_UC_MISS: @@ -2070,9 +2095,6 @@ execute_odp_actions(struct ofproto_dpif *ofproto, const struct flow *flow, upcall.key = NULL; upcall.key_len = 0; upcall.userdata = nl_attr_get_u64(odp_actions); - upcall.sample_pool = 0; - upcall.actions = NULL; - upcall.actions_len = 0; send_packet_in(ofproto, &upcall, flow, false); @@ -2811,6 +2833,99 @@ static void do_xlate_actions(const union ofp_action *in, size_t n_in, struct action_xlate_ctx *ctx); static void xlate_normal(struct action_xlate_ctx *); +/* + * This function reserves space for sampling action. + * only information we have is sflow sampling probability. other + * field are set to invalid values. + **/ +static void __compose_sflow_action(struct ofpbuf *odp_actions, + uint32_t probability) +{ + struct userdata_cookie data; + size_t offset1, offset2; + + offset1 = nl_msg_start_nested(odp_actions, ODP_ACTION_ATTR_SAMPLING); + + nl_msg_put_unspec(odp_actions, OVS_SAMPLE_ATTR_PROBABILITY, + &probability, sizeof probability); + + offset2 = nl_msg_start_nested(odp_actions, OVS_SAMPLE_ATTR_ACTIONS); + + data.output = -1; + data.tci = -1; + data.n_output = -1; + data.type = OVS_USERDATA_SFLOW; + nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_USERSPACE, &data, + sizeof(data)); + + nl_msg_end_nested(odp_actions, offset2); + nl_msg_end_nested(odp_actions, offset1); +} + +/* + * Sampling action must be first action in any given list of actions. + * so build sampling action as complete as possible. + * */ +static void +add_sample_action(struct action_xlate_ctx *ctx) +{ + uint32_t probability; + + if (!ctx->ofproto->sflow) + return; + + probability = dpif_sflow_get_probability(ctx->ofproto->sflow); + __compose_sflow_action(ctx->odp_actions, probability); + + ctx->sflow_odp_port = 0; + ctx->sflow_n_outputs = 0; + ctx->dst_tci = 0; +} + +/** + * Fix SAMPLING action according to data collected while computing actions. + * Specifically we need to fix USERSPACE argument which is required for + * sflow. + */ +static void +fix_sample_action(struct action_xlate_ctx *ctx) +{ + struct userdata_cookie *arg; + struct nlattr *nla, *userdata, *nla_acts; + + if (!ctx->ofproto->sflow) + return; + + nla = (struct nlattr *) nl_attr_find(ctx->odp_actions, 0, + ODP_ACTION_ATTR_SAMPLING); + if (nla == NULL) { + VLOG_ERR("No sampling attr"); + return; + } + nla_acts = (struct nlattr *)nl_attr_find_nested(nla, + OVS_SAMPLE_ATTR_ACTIONS); + if (nla_acts == NULL) { + VLOG_ERR("No actions for sampling"); + return; + } + userdata = (struct nlattr *)nl_attr_find_nested(nla_acts, + OVS_ACTION_ATTR_USERSPACE); + if (userdata == NULL) { + VLOG_ERR("No userspace action"); + return; + } + arg = (struct userdata_cookie *) nl_attr_get_unspec(userdata, sizeof(*arg)); + if (arg == NULL) { + VLOG_ERR("No userspace data"); + return; + } + + arg->n_output = ctx->sflow_n_outputs; + arg->output = ctx->sflow_odp_port; + arg->tci = ctx->dst_tci; + +} + static void commit_odp_actions(struct action_xlate_ctx *ctx) { @@ -2818,6 +2933,8 @@ commit_odp_actions(struct action_xlate_ctx *ctx) struct flow *base = &ctx->base_flow; struct ofpbuf *odp_actions = ctx->odp_actions; + add_sample_action(ctx); + if (base->tun_id != flow->tun_id) { nl_msg_put_be64(odp_actions, OVS_ACTION_ATTR_SET_TUNNEL, flow->tun_id); base->tun_id = flow->tun_id; @@ -2844,6 +2961,7 @@ commit_odp_actions(struct action_xlate_ctx *ctx) } else { nl_msg_put_be16(odp_actions, OVS_ACTION_ATTR_SET_DL_TCI, flow->vlan_tci & ~htons(VLAN_CFI)); + ctx->dst_tci = flow->vlan_tci & ~htons(VLAN_CFI); } base->vlan_tci = flow->vlan_tci; } @@ -2879,6 +2997,8 @@ commit_odp_actions(struct action_xlate_ctx *ctx) } ctx->base_priority = ctx->priority; } + + fix_sample_action(ctx); } static void @@ -2903,6 +3023,8 @@ add_output_action(struct action_xlate_ctx *ctx, uint16_t ofp_port) commit_odp_actions(ctx); nl_msg_put_u32(ctx->odp_actions, OVS_ACTION_ATTR_OUTPUT, odp_port); ctx->nf_output_iface = ofp_port; + ctx->sflow_odp_port = ofp_port; + ctx->sflow_n_outputs++; } static void @@ -2970,12 +3092,24 @@ flood_packets(struct action_xlate_ctx *ctx, ovs_be32 mask) if (ofp_port != ctx->flow.in_port && !(ofport->up.opp.config & mask)) { nl_msg_put_u32(ctx->odp_actions, OVS_ACTION_ATTR_OUTPUT, ofport->odp_port); + ctx->sflow_odp_port = ofport->odp_port; + ctx->sflow_n_outputs++; } } ctx->nf_output_iface = NF_OUT_FLOOD; } +static void compose_con_userspace_action(struct ofpbuf *odp_actions, int len) +{ + struct userdata_cookie data; + + data.type = OVS_USERDATA_CONTROLLER; + data.data = len; + nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_USERSPACE, + &data, sizeof data); +} + static void xlate_output_action__(struct action_xlate_ctx *ctx, uint16_t port, uint16_t max_len) @@ -3002,7 +3136,7 @@ xlate_output_action__(struct action_xlate_ctx *ctx, break; case OFPP_CONTROLLER: commit_odp_actions(ctx); - nl_msg_put_u64(ctx->odp_actions, OVS_ACTION_ATTR_USERSPACE, max_len); + compose_con_userspace_action(ctx->odp_actions, max_len); break; case OFPP_LOCAL: add_output_action(ctx, OFPP_LOCAL); @@ -3619,6 +3753,7 @@ compose_actions(struct action_xlate_ctx *ctx, uint16_t vlan, compose_mirror_dsts(ctx, vlan, in_bundle, &set); /* Output all the packets we can without having to change the VLAN. */ + add_sample_action(ctx); initial_vlan = vlan_tci_to_vid(ctx->flow.vlan_tci); if (initial_vlan == 0) { initial_vlan = OFP_VLAN_NONE; @@ -3629,6 +3764,8 @@ compose_actions(struct action_xlate_ctx *ctx, uint16_t vlan, } nl_msg_put_u32(ctx->odp_actions, OVS_ACTION_ATTR_OUTPUT, dst->port->odp_port); + ctx->sflow_odp_port = dst->port->odp_port; + ctx->sflow_n_outputs++; } /* Then output the rest. */ @@ -3646,13 +3783,18 @@ compose_actions(struct action_xlate_ctx *ctx, uint16_t vlan, tci |= ctx->flow.vlan_tci & htons(VLAN_PCP_MASK); nl_msg_put_be16(ctx->odp_actions, OVS_ACTION_ATTR_SET_DL_TCI, tci); + + ctx->dst_tci = tci; } cur_vlan = dst->vlan; } nl_msg_put_u32(ctx->odp_actions, OVS_ACTION_ATTR_OUTPUT, dst->port->odp_port); + ctx->sflow_odp_port = dst->port->odp_port; + ctx->sflow_n_outputs++; } + fix_sample_action(ctx); dst_set_free(&set); } diff --git a/ofproto/ofproto.h b/ofproto/ofproto.h index e0c99ea..acaf6e3 100644 --- a/ofproto/ofproto.h +++ b/ofproto/ofproto.h @@ -239,6 +239,26 @@ int ofproto_port_get_cfm_fault(const struct ofproto *, uint16_t ofp_port); void ofproto_get_ofproto_controller_info(const struct ofproto *, struct shash *); void ofproto_free_ofproto_controller_info(struct shash *); +enum ovs_userdata_dst { + OVS_USERDATA_CONTROLLER = 1, + OVS_USERDATA_SFLOW, +}; + +/* userdata_cookie is passed as argument to OVS_ACTION_ATTR_USERSPACE + * Since is it passed to kernel, its size can not be changed to 8 bytes */ +struct userdata_cookie { + uint16_t type; + uint16_t tci; + union { + uint32_t data; + struct { + uint16_t output; + uint16_t n_output; + }; + }; +}__attribute__((__packed__)); + +BUILD_ASSERT_DECL(8 == sizeof(struct userdata_cookie)); #ifdef __cplusplus } #endif -- 1.7.1 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev