+
+/* Send a copy of this packet up to the sFlow agent, along with extra
+ * information about what happened to it. */
+static void sflow_sample(struct datapath *dp, struct sk_buff *skb,
+ const struct nlattr *a, u32 actions_len,
+ struct vport *vport)
+{
+ struct odp_sflow_sample_header *hdr;
+ unsigned int hdrlen = sizeof(struct odp_sflow_sample_header);
+ struct sk_buff *nskb;
+
+ nskb = skb_copy_expand(skb, actions_len + hdrlen, 0, GFP_ATOMIC);
+ if (!nskb)
+ return;
+
+ memcpy(__skb_push(nskb, actions_len), a, actions_len);
+ hdr = (struct odp_sflow_sample_header*)__skb_push(nskb, hdrlen);
+ hdr->actions_len = actions_len;
+ hdr->sample_pool = atomic_read(&vport->sflow_pool);
+ dp_output_control(dp, nskb, _ODPL_SFLOW_NR, 0);
+}
+
+/* Execute a list of actions against 'skb'. */
+int execute_actions(struct datapath *dp, struct sk_buff *skb,
+ const struct odp_flow_key *key,
+ const struct nlattr *actions, u32 actions_len)
+{
+ if (dp->sflow_probability) {
+ struct vport *p = OVS_CB(skb)->vport;
+ if (p) {
+ atomic_inc(&p->sflow_pool);
+ if (dp->sflow_probability == UINT_MAX ||
+ net_random() < dp->sflow_probability)
+ sflow_sample(dp, skb, actions, actions_len, p);
+ }
+ }
+
+ OVS_CB(skb)->tun_id = 0;
+
+ return do_execute_actions(dp, skb, key, actions, actions_len);
+}