linux/include/linux/seqlock.h
<<
>>
Prefs
   1#ifndef __LINUX_SEQLOCK_H
   2#define __LINUX_SEQLOCK_H
   3/*
   4 * Reader/writer consistent mechanism without starving writers. This type of
   5 * lock for data where the reader wants a consistent set of information
   6 * and is willing to retry if the information changes.  Readers never
   7 * block but they may have to retry if a writer is in
   8 * progress. Writers do not wait for readers. 
   9 *
  10 * This is not as cache friendly as brlock. Also, this will not work
  11 * for data that contains pointers, because any writer could
  12 * invalidate a pointer that a reader was following.
  13 *
  14 * Expected reader usage:
  15 *      do {
  16 *          seq = read_seqbegin(&foo);
  17 *      ...
  18 *      } while (read_seqretry(&foo, seq));
  19 *
  20 *
  21 * On non-SMP the spin locks disappear but the writer still needs
  22 * to increment the sequence variables because an interrupt routine could
  23 * change the state of the data.
  24 *
  25 * Based on x86_64 vsyscall gettimeofday 
  26 * by Keith Owens and Andrea Arcangeli
  27 */
  28
  29#include <linux/spinlock.h>
  30#include <linux/preempt.h>
  31
  32typedef struct {
  33        unsigned sequence;
  34        spinlock_t lock;
  35} seqlock_t;
  36
  37/*
  38 * These macros triggered gcc-3.x compile-time problems.  We think these are
  39 * OK now.  Be cautious.
  40 */
  41#define __SEQLOCK_UNLOCKED(lockname) \
  42                 { 0, __SPIN_LOCK_UNLOCKED(lockname) }
  43
  44#define SEQLOCK_UNLOCKED \
  45                 __SEQLOCK_UNLOCKED(old_style_seqlock_init)
  46
  47#define seqlock_init(x)                                 \
  48        do {                                            \
  49                (x)->sequence = 0;                      \
  50                spin_lock_init(&(x)->lock);             \
  51        } while (0)
  52
  53#define DEFINE_SEQLOCK(x) \
  54                seqlock_t x = __SEQLOCK_UNLOCKED(x)
  55
  56/* Lock out other writers and update the count.
  57 * Acts like a normal spin_lock/unlock.
  58 * Don't need preempt_disable() because that is in the spin_lock already.
  59 */
  60static inline void write_seqlock(seqlock_t *sl)
  61{
  62        spin_lock(&sl->lock);
  63        ++sl->sequence;
  64        smp_wmb();
  65}
  66
  67static inline void write_sequnlock(seqlock_t *sl)
  68{
  69        smp_wmb();
  70        sl->sequence++;
  71        spin_unlock(&sl->lock);
  72}
  73
  74static inline int write_tryseqlock(seqlock_t *sl)
  75{
  76        int ret = spin_trylock(&sl->lock);
  77
  78        if (ret) {
  79                ++sl->sequence;
  80                smp_wmb();
  81        }
  82        return ret;
  83}
  84
  85/* Start of read calculation -- fetch last complete writer token */
  86static __always_inline unsigned read_seqbegin(const seqlock_t *sl)
  87{
  88        unsigned ret;
  89
  90repeat:
  91        ret = sl->sequence;
  92        smp_rmb();
  93        if (unlikely(ret & 1)) {
  94                cpu_relax();
  95                goto repeat;
  96        }
  97
  98        return ret;
  99}
 100
 101/*
 102 * Test if reader processed invalid data.
 103 *
 104 * If sequence value changed then writer changed data while in section.
 105 */
 106static __always_inline int read_seqretry(const seqlock_t *sl, unsigned start)
 107{
 108        smp_rmb();
 109
 110        return (sl->sequence != start);
 111}
 112
 113
 114/*
 115 * Version using sequence counter only.
 116 * This can be used when code has its own mutex protecting the
 117 * updating starting before the write_seqcountbeqin() and ending
 118 * after the write_seqcount_end().
 119 */
 120
 121typedef struct seqcount {
 122        unsigned sequence;
 123} seqcount_t;
 124
 125#define SEQCNT_ZERO { 0 }
 126#define seqcount_init(x)        do { *(x) = (seqcount_t) SEQCNT_ZERO; } while (0)
 127
 128/* Start of read using pointer to a sequence counter only.  */
 129static inline unsigned read_seqcount_begin(const seqcount_t *s)
 130{
 131        unsigned ret;
 132
 133repeat:
 134        ret = s->sequence;
 135        smp_rmb();
 136        if (unlikely(ret & 1)) {
 137                cpu_relax();
 138                goto repeat;
 139        }
 140        return ret;
 141}
 142
 143/*
 144 * Test if reader processed invalid data because sequence number has changed.
 145 */
 146static inline int read_seqcount_retry(const seqcount_t *s, unsigned start)
 147{
 148        smp_rmb();
 149
 150        return s->sequence != start;
 151}
 152
 153
 154/*
 155 * Sequence counter only version assumes that callers are using their
 156 * own mutexing.
 157 */
 158static inline void write_seqcount_begin(seqcount_t *s)
 159{
 160        s->sequence++;
 161        smp_wmb();
 162}
 163
 164static inline void write_seqcount_end(seqcount_t *s)
 165{
 166        smp_wmb();
 167        s->sequence++;
 168}
 169
 170/*
 171 * Possible sw/hw IRQ protected versions of the interfaces.
 172 */
 173#define write_seqlock_irqsave(lock, flags)                              \
 174        do { local_irq_save(flags); write_seqlock(lock); } while (0)
 175#define write_seqlock_irq(lock)                                         \
 176        do { local_irq_disable();   write_seqlock(lock); } while (0)
 177#define write_seqlock_bh(lock)                                          \
 178        do { local_bh_disable();    write_seqlock(lock); } while (0)
 179
 180#define write_sequnlock_irqrestore(lock, flags)                         \
 181        do { write_sequnlock(lock); local_irq_restore(flags); } while(0)
 182#define write_sequnlock_irq(lock)                                       \
 183        do { write_sequnlock(lock); local_irq_enable(); } while(0)
 184#define write_sequnlock_bh(lock)                                        \
 185        do { write_sequnlock(lock); local_bh_enable(); } while(0)
 186
 187#define read_seqbegin_irqsave(lock, flags)                              \
 188        ({ local_irq_save(flags);   read_seqbegin(lock); })
 189
 190#define read_seqretry_irqrestore(lock, iv, flags)                       \
 191        ({                                                              \
 192                int ret = read_seqretry(lock, iv);                      \
 193                local_irq_restore(flags);                               \
 194                ret;                                                    \
 195        })
 196
 197#endif /* __LINUX_SEQLOCK_H */
 198
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.