Skip to content

Commit 70d9329

Browse files
Peter ZijlstraIngo Molnar
authored andcommitted
notifier: Fix broken error handling pattern
The current notifiers have the following error handling pattern all over the place: int err, nr; err = __foo_notifier_call_chain(&chain, val_up, v, -1, &nr); if (err & NOTIFIER_STOP_MASK) __foo_notifier_call_chain(&chain, val_down, v, nr-1, NULL) And aside from the endless repetition thereof, it is broken. Consider blocking notifiers; both calls take and drop the rwsem, this means that the notifier list can change in between the two calls, making @nr meaningless. Fix this by replacing all the __foo_notifier_call_chain() functions with foo_notifier_call_chain_robust() that embeds the above pattern, but ensures it is inside a single lock region. Note: I switched atomic_notifier_call_chain_robust() to use the spinlock, since RCU cannot provide the guarantee required for the recovery. Note: software_resume() error handling was broken afaict. Signed-off-by: Peter Zijlstra (Intel) <[email protected]> Signed-off-by: Ingo Molnar <[email protected]> Acked-by: Rafael J. Wysocki <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent f75aef3 commit 70d9329

File tree

9 files changed

+147
-140
lines changed

9 files changed

+147
-140
lines changed

include/linux/notifier.h

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -161,20 +161,19 @@ extern int srcu_notifier_chain_unregister(struct srcu_notifier_head *nh,
161161

162162
extern int atomic_notifier_call_chain(struct atomic_notifier_head *nh,
163163
unsigned long val, void *v);
164-
extern int __atomic_notifier_call_chain(struct atomic_notifier_head *nh,
165-
unsigned long val, void *v, int nr_to_call, int *nr_calls);
166164
extern int blocking_notifier_call_chain(struct blocking_notifier_head *nh,
167165
unsigned long val, void *v);
168-
extern int __blocking_notifier_call_chain(struct blocking_notifier_head *nh,
169-
unsigned long val, void *v, int nr_to_call, int *nr_calls);
170166
extern int raw_notifier_call_chain(struct raw_notifier_head *nh,
171167
unsigned long val, void *v);
172-
extern int __raw_notifier_call_chain(struct raw_notifier_head *nh,
173-
unsigned long val, void *v, int nr_to_call, int *nr_calls);
174168
extern int srcu_notifier_call_chain(struct srcu_notifier_head *nh,
175169
unsigned long val, void *v);
176-
extern int __srcu_notifier_call_chain(struct srcu_notifier_head *nh,
177-
unsigned long val, void *v, int nr_to_call, int *nr_calls);
170+
171+
extern int atomic_notifier_call_chain_robust(struct atomic_notifier_head *nh,
172+
unsigned long val_up, unsigned long val_down, void *v);
173+
extern int blocking_notifier_call_chain_robust(struct blocking_notifier_head *nh,
174+
unsigned long val_up, unsigned long val_down, void *v);
175+
extern int raw_notifier_call_chain_robust(struct raw_notifier_head *nh,
176+
unsigned long val_up, unsigned long val_down, void *v);
178177

179178
#define NOTIFY_DONE 0x0000 /* Don't care */
180179
#define NOTIFY_OK 0x0001 /* Suits me */

kernel/cpu_pm.c

Lines changed: 18 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,28 @@
1515

1616
static ATOMIC_NOTIFIER_HEAD(cpu_pm_notifier_chain);
1717

18-
static int cpu_pm_notify(enum cpu_pm_event event, int nr_to_call, int *nr_calls)
18+
static int cpu_pm_notify(enum cpu_pm_event event)
1919
{
2020
int ret;
2121

2222
/*
23-
* __atomic_notifier_call_chain has a RCU read critical section, which
23+
* atomic_notifier_call_chain has a RCU read critical section, which
2424
* could be disfunctional in cpu idle. Copy RCU_NONIDLE code to let
2525
* RCU know this.
2626
*/
2727
rcu_irq_enter_irqson();
28-
ret = __atomic_notifier_call_chain(&cpu_pm_notifier_chain, event, NULL,
29-
nr_to_call, nr_calls);
28+
ret = atomic_notifier_call_chain(&cpu_pm_notifier_chain, event, NULL);
29+
rcu_irq_exit_irqson();
30+
31+
return notifier_to_errno(ret);
32+
}
33+
34+
static int cpu_pm_notify_robust(enum cpu_pm_event event_up, enum cpu_pm_event event_down)
35+
{
36+
int ret;
37+
38+
rcu_irq_enter_irqson();
39+
ret = atomic_notifier_call_chain_robust(&cpu_pm_notifier_chain, event_up, event_down, NULL);
3040
rcu_irq_exit_irqson();
3141

3242
return notifier_to_errno(ret);
@@ -80,18 +90,7 @@ EXPORT_SYMBOL_GPL(cpu_pm_unregister_notifier);
8090
*/
8191
int cpu_pm_enter(void)
8292
{
83-
int nr_calls = 0;
84-
int ret = 0;
85-
86-
ret = cpu_pm_notify(CPU_PM_ENTER, -1, &nr_calls);
87-
if (ret)
88-
/*
89-
* Inform listeners (nr_calls - 1) about failure of CPU PM
90-
* PM entry who are notified earlier to prepare for it.
91-
*/
92-
cpu_pm_notify(CPU_PM_ENTER_FAILED, nr_calls - 1, NULL);
93-
94-
return ret;
93+
return cpu_pm_notify_robust(CPU_PM_ENTER, CPU_PM_ENTER_FAILED);
9594
}
9695
EXPORT_SYMBOL_GPL(cpu_pm_enter);
9796

@@ -109,7 +108,7 @@ EXPORT_SYMBOL_GPL(cpu_pm_enter);
109108
*/
110109
int cpu_pm_exit(void)
111110
{
112-
return cpu_pm_notify(CPU_PM_EXIT, -1, NULL);
111+
return cpu_pm_notify(CPU_PM_EXIT);
113112
}
114113
EXPORT_SYMBOL_GPL(cpu_pm_exit);
115114

@@ -131,18 +130,7 @@ EXPORT_SYMBOL_GPL(cpu_pm_exit);
131130
*/
132131
int cpu_cluster_pm_enter(void)
133132
{
134-
int nr_calls = 0;
135-
int ret = 0;
136-
137-
ret = cpu_pm_notify(CPU_CLUSTER_PM_ENTER, -1, &nr_calls);
138-
if (ret)
139-
/*
140-
* Inform listeners (nr_calls - 1) about failure of CPU cluster
141-
* PM entry who are notified earlier to prepare for it.
142-
*/
143-
cpu_pm_notify(CPU_CLUSTER_PM_ENTER_FAILED, nr_calls - 1, NULL);
144-
145-
return ret;
133+
return cpu_pm_notify_robust(CPU_CLUSTER_PM_ENTER, CPU_CLUSTER_PM_ENTER_FAILED);
146134
}
147135
EXPORT_SYMBOL_GPL(cpu_cluster_pm_enter);
148136

@@ -163,7 +151,7 @@ EXPORT_SYMBOL_GPL(cpu_cluster_pm_enter);
163151
*/
164152
int cpu_cluster_pm_exit(void)
165153
{
166-
return cpu_pm_notify(CPU_CLUSTER_PM_EXIT, -1, NULL);
154+
return cpu_pm_notify(CPU_CLUSTER_PM_EXIT);
167155
}
168156
EXPORT_SYMBOL_GPL(cpu_cluster_pm_exit);
169157

kernel/notifier.c

Lines changed: 88 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,34 @@ static int notifier_call_chain(struct notifier_block **nl,
9494
}
9595
NOKPROBE_SYMBOL(notifier_call_chain);
9696

97+
/**
98+
* notifier_call_chain_robust - Inform the registered notifiers about an event
99+
* and rollback on error.
100+
* @nl: Pointer to head of the blocking notifier chain
101+
* @val_up: Value passed unmodified to the notifier function
102+
* @val_down: Value passed unmodified to the notifier function when recovering
103+
* from an error on @val_up
104+
* @v Pointer passed unmodified to the notifier function
105+
*
106+
* NOTE: It is important the @nl chain doesn't change between the two
107+
* invocations of notifier_call_chain() such that we visit the
108+
* exact same notifier callbacks; this rules out any RCU usage.
109+
*
110+
* Returns: the return value of the @val_up call.
111+
*/
112+
static int notifier_call_chain_robust(struct notifier_block **nl,
113+
unsigned long val_up, unsigned long val_down,
114+
void *v)
115+
{
116+
int ret, nr = 0;
117+
118+
ret = notifier_call_chain(nl, val_up, v, -1, &nr);
119+
if (ret & NOTIFY_STOP_MASK)
120+
notifier_call_chain(nl, val_down, v, nr-1, NULL);
121+
122+
return ret;
123+
}
124+
97125
/*
98126
* Atomic notifier chain routines. Registration and unregistration
99127
* use a spinlock, and call_chain is synchronized by RCU (no locks).
@@ -144,13 +172,30 @@ int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh,
144172
}
145173
EXPORT_SYMBOL_GPL(atomic_notifier_chain_unregister);
146174

175+
int atomic_notifier_call_chain_robust(struct atomic_notifier_head *nh,
176+
unsigned long val_up, unsigned long val_down, void *v)
177+
{
178+
unsigned long flags;
179+
int ret;
180+
181+
/*
182+
* Musn't use RCU; because then the notifier list can
183+
* change between the up and down traversal.
184+
*/
185+
spin_lock_irqsave(&nh->lock, flags);
186+
ret = notifier_call_chain_robust(&nh->head, val_up, val_down, v);
187+
spin_unlock_irqrestore(&nh->lock, flags);
188+
189+
return ret;
190+
}
191+
EXPORT_SYMBOL_GPL(atomic_notifier_call_chain_robust);
192+
NOKPROBE_SYMBOL(atomic_notifier_call_chain_robust);
193+
147194
/**
148-
* __atomic_notifier_call_chain - Call functions in an atomic notifier chain
195+
* atomic_notifier_call_chain - Call functions in an atomic notifier chain
149196
* @nh: Pointer to head of the atomic notifier chain
150197
* @val: Value passed unmodified to notifier function
151198
* @v: Pointer passed unmodified to notifier function
152-
* @nr_to_call: See the comment for notifier_call_chain.
153-
* @nr_calls: See the comment for notifier_call_chain.
154199
*
155200
* Calls each function in a notifier chain in turn. The functions
156201
* run in an atomic context, so they must not block.
@@ -163,24 +208,16 @@ EXPORT_SYMBOL_GPL(atomic_notifier_chain_unregister);
163208
* Otherwise the return value is the return value
164209
* of the last notifier function called.
165210
*/
166-
int __atomic_notifier_call_chain(struct atomic_notifier_head *nh,
167-
unsigned long val, void *v,
168-
int nr_to_call, int *nr_calls)
211+
int atomic_notifier_call_chain(struct atomic_notifier_head *nh,
212+
unsigned long val, void *v)
169213
{
170214
int ret;
171215

172216
rcu_read_lock();
173-
ret = notifier_call_chain(&nh->head, val, v, nr_to_call, nr_calls);
217+
ret = notifier_call_chain(&nh->head, val, v, -1, NULL);
174218
rcu_read_unlock();
175-
return ret;
176-
}
177-
EXPORT_SYMBOL_GPL(__atomic_notifier_call_chain);
178-
NOKPROBE_SYMBOL(__atomic_notifier_call_chain);
179219

180-
int atomic_notifier_call_chain(struct atomic_notifier_head *nh,
181-
unsigned long val, void *v)
182-
{
183-
return __atomic_notifier_call_chain(nh, val, v, -1, NULL);
220+
return ret;
184221
}
185222
EXPORT_SYMBOL_GPL(atomic_notifier_call_chain);
186223
NOKPROBE_SYMBOL(atomic_notifier_call_chain);
@@ -250,13 +287,30 @@ int blocking_notifier_chain_unregister(struct blocking_notifier_head *nh,
250287
}
251288
EXPORT_SYMBOL_GPL(blocking_notifier_chain_unregister);
252289

290+
int blocking_notifier_call_chain_robust(struct blocking_notifier_head *nh,
291+
unsigned long val_up, unsigned long val_down, void *v)
292+
{
293+
int ret = NOTIFY_DONE;
294+
295+
/*
296+
* We check the head outside the lock, but if this access is
297+
* racy then it does not matter what the result of the test
298+
* is, we re-check the list after having taken the lock anyway:
299+
*/
300+
if (rcu_access_pointer(nh->head)) {
301+
down_read(&nh->rwsem);
302+
ret = notifier_call_chain_robust(&nh->head, val_up, val_down, v);
303+
up_read(&nh->rwsem);
304+
}
305+
return ret;
306+
}
307+
EXPORT_SYMBOL_GPL(blocking_notifier_call_chain_robust);
308+
253309
/**
254-
* __blocking_notifier_call_chain - Call functions in a blocking notifier chain
310+
* blocking_notifier_call_chain - Call functions in a blocking notifier chain
255311
* @nh: Pointer to head of the blocking notifier chain
256312
* @val: Value passed unmodified to notifier function
257313
* @v: Pointer passed unmodified to notifier function
258-
* @nr_to_call: See comment for notifier_call_chain.
259-
* @nr_calls: See comment for notifier_call_chain.
260314
*
261315
* Calls each function in a notifier chain in turn. The functions
262316
* run in a process context, so they are allowed to block.
@@ -268,9 +322,8 @@ EXPORT_SYMBOL_GPL(blocking_notifier_chain_unregister);
268322
* Otherwise the return value is the return value
269323
* of the last notifier function called.
270324
*/
271-
int __blocking_notifier_call_chain(struct blocking_notifier_head *nh,
272-
unsigned long val, void *v,
273-
int nr_to_call, int *nr_calls)
325+
int blocking_notifier_call_chain(struct blocking_notifier_head *nh,
326+
unsigned long val, void *v)
274327
{
275328
int ret = NOTIFY_DONE;
276329

@@ -281,19 +334,11 @@ int __blocking_notifier_call_chain(struct blocking_notifier_head *nh,
281334
*/
282335
if (rcu_access_pointer(nh->head)) {
283336
down_read(&nh->rwsem);
284-
ret = notifier_call_chain(&nh->head, val, v, nr_to_call,
285-
nr_calls);
337+
ret = notifier_call_chain(&nh->head, val, v, -1, NULL);
286338
up_read(&nh->rwsem);
287339
}
288340
return ret;
289341
}
290-
EXPORT_SYMBOL_GPL(__blocking_notifier_call_chain);
291-
292-
int blocking_notifier_call_chain(struct blocking_notifier_head *nh,
293-
unsigned long val, void *v)
294-
{
295-
return __blocking_notifier_call_chain(nh, val, v, -1, NULL);
296-
}
297342
EXPORT_SYMBOL_GPL(blocking_notifier_call_chain);
298343

299344
/*
@@ -335,13 +380,18 @@ int raw_notifier_chain_unregister(struct raw_notifier_head *nh,
335380
}
336381
EXPORT_SYMBOL_GPL(raw_notifier_chain_unregister);
337382

383+
int raw_notifier_call_chain_robust(struct raw_notifier_head *nh,
384+
unsigned long val_up, unsigned long val_down, void *v)
385+
{
386+
return notifier_call_chain_robust(&nh->head, val_up, val_down, v);
387+
}
388+
EXPORT_SYMBOL_GPL(raw_notifier_call_chain_robust);
389+
338390
/**
339-
* __raw_notifier_call_chain - Call functions in a raw notifier chain
391+
* raw_notifier_call_chain - Call functions in a raw notifier chain
340392
* @nh: Pointer to head of the raw notifier chain
341393
* @val: Value passed unmodified to notifier function
342394
* @v: Pointer passed unmodified to notifier function
343-
* @nr_to_call: See comment for notifier_call_chain.
344-
* @nr_calls: See comment for notifier_call_chain
345395
*
346396
* Calls each function in a notifier chain in turn. The functions
347397
* run in an undefined context.
@@ -354,18 +404,10 @@ EXPORT_SYMBOL_GPL(raw_notifier_chain_unregister);
354404
* Otherwise the return value is the return value
355405
* of the last notifier function called.
356406
*/
357-
int __raw_notifier_call_chain(struct raw_notifier_head *nh,
358-
unsigned long val, void *v,
359-
int nr_to_call, int *nr_calls)
360-
{
361-
return notifier_call_chain(&nh->head, val, v, nr_to_call, nr_calls);
362-
}
363-
EXPORT_SYMBOL_GPL(__raw_notifier_call_chain);
364-
365407
int raw_notifier_call_chain(struct raw_notifier_head *nh,
366408
unsigned long val, void *v)
367409
{
368-
return __raw_notifier_call_chain(nh, val, v, -1, NULL);
410+
return notifier_call_chain(&nh->head, val, v, -1, NULL);
369411
}
370412
EXPORT_SYMBOL_GPL(raw_notifier_call_chain);
371413

@@ -437,12 +479,10 @@ int srcu_notifier_chain_unregister(struct srcu_notifier_head *nh,
437479
EXPORT_SYMBOL_GPL(srcu_notifier_chain_unregister);
438480

439481
/**
440-
* __srcu_notifier_call_chain - Call functions in an SRCU notifier chain
482+
* srcu_notifier_call_chain - Call functions in an SRCU notifier chain
441483
* @nh: Pointer to head of the SRCU notifier chain
442484
* @val: Value passed unmodified to notifier function
443485
* @v: Pointer passed unmodified to notifier function
444-
* @nr_to_call: See comment for notifier_call_chain.
445-
* @nr_calls: See comment for notifier_call_chain
446486
*
447487
* Calls each function in a notifier chain in turn. The functions
448488
* run in a process context, so they are allowed to block.
@@ -454,25 +494,17 @@ EXPORT_SYMBOL_GPL(srcu_notifier_chain_unregister);
454494
* Otherwise the return value is the return value
455495
* of the last notifier function called.
456496
*/
457-
int __srcu_notifier_call_chain(struct srcu_notifier_head *nh,
458-
unsigned long val, void *v,
459-
int nr_to_call, int *nr_calls)
497+
int srcu_notifier_call_chain(struct srcu_notifier_head *nh,
498+
unsigned long val, void *v)
460499
{
461500
int ret;
462501
int idx;
463502

464503
idx = srcu_read_lock(&nh->srcu);
465-
ret = notifier_call_chain(&nh->head, val, v, nr_to_call, nr_calls);
504+
ret = notifier_call_chain(&nh->head, val, v, -1, NULL);
466505
srcu_read_unlock(&nh->srcu, idx);
467506
return ret;
468507
}
469-
EXPORT_SYMBOL_GPL(__srcu_notifier_call_chain);
470-
471-
int srcu_notifier_call_chain(struct srcu_notifier_head *nh,
472-
unsigned long val, void *v)
473-
{
474-
return __srcu_notifier_call_chain(nh, val, v, -1, NULL);
475-
}
476508
EXPORT_SYMBOL_GPL(srcu_notifier_call_chain);
477509

478510
/**

0 commit comments

Comments
 (0)