A read operation from a non atomic shared value (without external locking) can return incorrect values. Using the atomic semantics prevents this from happening.
However: * No memory barriers are used. We don't need that kind of consistency for statistics (we use relaxed operations). * The updates are not atomic, just the loads and stores. This is ok because there's a single writer. Suggested-by: Ethan Jackson <et...@nicira.com> Signed-off-by: Daniele Di Proietto <diproiet...@vmware.com> --- lib/dpif-netdev.c | 69 ++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 50 insertions(+), 19 deletions(-) diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c index b5cfdcb..a882e9c 100644 --- a/lib/dpif-netdev.c +++ b/lib/dpif-netdev.c @@ -239,10 +239,10 @@ struct dp_netdev_port { /* Contained by struct dp_netdev_flow's 'stats' member. */ struct dp_netdev_flow_stats { - long long int used; /* Last used time, in monotonic msecs. */ - long long int packet_count; /* Number of packets matched. */ - long long int byte_count; /* Number of bytes matched. */ - uint16_t tcp_flags; /* Bitwise-OR of seen tcp_flags values. */ + ATOMIC(long long) used; /* Last used time, in monotonic msecs. */ + ATOMIC(long long) packet_count; /* Number of packets matched. */ + ATOMIC(long long) byte_count; /* Number of bytes matched. */ + ATOMIC(uint16_t) tcp_flags; /* Bitwise-OR of seen tcp_flags values. */ }; /* A flow in 'dp_netdev_pmd_thread's 'flow_table'. @@ -338,7 +338,7 @@ static void dp_netdev_actions_free(struct dp_netdev_actions *); /* Contained by struct dp_netdev_pmd_thread's 'stats' member. */ struct dp_netdev_pmd_stats { /* Indexed by DP_STAT_*. */ - unsigned long long int n[DP_N_STATS]; + ATOMIC(unsigned long long) n[DP_N_STATS]; }; /* PMD: Poll modes drivers. PMD accesses devices via polling to eliminate @@ -754,10 +754,15 @@ dpif_netdev_get_stats(const struct dpif *dpif, struct dpif_dp_stats *stats) stats->n_flows = stats->n_hit = stats->n_missed = stats->n_lost = 0; CMAP_FOR_EACH (pmd, node, &dp->poll_threads) { + unsigned long long n; stats->n_flows += cmap_count(&pmd->flow_table); - stats->n_hit += pmd->stats.n[DP_STAT_HIT]; - stats->n_missed += pmd->stats.n[DP_STAT_MISS]; - stats->n_lost += pmd->stats.n[DP_STAT_LOST]; + + atomic_read_relaxed(&pmd->stats.n[DP_STAT_HIT], &n); + stats->n_hit += n; + atomic_read_relaxed(&pmd->stats.n[DP_STAT_MISS], &n); + stats->n_missed += n; + atomic_read_relaxed(&pmd->stats.n[DP_STAT_LOST], &n); + stats->n_lost += n; } stats->n_masks = UINT32_MAX; stats->n_mask_hit = UINT64_MAX; @@ -1545,13 +1550,23 @@ dp_netdev_pmd_find_flow(const struct dp_netdev_pmd_thread *pmd, } static void -get_dpif_flow_stats(const struct dp_netdev_flow *netdev_flow, +get_dpif_flow_stats(const struct dp_netdev_flow *netdev_flow_, struct dpif_flow_stats *stats) { - stats->n_packets = netdev_flow->stats.packet_count; - stats->n_bytes = netdev_flow->stats.byte_count; - stats->used = netdev_flow->stats.used; - stats->tcp_flags = netdev_flow->stats.tcp_flags; + struct dp_netdev_flow *netdev_flow; + long long n; + uint16_t flags; + + netdev_flow = CONST_CAST(struct dp_netdev_flow *, netdev_flow_); + + atomic_read_relaxed(&netdev_flow->stats.packet_count, &n); + stats->n_packets = n; + atomic_read_relaxed(&netdev_flow->stats.byte_count, &n); + stats->n_bytes = n; + atomic_read_relaxed(&netdev_flow->stats.used, &n); + stats->used = n; + atomic_read_relaxed(&netdev_flow->stats.tcp_flags, &flags); + stats->tcp_flags = flags; } /* Converts to the dpif_flow format, using 'key_buf' and 'mask_buf' for @@ -2678,19 +2693,35 @@ static void dp_netdev_flow_used(struct dp_netdev_flow *netdev_flow, int cnt, int size, uint16_t tcp_flags) { - long long int now = time_msec(); + long long n, now = time_msec(); + uint16_t flags; - netdev_flow->stats.used = MAX(now, netdev_flow->stats.used); - netdev_flow->stats.packet_count += cnt; - netdev_flow->stats.byte_count += size; - netdev_flow->stats.tcp_flags |= tcp_flags; + atomic_read_relaxed(&netdev_flow->stats.used, &n); + n = MAX(now, n); + atomic_store_relaxed(&netdev_flow->stats.used, n); + + atomic_read_relaxed(&netdev_flow->stats.packet_count, &n); + n += cnt; + atomic_store_relaxed(&netdev_flow->stats.packet_count, n); + + atomic_read_relaxed(&netdev_flow->stats.byte_count, &n); + n += size; + atomic_store_relaxed(&netdev_flow->stats.byte_count, n); + + atomic_read_relaxed(&netdev_flow->stats.tcp_flags, &flags); + flags |= tcp_flags; + atomic_store_relaxed(&netdev_flow->stats.tcp_flags, flags); } static void dp_netdev_count_packet(struct dp_netdev_pmd_thread *pmd, enum dp_stat_type type, int cnt) { - pmd->stats.n[type] += cnt; + unsigned long long n; + + atomic_read_relaxed(&pmd->stats.n[type], &n); + n += cnt; + atomic_store_relaxed(&pmd->stats.n[type], n); } static int -- 2.1.4 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev