[ovs-dev] [PATCH] datapath: Per cpu flow stats.

Pravin Shelar pshelar at nicira.com
Fri Oct 18 15:32:45 UTC 2013


On Fri, Oct 18, 2013 at 8:25 AM, Andy Zhou <azhou at nicira.com> wrote:
> Do we still need a spinlock when we have per-cpu counters?
>
Yes, cmd OVS_FLOW_ATTR_CLEAR needs clear stats. So lock is required
for atomic reset.


>
> On Fri, Oct 18, 2013 at 8:11 AM, Pravin B Shelar <pshelar at nicira.com> wrote:
>>
>> With mega flow implementation ovs flow can be shared between
>> multiple CPUs which makes stats updates highly contended
>> operation. Following patch allocates separate stats for each
>> CPU to make stats update scalable.
>>
>> Signed-off-by: Pravin B Shelar <pshelar at nicira.com>
>> ---
>>  datapath/datapath.c   |   54
>> ++++++++++++++++++------------------------------
>>  datapath/flow.c       |   50
>> +++++++++++++++++++++++++++++++++++++++------
>>  datapath/flow.h       |   20 +++++++++++------
>>  datapath/flow_table.c |   14 ++++++++++--
>>  4 files changed, 87 insertions(+), 51 deletions(-)
>>
>> diff --git a/datapath/datapath.c b/datapath/datapath.c
>> index 9e6df12..25f25f2 100644
>> --- a/datapath/datapath.c
>> +++ b/datapath/datapath.c
>> @@ -252,9 +252,9 @@ void ovs_dp_process_received_packet(struct vport *p,
>> struct sk_buff *skb)
>>         OVS_CB(skb)->flow = flow;
>>         OVS_CB(skb)->pkt_key = &key;
>>
>> -       stats_counter = &stats->n_hit;
>> -       ovs_flow_used(OVS_CB(skb)->flow, skb);
>> +       ovs_flow_stats_update(OVS_CB(skb)->flow, skb);
>>         ovs_execute_actions(dp, skb);
>> +       stats_counter = &stats->n_hit;
>>
>>  out:
>>         /* Update datapath statistics. */
>> @@ -454,14 +454,6 @@ out:
>>         return err;
>>  }
>>
>> -static void clear_stats(struct sw_flow *flow)
>> -{
>> -       flow->used = 0;
>> -       flow->tcp_flags = 0;
>> -       flow->packet_count = 0;
>> -       flow->byte_count = 0;
>> -}
>> -
>>  static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info
>> *info)
>>  {
>>         struct ovs_header *ovs_header = info->userhdr;
>> @@ -629,11 +621,9 @@ static int ovs_flow_cmd_fill_info(struct sw_flow
>> *flow, struct datapath *dp,
>>  {
>>         const int skb_orig_len = skb->len;
>>         struct nlattr *start;
>> -       struct ovs_flow_stats stats;
>> +       struct sw_flow_stats flow_stats;
>>         struct ovs_header *ovs_header;
>>         struct nlattr *nla;
>> -       unsigned long used;
>> -       u8 tcp_flags;
>>         int err;
>>
>>         ovs_header = genlmsg_put(skb, portid, seq, &dp_flow_genl_family,
>> flags, cmd);
>> @@ -662,24 +652,24 @@ static int ovs_flow_cmd_fill_info(struct sw_flow
>> *flow, struct datapath *dp,
>>
>>         nla_nest_end(skb, nla);
>>
>> -       spin_lock_bh(&flow->lock);
>> -       used = flow->used;
>> -       stats.n_packets = flow->packet_count;
>> -       stats.n_bytes = flow->byte_count;
>> -       tcp_flags = flow->tcp_flags;
>> -       spin_unlock_bh(&flow->lock);
>> -
>> -       if (used &&
>> -           nla_put_u64(skb, OVS_FLOW_ATTR_USED,
>> ovs_flow_used_time(used)))
>> +       ovs_flow_stats_get(flow, &flow_stats);
>> +       if (flow_stats.used &&
>> +           nla_put_u64(skb, OVS_FLOW_ATTR_USED,
>> ovs_flow_used_time(flow_stats.used)))
>>                 goto nla_put_failure;
>>
>> -       if (stats.n_packets &&
>> -           nla_put(skb, OVS_FLOW_ATTR_STATS,
>> -                   sizeof(struct ovs_flow_stats), &stats))
>> -               goto nla_put_failure;
>> +       if (flow_stats.packet_count) {
>> +               struct ovs_flow_stats stats = {
>> +                       .n_packets = flow_stats.packet_count,
>> +                       .n_bytes = flow_stats.byte_count,
>> +               };
>>
>> -       if (tcp_flags &&
>> -           nla_put_u8(skb, OVS_FLOW_ATTR_TCP_FLAGS, tcp_flags))
>> +               if (nla_put(skb, OVS_FLOW_ATTR_STATS,
>> +                           sizeof(struct ovs_flow_stats), &stats))
>> +                       goto nla_put_failure;
>> +       }
>> +
>> +       if (flow_stats.tcp_flags &&
>> +           nla_put_u8(skb, OVS_FLOW_ATTR_TCP_FLAGS,
>> flow_stats.tcp_flags))
>>                 goto nla_put_failure;
>>
>>         /* If OVS_FLOW_ATTR_ACTIONS doesn't fit, skip dumping the actions
>> if
>> @@ -809,7 +799,6 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff
>> *skb, struct genl_info *info)
>>                         error = PTR_ERR(flow);
>>                         goto err_unlock_ovs;
>>                 }
>> -               clear_stats(flow);
>>
>>                 flow->key = masked_key;
>>                 flow->unmasked_key = key;
>> @@ -855,11 +844,8 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff
>> *skb, struct genl_info *info)
>>                                                info->snd_seq,
>> OVS_FLOW_CMD_NEW);
>>
>>                 /* Clear stats. */
>> -               if (a[OVS_FLOW_ATTR_CLEAR]) {
>> -                       spin_lock_bh(&flow->lock);
>> -                       clear_stats(flow);
>> -                       spin_unlock_bh(&flow->lock);
>> -               }
>> +               if (a[OVS_FLOW_ATTR_CLEAR])
>> +                       ovs_sw_flow_stats_clear(flow);
>>         }
>>         ovs_unlock();
>>
>> diff --git a/datapath/flow.c b/datapath/flow.c
>> index faa4e15..1ee01ec 100644
>> --- a/datapath/flow.c
>> +++ b/datapath/flow.c
>> @@ -62,8 +62,9 @@ u64 ovs_flow_used_time(unsigned long flow_jiffies)
>>  #define TCP_FLAGS_OFFSET 13
>>  #define TCP_FLAG_MASK 0x3f
>>
>> -void ovs_flow_used(struct sw_flow *flow, struct sk_buff *skb)
>> +void ovs_flow_stats_update(struct sw_flow *flow, struct sk_buff *skb)
>>  {
>> +       struct sw_flow_stats *stats = &flow->stats[smp_processor_id()];
>>         u8 tcp_flags = 0;
>>
>>         if ((flow->key.eth.type == htons(ETH_P_IP) ||
>> @@ -74,12 +75,47 @@ void ovs_flow_used(struct sw_flow *flow, struct
>> sk_buff *skb)
>>                 tcp_flags = *(tcp + TCP_FLAGS_OFFSET) & TCP_FLAG_MASK;
>>         }
>>
>> -       spin_lock(&flow->lock);
>> -       flow->used = jiffies;
>> -       flow->packet_count++;
>> -       flow->byte_count += skb->len;
>> -       flow->tcp_flags |= tcp_flags;
>> -       spin_unlock(&flow->lock);
>> +       spin_lock(&stats->lock);
>> +       stats->used = jiffies;
>> +       stats->packet_count++;
>> +       stats->byte_count += skb->len;
>> +       stats->tcp_flags |= tcp_flags;
>> +       spin_unlock(&stats->lock);
>> +}
>> +
>> +void ovs_flow_stats_get(struct sw_flow *flow, struct sw_flow_stats *res)
>> +{
>> +       int cpu;
>> +
>> +       memset(res, 0, sizeof(*res));
>> +
>> +       for_each_possible_cpu(cpu) {
>> +               struct sw_flow_stats *stats = &flow->stats[cpu];
>> +
>> +               spin_lock(&stats->lock);
>> +               if (time_after(stats->used, res->used))
>> +                       res->used = stats->used;
>> +               res->packet_count += stats->packet_count;
>> +               res->byte_count += stats->byte_count;
>> +               res->tcp_flags |= stats->tcp_flags;
>> +               spin_unlock(&stats->lock);
>> +       }
>> +}
>> +
>> +void ovs_sw_flow_stats_clear(struct sw_flow *flow)
>> +{
>> +       int cpu;
>> +
>> +       for_each_possible_cpu(cpu) {
>> +               struct sw_flow_stats *stats = &flow->stats[cpu];
>> +
>> +               spin_lock(&stats->lock);
>> +               stats->used = 0;
>> +               stats->packet_count = 0;
>> +               stats->byte_count = 0;
>> +               stats->tcp_flags = 0;
>> +               spin_unlock(&stats->lock);
>> +       }
>>  }
>>
>>  static int check_header(struct sk_buff *skb, int len)
>> diff --git a/datapath/flow.h b/datapath/flow.h
>> index 91a3022..987e9e5 100644
>> --- a/datapath/flow.h
>> +++ b/datapath/flow.h
>> @@ -19,6 +19,7 @@
>>  #ifndef FLOW_H
>>  #define FLOW_H 1
>>
>> +#include <linux/cache.h>
>>  #include <linux/kernel.h>
>>  #include <linux/netlink.h>
>>  #include <linux/openvswitch.h>
>> @@ -146,6 +147,14 @@ struct sw_flow_actions {
>>         struct nlattr actions[];
>>  };
>>
>> +struct sw_flow_stats {
>> +       u64 packet_count;               /* Number of packets matched. */
>> +       u64 byte_count;                 /* Number of bytes matched. */
>> +       unsigned long used;             /* Last used time (in jiffies). */
>> +       spinlock_t lock;                /* Lock for values below. */
>> +       u8 tcp_flags;                   /* Union of seen TCP flags. */
>> +} ____cacheline_aligned;
>> +
>>  struct sw_flow {
>>         struct rcu_head rcu;
>>         struct hlist_node hash_node[2];
>> @@ -155,12 +164,7 @@ struct sw_flow {
>>         struct sw_flow_key unmasked_key;
>>         struct sw_flow_mask *mask;
>>         struct sw_flow_actions __rcu *sf_acts;
>> -
>> -       spinlock_t lock;        /* Lock for values below. */
>> -       unsigned long used;     /* Last used time (in jiffies). */
>> -       u64 packet_count;       /* Number of packets matched. */
>> -       u64 byte_count;         /* Number of bytes matched. */
>> -       u8 tcp_flags;           /* Union of seen TCP flags. */
>> +       struct sw_flow_stats stats[];
>>  };
>>
>>  struct arp_eth_header {
>> @@ -177,7 +181,9 @@ struct arp_eth_header {
>>         unsigned char       ar_tip[4];          /* target IP address
>> */
>>  } __packed;
>>
>> -void ovs_flow_used(struct sw_flow *, struct sk_buff *);
>> +void ovs_flow_stats_update(struct sw_flow *flow, struct sk_buff *skb);
>> +void ovs_flow_stats_get(struct sw_flow *flow, struct sw_flow_stats *res);
>> +void ovs_sw_flow_stats_clear(struct sw_flow *flow);
>>  u64 ovs_flow_used_time(unsigned long flow_jiffies);
>>
>>  int ovs_flow_extract(struct sk_buff *, u16 in_port, struct sw_flow_key
>> *);
>> diff --git a/datapath/flow_table.c b/datapath/flow_table.c
>> index 98eb809..23ed9ee 100644
>> --- a/datapath/flow_table.c
>> +++ b/datapath/flow_table.c
>> @@ -76,15 +76,19 @@ void ovs_flow_mask_key(struct sw_flow_key *dst, const
>> struct sw_flow_key *src,
>>  struct sw_flow *ovs_flow_alloc(void)
>>  {
>>         struct sw_flow *flow;
>> +       int cpu;
>>
>>         flow = kmem_cache_alloc(flow_cache, GFP_KERNEL);
>>         if (!flow)
>>                 return ERR_PTR(-ENOMEM);
>>
>> -       spin_lock_init(&flow->lock);
>>         flow->sf_acts = NULL;
>>         flow->mask = NULL;
>>
>> +       memset(flow->stats, 0, num_possible_cpus() * sizeof(struct
>> sw_flow_stats));
>> +       for_each_possible_cpu(cpu)
>> +               spin_lock_init(&flow->stats[cpu].lock);
>> +
>>         return flow;
>>  }
>>
>> @@ -561,11 +565,15 @@ int ovs_flow_tbl_insert(struct flow_table *table,
>> struct sw_flow *flow,
>>   * Returns zero if successful or a negative error code. */
>>  int ovs_flow_init(void)
>>  {
>> +       int flow_size;
>> +
>>         BUILD_BUG_ON(__alignof__(struct sw_flow_key) % __alignof__(long));
>>         BUILD_BUG_ON(sizeof(struct sw_flow_key) % sizeof(long));
>>
>> -       flow_cache = kmem_cache_create("sw_flow", sizeof(struct sw_flow),
>> 0,
>> -                                       0, NULL);
>> +       flow_size = sizeof(struct sw_flow) +
>> +                   (num_possible_cpus() * sizeof(struct sw_flow_stats));
>> +
>> +       flow_cache = kmem_cache_create("sw_flow", flow_size, 0, 0, NULL);
>>         if (flow_cache == NULL)
>>                 return -ENOMEM;
>>
>> --
>> 1.7.1
>>
>> _______________________________________________
>> dev mailing list
>> dev at openvswitch.org
>> http://openvswitch.org/mailman/listinfo/dev
>
>



More information about the dev mailing list