Home | History | Annotate | Download | only in bitops
      1 #ifndef _ASM_GENERIC_BITOPS_ATOMIC_H_
      2 #define _ASM_GENERIC_BITOPS_ATOMIC_H_
      3 
      4 #include <asm/types.h>
      5 
      6 #define BITOP_MASK(nr)		(1UL << ((nr) % BITS_PER_LONG))
      7 #define BITOP_WORD(nr)		((nr) / BITS_PER_LONG)
      8 
      9 #ifdef CONFIG_SMP
     10 #include <asm/spinlock.h>
     11 #include <asm/cache.h>		/* we use L1_CACHE_BYTES */
     12 
     13 /* Use an array of spinlocks for our atomic_ts.
     14  * Hash function to index into a different SPINLOCK.
     15  * Since "a" is usually an address, use one spinlock per cacheline.
     16  */
     17 #  define ATOMIC_HASH_SIZE 4
     18 #  define ATOMIC_HASH(a) (&(__atomic_hash[ (((unsigned long) a)/L1_CACHE_BYTES) & (ATOMIC_HASH_SIZE-1) ]))
     19 
     20 extern raw_spinlock_t __atomic_hash[ATOMIC_HASH_SIZE] __lock_aligned;
     21 
     22 /* Can't use raw_spin_lock_irq because of #include problems, so
     23  * this is the substitute */
     24 #define _atomic_spin_lock_irqsave(l,f) do {	\
     25 	raw_spinlock_t *s = ATOMIC_HASH(l);	\
     26 	local_irq_save(f);			\
     27 	__raw_spin_lock(s);			\
     28 } while(0)
     29 
     30 #define _atomic_spin_unlock_irqrestore(l,f) do {	\
     31 	raw_spinlock_t *s = ATOMIC_HASH(l);		\
     32 	__raw_spin_unlock(s);				\
     33 	local_irq_restore(f);				\
     34 } while(0)
     35 
     36 
     37 #else
     38 #  define _atomic_spin_lock_irqsave(l,f) do { local_irq_save(f); } while (0)
     39 #  define _atomic_spin_unlock_irqrestore(l,f) do { local_irq_restore(f); } while (0)
     40 #endif
     41 
     42 /*
     43  * NMI events can occur at any time, including when interrupts have been
     44  * disabled by *_irqsave().  So you can get NMI events occurring while a
     45  * *_bit function is holding a spin lock.  If the NMI handler also wants
     46  * to do bit manipulation (and they do) then you can get a deadlock
     47  * between the original caller of *_bit() and the NMI handler.
     48  *
     49  * by Keith Owens
     50  */
     51 
     52 /**
     53  * set_bit - Atomically set a bit in memory
     54  * @nr: the bit to set
     55  * @addr: the address to start counting from
     56  *
     57  * This function is atomic and may not be reordered.  See __set_bit()
     58  * if you do not require the atomic guarantees.
     59  *
     60  * Note: there are no guarantees that this function will not be reordered
     61  * on non x86 architectures, so if you are writting portable code,
     62  * make sure not to rely on its reordering guarantees.
     63  *
     64  * Note that @nr may be almost arbitrarily large; this function is not
     65  * restricted to acting on a single-word quantity.
     66  */
     67 static inline void set_bit(int nr, volatile unsigned long *addr)
     68 {
     69 	unsigned long mask = BITOP_MASK(nr);
     70 	unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr);
     71 	unsigned long flags;
     72 
     73 	_atomic_spin_lock_irqsave(p, flags);
     74 	*p  |= mask;
     75 	_atomic_spin_unlock_irqrestore(p, flags);
     76 }
     77 
     78 /**
     79  * clear_bit - Clears a bit in memory
     80  * @nr: Bit to clear
     81  * @addr: Address to start counting from
     82  *
     83  * clear_bit() is atomic and may not be reordered.  However, it does
     84  * not contain a memory barrier, so if it is used for locking purposes,
     85  * you should call smp_mb__before_clear_bit() and/or smp_mb__after_clear_bit()
     86  * in order to ensure changes are visible on other processors.
     87  */
     88 static inline void clear_bit(int nr, volatile unsigned long *addr)
     89 {
     90 	unsigned long mask = BITOP_MASK(nr);
     91 	unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr);
     92 	unsigned long flags;
     93 
     94 	_atomic_spin_lock_irqsave(p, flags);
     95 	*p &= ~mask;
     96 	_atomic_spin_unlock_irqrestore(p, flags);
     97 }
     98 
     99 /**
    100  * change_bit - Toggle a bit in memory
    101  * @nr: Bit to change
    102  * @addr: Address to start counting from
    103  *
    104  * change_bit() is atomic and may not be reordered. It may be
    105  * reordered on other architectures than x86.
    106  * Note that @nr may be almost arbitrarily large; this function is not
    107  * restricted to acting on a single-word quantity.
    108  */
    109 static inline void change_bit(int nr, volatile unsigned long *addr)
    110 {
    111 	unsigned long mask = BITOP_MASK(nr);
    112 	unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr);
    113 	unsigned long flags;
    114 
    115 	_atomic_spin_lock_irqsave(p, flags);
    116 	*p ^= mask;
    117 	_atomic_spin_unlock_irqrestore(p, flags);
    118 }
    119 
    120 /**
    121  * test_and_set_bit - Set a bit and return its old value
    122  * @nr: Bit to set
    123  * @addr: Address to count from
    124  *
    125  * This operation is atomic and cannot be reordered.
    126  * It may be reordered on other architectures than x86.
    127  * It also implies a memory barrier.
    128  */
    129 static inline int test_and_set_bit(int nr, volatile unsigned long *addr)
    130 {
    131 	unsigned long mask = BITOP_MASK(nr);
    132 	unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr);
    133 	unsigned long old;
    134 	unsigned long flags;
    135 
    136 	_atomic_spin_lock_irqsave(p, flags);
    137 	old = *p;
    138 	*p = old | mask;
    139 	_atomic_spin_unlock_irqrestore(p, flags);
    140 
    141 	return (old & mask) != 0;
    142 }
    143 
    144 /**
    145  * test_and_clear_bit - Clear a bit and return its old value
    146  * @nr: Bit to clear
    147  * @addr: Address to count from
    148  *
    149  * This operation is atomic and cannot be reordered.
    150  * It can be reorderdered on other architectures other than x86.
    151  * It also implies a memory barrier.
    152  */
    153 static inline int test_and_clear_bit(int nr, volatile unsigned long *addr)
    154 {
    155 	unsigned long mask = BITOP_MASK(nr);
    156 	unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr);
    157 	unsigned long old;
    158 	unsigned long flags;
    159 
    160 	_atomic_spin_lock_irqsave(p, flags);
    161 	old = *p;
    162 	*p = old & ~mask;
    163 	_atomic_spin_unlock_irqrestore(p, flags);
    164 
    165 	return (old & mask) != 0;
    166 }
    167 
    168 /**
    169  * test_and_change_bit - Change a bit and return its old value
    170  * @nr: Bit to change
    171  * @addr: Address to count from
    172  *
    173  * This operation is atomic and cannot be reordered.
    174  * It also implies a memory barrier.
    175  */
    176 static inline int test_and_change_bit(int nr, volatile unsigned long *addr)
    177 {
    178 	unsigned long mask = BITOP_MASK(nr);
    179 	unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr);
    180 	unsigned long old;
    181 	unsigned long flags;
    182 
    183 	_atomic_spin_lock_irqsave(p, flags);
    184 	old = *p;
    185 	*p = old ^ mask;
    186 	_atomic_spin_unlock_irqrestore(p, flags);
    187 
    188 	return (old & mask) != 0;
    189 }
    190 
    191 #endif /* _ASM_GENERIC_BITOPS_ATOMIC_H */
    192