Skip to content

Commit 09d4e0e

Browse files
paulusmackozbenh
authored andcommitted
lib: Provide generic atomic64_t implementation
Many processor architectures have no 64-bit atomic instructions, but we need atomic64_t in order to support the perf_counter subsystem. This adds an implementation of 64-bit atomic operations using hashed spinlocks to provide atomicity. For each atomic operation, the address of the atomic64_t variable is hashed to an index into an array of 16 spinlocks. That spinlock is taken (with interrupts disabled) around the operation, which can then be coded non-atomically within the lock. On UP, all the spinlock manipulation goes away and we simply disable interrupts around each operation. In fact gcc eliminates the whole atomic64_lock variable as well. Signed-off-by: Paul Mackerras <paulus@samba.org> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
1 parent 4c75f84 commit 09d4e0e

File tree

4 files changed

+225
-0
lines changed

4 files changed

+225
-0
lines changed

include/asm-generic/atomic64.h

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Generic implementation of 64-bit atomics using spinlocks,
3+
* useful on processors that don't have 64-bit atomic instructions.
4+
*
5+
* Copyright © 2009 Paul Mackerras, IBM Corp. <paulus@au1.ibm.com>
6+
*
7+
* This program is free software; you can redistribute it and/or
8+
* modify it under the terms of the GNU General Public License
9+
* as published by the Free Software Foundation; either version
10+
* 2 of the License, or (at your option) any later version.
11+
*/
12+
#ifndef _ASM_GENERIC_ATOMIC64_H
13+
#define _ASM_GENERIC_ATOMIC64_H
14+
15+
typedef struct {
16+
long long counter;
17+
} atomic64_t;
18+
19+
#define ATOMIC64_INIT(i) { (i) }
20+
21+
extern long long atomic64_read(const atomic64_t *v);
22+
extern void atomic64_set(atomic64_t *v, long long i);
23+
extern void atomic64_add(long long a, atomic64_t *v);
24+
extern long long atomic64_add_return(long long a, atomic64_t *v);
25+
extern void atomic64_sub(long long a, atomic64_t *v);
26+
extern long long atomic64_sub_return(long long a, atomic64_t *v);
27+
extern long long atomic64_dec_if_positive(atomic64_t *v);
28+
extern long long atomic64_cmpxchg(atomic64_t *v, long long o, long long n);
29+
extern long long atomic64_xchg(atomic64_t *v, long long new);
30+
extern int atomic64_add_unless(atomic64_t *v, long long a, long long u);
31+
32+
#define atomic64_add_negative(a, v) (atomic64_add_return((a), (v)) < 0)
33+
#define atomic64_inc(v) atomic64_add(1LL, (v))
34+
#define atomic64_inc_return(v) atomic64_add_return(1LL, (v))
35+
#define atomic64_inc_and_test(v) (atomic64_inc_return(v) == 0)
36+
#define atomic64_sub_and_test(a, v) (atomic64_sub_return((a), (v)) == 0)
37+
#define atomic64_dec(v) atomic64_sub(1LL, (v))
38+
#define atomic64_dec_return(v) atomic64_sub_return(1LL, (v))
39+
#define atomic64_dec_and_test(v) (atomic64_dec_return((v)) == 0)
40+
#define atomic64_inc_not_zero(v) atomic64_add_unless((v), 1LL, 0LL)
41+
42+
#endif /* _ASM_GENERIC_ATOMIC64_H */

lib/Kconfig

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,4 +194,10 @@ config DISABLE_OBSOLETE_CPUMASK_FUNCTIONS
194194
config NLATTR
195195
bool
196196

197+
#
198+
# Generic 64-bit atomic support is selected if needed
199+
#
200+
config GENERIC_ATOMIC64
201+
bool
202+
197203
endmenu

lib/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ obj-$(CONFIG_DMA_API_DEBUG) += dma-debug.o
9595

9696
obj-$(CONFIG_GENERIC_CSUM) += checksum.o
9797

98+
obj-$(CONFIG_GENERIC_ATOMIC64) += atomic64.o
99+
98100
hostprogs-y := gen_crc32table
99101
clean-files := crc32table.h
100102

lib/atomic64.c

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
/*
2+
* Generic implementation of 64-bit atomics using spinlocks,
3+
* useful on processors that don't have 64-bit atomic instructions.
4+
*
5+
* Copyright © 2009 Paul Mackerras, IBM Corp. <paulus@au1.ibm.com>
6+
*
7+
* This program is free software; you can redistribute it and/or
8+
* modify it under the terms of the GNU General Public License
9+
* as published by the Free Software Foundation; either version
10+
* 2 of the License, or (at your option) any later version.
11+
*/
12+
#include <linux/types.h>
13+
#include <linux/cache.h>
14+
#include <linux/spinlock.h>
15+
#include <linux/init.h>
16+
#include <asm/atomic.h>
17+
18+
/*
19+
* We use a hashed array of spinlocks to provide exclusive access
20+
* to each atomic64_t variable. Since this is expected to used on
21+
* systems with small numbers of CPUs (<= 4 or so), we use a
22+
* relatively small array of 16 spinlocks to avoid wasting too much
23+
* memory on the spinlock array.
24+
*/
25+
#define NR_LOCKS 16
26+
27+
/*
28+
* Ensure each lock is in a separate cacheline.
29+
*/
30+
static union {
31+
spinlock_t lock;
32+
char pad[L1_CACHE_BYTES];
33+
} atomic64_lock[NR_LOCKS] __cacheline_aligned_in_smp;
34+
35+
static inline spinlock_t *lock_addr(const atomic64_t *v)
36+
{
37+
unsigned long addr = (unsigned long) v;
38+
39+
addr >>= L1_CACHE_SHIFT;
40+
addr ^= (addr >> 8) ^ (addr >> 16);
41+
return &atomic64_lock[addr & (NR_LOCKS - 1)].lock;
42+
}
43+
44+
long long atomic64_read(const atomic64_t *v)
45+
{
46+
unsigned long flags;
47+
spinlock_t *lock = lock_addr(v);
48+
long long val;
49+
50+
spin_lock_irqsave(lock, flags);
51+
val = v->counter;
52+
spin_unlock_irqrestore(lock, flags);
53+
return val;
54+
}
55+
56+
void atomic64_set(atomic64_t *v, long long i)
57+
{
58+
unsigned long flags;
59+
spinlock_t *lock = lock_addr(v);
60+
61+
spin_lock_irqsave(lock, flags);
62+
v->counter = i;
63+
spin_unlock_irqrestore(lock, flags);
64+
}
65+
66+
void atomic64_add(long long a, atomic64_t *v)
67+
{
68+
unsigned long flags;
69+
spinlock_t *lock = lock_addr(v);
70+
71+
spin_lock_irqsave(lock, flags);
72+
v->counter += a;
73+
spin_unlock_irqrestore(lock, flags);
74+
}
75+
76+
long long atomic64_add_return(long long a, atomic64_t *v)
77+
{
78+
unsigned long flags;
79+
spinlock_t *lock = lock_addr(v);
80+
long long val;
81+
82+
spin_lock_irqsave(lock, flags);
83+
val = v->counter += a;
84+
spin_unlock_irqrestore(lock, flags);
85+
return val;
86+
}
87+
88+
void atomic64_sub(long long a, atomic64_t *v)
89+
{
90+
unsigned long flags;
91+
spinlock_t *lock = lock_addr(v);
92+
93+
spin_lock_irqsave(lock, flags);
94+
v->counter -= a;
95+
spin_unlock_irqrestore(lock, flags);
96+
}
97+
98+
long long atomic64_sub_return(long long a, atomic64_t *v)
99+
{
100+
unsigned long flags;
101+
spinlock_t *lock = lock_addr(v);
102+
long long val;
103+
104+
spin_lock_irqsave(lock, flags);
105+
val = v->counter -= a;
106+
spin_unlock_irqrestore(lock, flags);
107+
return val;
108+
}
109+
110+
long long atomic64_dec_if_positive(atomic64_t *v)
111+
{
112+
unsigned long flags;
113+
spinlock_t *lock = lock_addr(v);
114+
long long val;
115+
116+
spin_lock_irqsave(lock, flags);
117+
val = v->counter - 1;
118+
if (val >= 0)
119+
v->counter = val;
120+
spin_unlock_irqrestore(lock, flags);
121+
return val;
122+
}
123+
124+
long long atomic64_cmpxchg(atomic64_t *v, long long o, long long n)
125+
{
126+
unsigned long flags;
127+
spinlock_t *lock = lock_addr(v);
128+
long long val;
129+
130+
spin_lock_irqsave(lock, flags);
131+
val = v->counter;
132+
if (val == o)
133+
v->counter = n;
134+
spin_unlock_irqrestore(lock, flags);
135+
return val;
136+
}
137+
138+
long long atomic64_xchg(atomic64_t *v, long long new)
139+
{
140+
unsigned long flags;
141+
spinlock_t *lock = lock_addr(v);
142+
long long val;
143+
144+
spin_lock_irqsave(lock, flags);
145+
val = v->counter;
146+
v->counter = new;
147+
spin_unlock_irqrestore(lock, flags);
148+
return val;
149+
}
150+
151+
int atomic64_add_unless(atomic64_t *v, long long a, long long u)
152+
{
153+
unsigned long flags;
154+
spinlock_t *lock = lock_addr(v);
155+
int ret = 1;
156+
157+
spin_lock_irqsave(lock, flags);
158+
if (v->counter != u) {
159+
v->counter += a;
160+
ret = 0;
161+
}
162+
spin_unlock_irqrestore(lock, flags);
163+
return ret;
164+
}
165+
166+
static int init_atomic64_lock(void)
167+
{
168+
int i;
169+
170+
for (i = 0; i < NR_LOCKS; ++i)
171+
spin_lock_init(&atomic64_lock[i].lock);
172+
return 0;
173+
}
174+
175+
pure_initcall(init_atomic64_lock);

0 commit comments

Comments
 (0)