Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[PATCH] bitops: generic {,test_and_}{set,clear,change}_bit()

This patch introduces the C-language equivalents of the functions below:

void set_bit(int nr, volatile unsigned long *addr);
void clear_bit(int nr, volatile unsigned long *addr);
void change_bit(int nr, volatile unsigned long *addr);
int test_and_set_bit(int nr, volatile unsigned long *addr);
int test_and_clear_bit(int nr, volatile unsigned long *addr);
int test_and_change_bit(int nr, volatile unsigned long *addr);

In include/asm-generic/bitops/atomic.h

This code largely copied from:

include/asm-powerpc/bitops.h
include/asm-parisc/bitops.h
include/asm-parisc/atomic.h

Signed-off-by: Akinobu Mita <mita@miraclelinux.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>

authored by

Akinobu Mita and committed by
Linus Torvalds
7a8a2429 67b0ad57

+191
+191
include/asm-generic/bitops/atomic.h
··· 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 */