Initial implementation of sFlow.
[openvswitch] / datapath / actions.c
index cadab05f177234b9fa61ec31b5e5262207203561..7c618ccd222f841fe294089de1578f28be422fb9 100644 (file)
@@ -366,6 +366,33 @@ output_control(struct datapath *dp, struct sk_buff *skb, u32 arg, gfp_t gfp)
        return dp_output_control(dp, skb, _ODPL_ACTION_NR, arg);
 }
 
+/* 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 union odp_action *a, int n_actions, gfp_t gfp)
+{
+       struct odp_sflow_sample_header *hdr;
+       unsigned int actlen = n_actions * sizeof(union odp_action);
+       unsigned int hdrlen = sizeof(struct odp_sflow_sample_header);
+       struct sk_buff *nskb;
+       int i;
+
+       nskb = skb_copy_expand(skb, actlen + hdrlen, 0, gfp);
+       if (!nskb)
+               return;
+
+       memcpy(__skb_push(nskb, actlen), a, actlen);
+       hdr = (struct odp_sflow_sample_header*)__skb_push(nskb, hdrlen);
+       hdr->n_actions = n_actions;
+       hdr->sample_pool = 0;
+       for_each_possible_cpu (i) {
+               const struct dp_stats_percpu *stats;
+               stats = per_cpu_ptr(dp->stats_percpu, i);
+               hdr->sample_pool += stats->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,
                    struct odp_flow_key *key,
@@ -378,6 +405,19 @@ int execute_actions(struct datapath *dp, struct sk_buff *skb,
         * is slightly obscure just to avoid that. */
        int prev_port = -1;
        int err;
+
+       if (dp->sflow_probability) {
+               /* Increment sample pool. */
+               int cpu = get_cpu();
+               per_cpu_ptr(dp->stats_percpu, cpu)->sflow_pool++;
+               put_cpu();
+
+               /* Sample packet. */
+               if (dp->sflow_probability == UINT_MAX ||
+                   net_random() < dp->sflow_probability)
+                       sflow_sample(dp, skb, a, n_actions, gfp);
+       }
+
        for (; n_actions > 0; a++, n_actions--) {
                WARN_ON_ONCE(skb_shared(skb));
                if (prev_port != -1) {