Something went wrong. Try again.
Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243/* SPDX-License-Identifier: GPL-2.0 */#ifndef _LINUX_U64_STATS_SYNC_H#define _LINUX_U64_STATS_SYNC_H
/* * Protect against 64-bit values tearing on 32-bit architectures. This is * typically used for statistics read/update in different subsystems. * * Key points : * * - Use a seqcount on 32-bit * - The whole thing is a no-op on 64-bit architectures. * * Usage constraints: * * 1) Write side must ensure mutual exclusion, or one seqcount update could * be lost, thus blocking readers forever. * * 2) Write side must disable preemption, or a seqcount reader can preempt the * writer and also spin forever. * * 3) Write side must use the _irqsave() variant if other writers, or a reader, * can be invoked from an IRQ context. On 64bit systems this variant does not * disable interrupts. * * 4) If reader fetches several counters, there is no guarantee the whole values * are consistent w.r.t. each other (remember point #2: seqcounts are not * used for 64bit architectures). * * 5) Readers are allowed to sleep or be preempted/interrupted: they perform * pure reads. * * Usage : * * Stats producer (writer) should use following template granted it already got * an exclusive access to counters (a lock is already taken, or per cpu * data is used [in a non preemptable context]) * * spin_lock_bh(...) or other synchronization to get exclusive access * ... * u64_stats_update_begin(&stats->syncp); * u64_stats_add(&stats->bytes64, len); // non atomic operation * u64_stats_inc(&stats->packets64); // non atomic operation * u64_stats_update_end(&stats->syncp); * * While a consumer (reader) should use following template to get consistent * snapshot for each variable (but no guarantee on several ones) * * u64 tbytes, tpackets; * unsigned int start; * * do { * start = u64_stats_fetch_begin(&stats->syncp); * tbytes = u64_stats_read(&stats->bytes64); // non atomic operation * tpackets = u64_stats_read(&stats->packets64); // non atomic operation * } while (u64_stats_fetch_retry(&stats->syncp, start)); * * * Example of use in drivers/net/loopback.c, using per_cpu containers, * in BH disabled context. */#include <linux/seqlock.h>
struct u64_stats_sync {#if BITS_PER_LONG == 32 seqcount_t seq;#endif};
#if BITS_PER_LONG == 64#include <asm/local64.h>
typedef struct { local64_t v;} u64_stats_t ;
static inline u64 u64_stats_read(const u64_stats_t *p){ return local64_read(&p->v);}
static inline void *u64_stats_copy(void *dst, const void *src, size_t len){ BUILD_BUG_ON(len % sizeof(u64_stats_t)); for (size_t i = 0; i < len / sizeof(u64_stats_t); i++) ((u64 *)dst)[i] = local64_read(&((local64_t *)src)[i]); return dst;}
static inline void u64_stats_set(u64_stats_t *p, u64 val){ local64_set(&p->v, val);}
static inline void u64_stats_add(u64_stats_t *p, unsigned long val){ local64_add(val, &p->v);}
static inline void u64_stats_sub(u64_stats_t *p, s64 val){ local64_sub(val, &p->v);}
static inline void u64_stats_inc(u64_stats_t *p){ local64_inc(&p->v);}
static inline void u64_stats_init(struct u64_stats_sync *syncp) { }static inline void __u64_stats_update_begin(struct u64_stats_sync *syncp) { }static inline void __u64_stats_update_end(struct u64_stats_sync *syncp) { }static inline unsigned long __u64_stats_irqsave(void) { return 0; }static inline void __u64_stats_irqrestore(unsigned long flags) { }static inline unsigned int __u64_stats_fetch_begin(const struct u64_stats_sync *syncp){ return 0;}static inline bool __u64_stats_fetch_retry(const struct u64_stats_sync *syncp, unsigned int start){ return false;}
#else /* 64 bit */#include <linux/string.h>
typedef struct { u64 v;} u64_stats_t;
static inline u64 u64_stats_read(const u64_stats_t *p){ return p->v;}
static inline void *u64_stats_copy(void *dst, const void *src, size_t len){ BUILD_BUG_ON(len % sizeof(u64_stats_t)); return memcpy(dst, src, len);}
static inline void u64_stats_set(u64_stats_t *p, u64 val){ p->v = val;}
static inline void u64_stats_add(u64_stats_t *p, unsigned long val){ p->v += val;}
static inline void u64_stats_sub(u64_stats_t *p, s64 val){ p->v -= val;}
static inline void u64_stats_inc(u64_stats_t *p){ p->v++;}
#define u64_stats_init(syncp) \ do { \ struct u64_stats_sync *__s = (syncp); \ seqcount_init(&__s->seq); \ } while (0)
static inline void __u64_stats_update_begin(struct u64_stats_sync *syncp){ preempt_disable_nested(); write_seqcount_begin(&syncp->seq);}
static inline void __u64_stats_update_end(struct u64_stats_sync *syncp){ write_seqcount_end(&syncp->seq); preempt_enable_nested();}
static inline unsigned long __u64_stats_irqsave(void){ unsigned long flags;
local_irq_save(flags); return flags;}
static inline void __u64_stats_irqrestore(unsigned long flags){ local_irq_restore(flags);}
static inline unsigned int __u64_stats_fetch_begin(const struct u64_stats_sync *syncp){ return read_seqcount_begin(&syncp->seq);}
static inline bool __u64_stats_fetch_retry(const struct u64_stats_sync *syncp, unsigned int start){ return read_seqcount_retry(&syncp->seq, start);}#endif /* !64 bit */
static inline void u64_stats_update_begin(struct u64_stats_sync *syncp){ __u64_stats_update_begin(syncp);}
static inline void u64_stats_update_end(struct u64_stats_sync *syncp){ __u64_stats_update_end(syncp);}
static inline unsigned long u64_stats_update_begin_irqsave(struct u64_stats_sync *syncp){ unsigned long flags = __u64_stats_irqsave();
__u64_stats_update_begin(syncp); return flags;}
static inline void u64_stats_update_end_irqrestore(struct u64_stats_sync *syncp, unsigned long flags){ __u64_stats_update_end(syncp); __u64_stats_irqrestore(flags);}
static inline unsigned int u64_stats_fetch_begin(const struct u64_stats_sync *syncp){ return __u64_stats_fetch_begin(syncp);}
static inline bool u64_stats_fetch_retry(const struct u64_stats_sync *syncp, unsigned int start){ return __u64_stats_fetch_retry(syncp, start);}
#endif /* _LINUX_U64_STATS_SYNC_H */