Skip to content

Commit e192832

Browse files
committed
Merge branch 'locking-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull locking updates from Ingo Molnar: "The main changes in this cycle are: - rwsem scalability improvements, phase #2, by Waiman Long, which are rather impressive: "On a 2-socket 40-core 80-thread Skylake system with 40 reader and writer locking threads, the min/mean/max locking operations done in a 5-second testing window before the patchset were: 40 readers, Iterations Min/Mean/Max = 1,807/1,808/1,810 40 writers, Iterations Min/Mean/Max = 1,807/50,344/151,255 After the patchset, they became: 40 readers, Iterations Min/Mean/Max = 30,057/31,359/32,741 40 writers, Iterations Min/Mean/Max = 94,466/95,845/97,098" There's a lot of changes to the locking implementation that makes it similar to qrwlock, including owner handoff for more fair locking. Another microbenchmark shows how across the spectrum the improvements are: "With a locking microbenchmark running on 5.1 based kernel, the total locking rates (in kops/s) on a 2-socket Skylake system with equal numbers of readers and writers (mixed) before and after this patchset were: # of Threads Before Patch After Patch ------------ ------------ ----------- 2 2,618 4,193 4 1,202 3,726 8 802 3,622 16 729 3,359 32 319 2,826 64 102 2,744" The changes are extensive and the patch-set has been through several iterations addressing various locking workloads. There might be more regressions, but unless they are pathological I believe we want to use this new implementation as the baseline going forward. - jump-label optimizations by Daniel Bristot de Oliveira: the primary motivation was to remove IPI disturbance of isolated RT-workload CPUs, which resulted in the implementation of batched jump-label updates. Beyond the improvement of the real-time characteristics kernel, in one test this patchset improved static key update overhead from 57 msecs to just 1.4 msecs - which is a nice speedup as well. - atomic64_t cross-arch type cleanups by Mark Rutland: over the last ~10 years of atomic64_t existence the various types used by the APIs only had to be self-consistent within each architecture - which means they became wildly inconsistent across architectures. Mark puts and end to this by reworking all the atomic64 implementations to use 's64' as the base type for atomic64_t, and to ensure that this type is consistently used for parameters and return values in the API, avoiding further problems in this area. - A large set of small improvements to lockdep by Yuyang Du: type cleanups, output cleanups, function return type and othr cleanups all around the place. - A set of percpu ops cleanups and fixes by Peter Zijlstra. - Misc other changes - please see the Git log for more details" * 'locking-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (82 commits) locking/lockdep: increase size of counters for lockdep statistics locking/atomics: Use sed(1) instead of non-standard head(1) option locking/lockdep: Move mark_lock() inside CONFIG_TRACE_IRQFLAGS && CONFIG_PROVE_LOCKING x86/jump_label: Make tp_vec_nr static x86/percpu: Optimize raw_cpu_xchg() x86/percpu, sched/fair: Avoid local_clock() x86/percpu, x86/irq: Relax {set,get}_irq_regs() x86/percpu: Relax smp_processor_id() x86/percpu: Differentiate this_cpu_{}() and __this_cpu_{}() locking/rwsem: Guard against making count negative locking/rwsem: Adaptive disabling of reader optimistic spinning locking/rwsem: Enable time-based spinning on reader-owned rwsem locking/rwsem: Make rwsem->owner an atomic_long_t locking/rwsem: Enable readers spinning on writer locking/rwsem: Clarify usage of owner's nonspinaable bit locking/rwsem: Wake up almost all readers in wait queue locking/rwsem: More optimal RT task handling of null owner locking/rwsem: Always release wait_lock before waking up tasks locking/rwsem: Implement lock handoff to prevent lock starvation locking/rwsem: Make rwsem_spin_on_owner() return owner state ...
2 parents 46f1ec2 + 9156e54 commit e192832

File tree

55 files changed

+2788
-2020
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+2788
-2020
lines changed

Documentation/atomic_t.txt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,11 @@ Non-RMW ops:
8181

8282
The non-RMW ops are (typically) regular LOADs and STOREs and are canonically
8383
implemented using READ_ONCE(), WRITE_ONCE(), smp_load_acquire() and
84-
smp_store_release() respectively.
84+
smp_store_release() respectively. Therefore, if you find yourself only using
85+
the Non-RMW operations of atomic_t, you do not in fact need atomic_t at all
86+
and are doing it wrong.
8587

86-
The one detail to this is that atomic_set{}() should be observable to the RMW
88+
A subtle detail of atomic_set{}() is that it should be observable to the RMW
8789
ops. That is:
8890

8991
C atomic-set
@@ -200,6 +202,9 @@ These helper barriers exist because architectures have varying implicit
200202
ordering on their SMP atomic primitives. For example our TSO architectures
201203
provide full ordered atomics and these barriers are no-ops.
202204

205+
NOTE: when the atomic RmW ops are fully ordered, they should also imply a
206+
compiler barrier.
207+
203208
Thus:
204209

205210
atomic_fetch_add();

Documentation/locking/lockdep-design.txt

Lines changed: 84 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -15,34 +15,48 @@ tens of thousands of) instantiations. For example a lock in the inode
1515
struct is one class, while each inode has its own instantiation of that
1616
lock class.
1717

18-
The validator tracks the 'state' of lock-classes, and it tracks
19-
dependencies between different lock-classes. The validator maintains a
20-
rolling proof that the state and the dependencies are correct.
21-
22-
Unlike an lock instantiation, the lock-class itself never goes away: when
23-
a lock-class is used for the first time after bootup it gets registered,
24-
and all subsequent uses of that lock-class will be attached to this
25-
lock-class.
18+
The validator tracks the 'usage state' of lock-classes, and it tracks
19+
the dependencies between different lock-classes. Lock usage indicates
20+
how a lock is used with regard to its IRQ contexts, while lock
21+
dependency can be understood as lock order, where L1 -> L2 suggests that
22+
a task is attempting to acquire L2 while holding L1. From lockdep's
23+
perspective, the two locks (L1 and L2) are not necessarily related; that
24+
dependency just means the order ever happened. The validator maintains a
25+
continuing effort to prove lock usages and dependencies are correct or
26+
the validator will shoot a splat if incorrect.
27+
28+
A lock-class's behavior is constructed by its instances collectively:
29+
when the first instance of a lock-class is used after bootup the class
30+
gets registered, then all (subsequent) instances will be mapped to the
31+
class and hence their usages and dependecies will contribute to those of
32+
the class. A lock-class does not go away when a lock instance does, but
33+
it can be removed if the memory space of the lock class (static or
34+
dynamic) is reclaimed, this happens for example when a module is
35+
unloaded or a workqueue is destroyed.
2636

2737
State
2838
-----
2939

30-
The validator tracks lock-class usage history into 4 * nSTATEs + 1 separate
31-
state bits:
40+
The validator tracks lock-class usage history and divides the usage into
41+
(4 usages * n STATEs + 1) categories:
3242

43+
where the 4 usages can be:
3344
- 'ever held in STATE context'
3445
- 'ever held as readlock in STATE context'
3546
- 'ever held with STATE enabled'
3647
- 'ever held as readlock with STATE enabled'
3748

38-
Where STATE can be either one of (kernel/locking/lockdep_states.h)
39-
- hardirq
40-
- softirq
49+
where the n STATEs are coded in kernel/locking/lockdep_states.h and as of
50+
now they include:
51+
- hardirq
52+
- softirq
4153

54+
where the last 1 category is:
4255
- 'ever used' [ == !unused ]
4356

44-
When locking rules are violated, these state bits are presented in the
45-
locking error messages, inside curlies. A contrived example:
57+
When locking rules are violated, these usage bits are presented in the
58+
locking error messages, inside curlies, with a total of 2 * n STATEs bits.
59+
A contrived example:
4660

4761
modprobe/2287 is trying to acquire lock:
4862
(&sio_locks[i].lock){-.-.}, at: [<c02867fd>] mutex_lock+0x21/0x24
@@ -51,28 +65,67 @@ locking error messages, inside curlies. A contrived example:
5165
(&sio_locks[i].lock){-.-.}, at: [<c02867fd>] mutex_lock+0x21/0x24
5266

5367

54-
The bit position indicates STATE, STATE-read, for each of the states listed
55-
above, and the character displayed in each indicates:
68+
For a given lock, the bit positions from left to right indicate the usage
69+
of the lock and readlock (if exists), for each of the n STATEs listed
70+
above respectively, and the character displayed at each bit position
71+
indicates:
5672

5773
'.' acquired while irqs disabled and not in irq context
5874
'-' acquired in irq context
5975
'+' acquired with irqs enabled
6076
'?' acquired in irq context with irqs enabled.
6177

62-
Unused mutexes cannot be part of the cause of an error.
78+
The bits are illustrated with an example:
79+
80+
(&sio_locks[i].lock){-.-.}, at: [<c02867fd>] mutex_lock+0x21/0x24
81+
||||
82+
||| \-> softirq disabled and not in softirq context
83+
|| \--> acquired in softirq context
84+
| \---> hardirq disabled and not in hardirq context
85+
\----> acquired in hardirq context
86+
87+
88+
For a given STATE, whether the lock is ever acquired in that STATE
89+
context and whether that STATE is enabled yields four possible cases as
90+
shown in the table below. The bit character is able to indicate which
91+
exact case is for the lock as of the reporting time.
92+
93+
-------------------------------------------
94+
| | irq enabled | irq disabled |
95+
|-------------------------------------------|
96+
| ever in irq | ? | - |
97+
|-------------------------------------------|
98+
| never in irq | + | . |
99+
-------------------------------------------
100+
101+
The character '-' suggests irq is disabled because if otherwise the
102+
charactor '?' would have been shown instead. Similar deduction can be
103+
applied for '+' too.
104+
105+
Unused locks (e.g., mutexes) cannot be part of the cause of an error.
63106

64107

65108
Single-lock state rules:
66109
------------------------
67110

111+
A lock is irq-safe means it was ever used in an irq context, while a lock
112+
is irq-unsafe means it was ever acquired with irq enabled.
113+
68114
A softirq-unsafe lock-class is automatically hardirq-unsafe as well. The
69-
following states are exclusive, and only one of them is allowed to be
70-
set for any lock-class:
115+
following states must be exclusive: only one of them is allowed to be set
116+
for any lock-class based on its usage:
117+
118+
<hardirq-safe> or <hardirq-unsafe>
119+
<softirq-safe> or <softirq-unsafe>
71120

72-
<hardirq-safe> and <hardirq-unsafe>
73-
<softirq-safe> and <softirq-unsafe>
121+
This is because if a lock can be used in irq context (irq-safe) then it
122+
cannot be ever acquired with irq enabled (irq-unsafe). Otherwise, a
123+
deadlock may happen. For example, in the scenario that after this lock
124+
was acquired but before released, if the context is interrupted this
125+
lock will be attempted to acquire twice, which creates a deadlock,
126+
referred to as lock recursion deadlock.
74127

75-
The validator detects and reports lock usage that violate these
128+
The validator detects and reports lock usage that violates these
76129
single-lock state rules.
77130

78131
Multi-lock dependency rules:
@@ -81,15 +134,18 @@ Multi-lock dependency rules:
81134
The same lock-class must not be acquired twice, because this could lead
82135
to lock recursion deadlocks.
83136

84-
Furthermore, two locks may not be taken in different order:
137+
Furthermore, two locks can not be taken in inverse order:
85138

86139
<L1> -> <L2>
87140
<L2> -> <L1>
88141

89-
because this could lead to lock inversion deadlocks. (The validator
90-
finds such dependencies in arbitrary complexity, i.e. there can be any
91-
other locking sequence between the acquire-lock operations, the
92-
validator will still track all dependencies between locks.)
142+
because this could lead to a deadlock - referred to as lock inversion
143+
deadlock - as attempts to acquire the two locks form a circle which
144+
could lead to the two contexts waiting for each other permanently. The
145+
validator will find such dependency circle in arbitrary complexity,
146+
i.e., there can be any other locking sequence between the acquire-lock
147+
operations; the validator will still find whether these locks can be
148+
acquired in a circular fashion.
93149

94150
Furthermore, the following usage based lock dependencies are not allowed
95151
between any two lock-classes:

arch/alpha/include/asm/atomic.h

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,9 @@ static inline int atomic_fetch_##op##_relaxed(int i, atomic_t *v) \
9393
}
9494

9595
#define ATOMIC64_OP(op, asm_op) \
96-
static __inline__ void atomic64_##op(long i, atomic64_t * v) \
96+
static __inline__ void atomic64_##op(s64 i, atomic64_t * v) \
9797
{ \
98-
unsigned long temp; \
98+
s64 temp; \
9999
__asm__ __volatile__( \
100100
"1: ldq_l %0,%1\n" \
101101
" " #asm_op " %0,%2,%0\n" \
@@ -109,9 +109,9 @@ static __inline__ void atomic64_##op(long i, atomic64_t * v) \
109109
} \
110110

111111
#define ATOMIC64_OP_RETURN(op, asm_op) \
112-
static __inline__ long atomic64_##op##_return_relaxed(long i, atomic64_t * v) \
112+
static __inline__ s64 atomic64_##op##_return_relaxed(s64 i, atomic64_t * v) \
113113
{ \
114-
long temp, result; \
114+
s64 temp, result; \
115115
__asm__ __volatile__( \
116116
"1: ldq_l %0,%1\n" \
117117
" " #asm_op " %0,%3,%2\n" \
@@ -128,9 +128,9 @@ static __inline__ long atomic64_##op##_return_relaxed(long i, atomic64_t * v) \
128128
}
129129

130130
#define ATOMIC64_FETCH_OP(op, asm_op) \
131-
static __inline__ long atomic64_fetch_##op##_relaxed(long i, atomic64_t * v) \
131+
static __inline__ s64 atomic64_fetch_##op##_relaxed(s64 i, atomic64_t * v) \
132132
{ \
133-
long temp, result; \
133+
s64 temp, result; \
134134
__asm__ __volatile__( \
135135
"1: ldq_l %2,%1\n" \
136136
" " #asm_op " %2,%3,%0\n" \
@@ -246,9 +246,9 @@ static __inline__ int atomic_fetch_add_unless(atomic_t *v, int a, int u)
246246
* Atomically adds @a to @v, so long as it was not @u.
247247
* Returns the old value of @v.
248248
*/
249-
static __inline__ long atomic64_fetch_add_unless(atomic64_t *v, long a, long u)
249+
static __inline__ s64 atomic64_fetch_add_unless(atomic64_t *v, s64 a, s64 u)
250250
{
251-
long c, new, old;
251+
s64 c, new, old;
252252
smp_mb();
253253
__asm__ __volatile__(
254254
"1: ldq_l %[old],%[mem]\n"
@@ -276,9 +276,9 @@ static __inline__ long atomic64_fetch_add_unless(atomic64_t *v, long a, long u)
276276
* The function returns the old value of *v minus 1, even if
277277
* the atomic variable, v, was not decremented.
278278
*/
279-
static inline long atomic64_dec_if_positive(atomic64_t *v)
279+
static inline s64 atomic64_dec_if_positive(atomic64_t *v)
280280
{
281-
long old, tmp;
281+
s64 old, tmp;
282282
smp_mb();
283283
__asm__ __volatile__(
284284
"1: ldq_l %[old],%[mem]\n"

arch/arc/include/asm/atomic.h

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -321,14 +321,14 @@ ATOMIC_OPS(xor, ^=, CTOP_INST_AXOR_DI_R2_R2_R3)
321321
*/
322322

323323
typedef struct {
324-
aligned_u64 counter;
324+
s64 __aligned(8) counter;
325325
} atomic64_t;
326326

327327
#define ATOMIC64_INIT(a) { (a) }
328328

329-
static inline long long atomic64_read(const atomic64_t *v)
329+
static inline s64 atomic64_read(const atomic64_t *v)
330330
{
331-
unsigned long long val;
331+
s64 val;
332332

333333
__asm__ __volatile__(
334334
" ldd %0, [%1] \n"
@@ -338,7 +338,7 @@ static inline long long atomic64_read(const atomic64_t *v)
338338
return val;
339339
}
340340

341-
static inline void atomic64_set(atomic64_t *v, long long a)
341+
static inline void atomic64_set(atomic64_t *v, s64 a)
342342
{
343343
/*
344344
* This could have been a simple assignment in "C" but would need
@@ -359,9 +359,9 @@ static inline void atomic64_set(atomic64_t *v, long long a)
359359
}
360360

361361
#define ATOMIC64_OP(op, op1, op2) \
362-
static inline void atomic64_##op(long long a, atomic64_t *v) \
362+
static inline void atomic64_##op(s64 a, atomic64_t *v) \
363363
{ \
364-
unsigned long long val; \
364+
s64 val; \
365365
\
366366
__asm__ __volatile__( \
367367
"1: \n" \
@@ -372,13 +372,13 @@ static inline void atomic64_##op(long long a, atomic64_t *v) \
372372
" bnz 1b \n" \
373373
: "=&r"(val) \
374374
: "r"(&v->counter), "ir"(a) \
375-
: "cc"); \
375+
: "cc"); \
376376
} \
377377

378378
#define ATOMIC64_OP_RETURN(op, op1, op2) \
379-
static inline long long atomic64_##op##_return(long long a, atomic64_t *v) \
379+
static inline s64 atomic64_##op##_return(s64 a, atomic64_t *v) \
380380
{ \
381-
unsigned long long val; \
381+
s64 val; \
382382
\
383383
smp_mb(); \
384384
\
@@ -399,9 +399,9 @@ static inline long long atomic64_##op##_return(long long a, atomic64_t *v) \
399399
}
400400

401401
#define ATOMIC64_FETCH_OP(op, op1, op2) \
402-
static inline long long atomic64_fetch_##op(long long a, atomic64_t *v) \
402+
static inline s64 atomic64_fetch_##op(s64 a, atomic64_t *v) \
403403
{ \
404-
unsigned long long val, orig; \
404+
s64 val, orig; \
405405
\
406406
smp_mb(); \
407407
\
@@ -441,10 +441,10 @@ ATOMIC64_OPS(xor, xor, xor)
441441
#undef ATOMIC64_OP_RETURN
442442
#undef ATOMIC64_OP
443443

444-
static inline long long
445-
atomic64_cmpxchg(atomic64_t *ptr, long long expected, long long new)
444+
static inline s64
445+
atomic64_cmpxchg(atomic64_t *ptr, s64 expected, s64 new)
446446
{
447-
long long prev;
447+
s64 prev;
448448

449449
smp_mb();
450450

@@ -464,9 +464,9 @@ atomic64_cmpxchg(atomic64_t *ptr, long long expected, long long new)
464464
return prev;
465465
}
466466

467-
static inline long long atomic64_xchg(atomic64_t *ptr, long long new)
467+
static inline s64 atomic64_xchg(atomic64_t *ptr, s64 new)
468468
{
469-
long long prev;
469+
s64 prev;
470470

471471
smp_mb();
472472

@@ -492,9 +492,9 @@ static inline long long atomic64_xchg(atomic64_t *ptr, long long new)
492492
* the atomic variable, v, was not decremented.
493493
*/
494494

495-
static inline long long atomic64_dec_if_positive(atomic64_t *v)
495+
static inline s64 atomic64_dec_if_positive(atomic64_t *v)
496496
{
497-
long long val;
497+
s64 val;
498498

499499
smp_mb();
500500

@@ -525,10 +525,9 @@ static inline long long atomic64_dec_if_positive(atomic64_t *v)
525525
* Atomically adds @a to @v, if it was not @u.
526526
* Returns the old value of @v
527527
*/
528-
static inline long long atomic64_fetch_add_unless(atomic64_t *v, long long a,
529-
long long u)
528+
static inline s64 atomic64_fetch_add_unless(atomic64_t *v, s64 a, s64 u)
530529
{
531-
long long old, temp;
530+
s64 old, temp;
532531

533532
smp_mb();
534533

0 commit comments

Comments
 (0)