xref: /linux-6.15/kernel/notifier.c (revision f2fa0fd4)
1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2fe9d4f57SAlexey Dobriyan #include <linux/kdebug.h>
3fe9d4f57SAlexey Dobriyan #include <linux/kprobes.h>
49984de1aSPaul Gortmaker #include <linux/export.h>
5fe9d4f57SAlexey Dobriyan #include <linux/notifier.h>
6fe9d4f57SAlexey Dobriyan #include <linux/rcupdate.h>
7fe9d4f57SAlexey Dobriyan #include <linux/vmalloc.h>
8fe9d4f57SAlexey Dobriyan 
9*f4708a82SGuilherme G. Piccoli #define CREATE_TRACE_POINTS
10*f4708a82SGuilherme G. Piccoli #include <trace/events/notifier.h>
11*f4708a82SGuilherme G. Piccoli 
12fe9d4f57SAlexey Dobriyan /*
13fe9d4f57SAlexey Dobriyan  *	Notifier chain core routines.  The exported routines below
14fe9d4f57SAlexey Dobriyan  *	are layered on top of these, with appropriate locking added.
15fe9d4f57SAlexey Dobriyan  */
16fe9d4f57SAlexey Dobriyan 
notifier_chain_register(struct notifier_block ** nl,struct notifier_block * n,bool unique_priority)17fe9d4f57SAlexey Dobriyan static int notifier_chain_register(struct notifier_block **nl,
18c82f898dSDmitry Osipenko 				   struct notifier_block *n,
19c82f898dSDmitry Osipenko 				   bool unique_priority)
20fe9d4f57SAlexey Dobriyan {
21fe9d4f57SAlexey Dobriyan 	while ((*nl) != NULL) {
221a50cb80SXiaoming Ni 		if (unlikely((*nl) == n)) {
235abb065dSBorislav Petkov 			WARN(1, "notifier callback %ps already registered",
245abb065dSBorislav Petkov 			     n->notifier_call);
255abb065dSBorislav Petkov 			return -EEXIST;
261a50cb80SXiaoming Ni 		}
27fe9d4f57SAlexey Dobriyan 		if (n->priority > (*nl)->priority)
28fe9d4f57SAlexey Dobriyan 			break;
29c82f898dSDmitry Osipenko 		if (n->priority == (*nl)->priority && unique_priority)
30c82f898dSDmitry Osipenko 			return -EBUSY;
31fe9d4f57SAlexey Dobriyan 		nl = &((*nl)->next);
32fe9d4f57SAlexey Dobriyan 	}
33fe9d4f57SAlexey Dobriyan 	n->next = *nl;
34fe9d4f57SAlexey Dobriyan 	rcu_assign_pointer(*nl, n);
35*f4708a82SGuilherme G. Piccoli 	trace_notifier_register((void *)n->notifier_call);
36fe9d4f57SAlexey Dobriyan 	return 0;
37fe9d4f57SAlexey Dobriyan }
38fe9d4f57SAlexey Dobriyan 
notifier_chain_unregister(struct notifier_block ** nl,struct notifier_block * n)39fe9d4f57SAlexey Dobriyan static int notifier_chain_unregister(struct notifier_block **nl,
40fe9d4f57SAlexey Dobriyan 		struct notifier_block *n)
41fe9d4f57SAlexey Dobriyan {
42fe9d4f57SAlexey Dobriyan 	while ((*nl) != NULL) {
43fe9d4f57SAlexey Dobriyan 		if ((*nl) == n) {
44fe9d4f57SAlexey Dobriyan 			rcu_assign_pointer(*nl, n->next);
45*f4708a82SGuilherme G. Piccoli 			trace_notifier_unregister((void *)n->notifier_call);
46fe9d4f57SAlexey Dobriyan 			return 0;
47fe9d4f57SAlexey Dobriyan 		}
48fe9d4f57SAlexey Dobriyan 		nl = &((*nl)->next);
49fe9d4f57SAlexey Dobriyan 	}
50fe9d4f57SAlexey Dobriyan 	return -ENOENT;
51fe9d4f57SAlexey Dobriyan }
52fe9d4f57SAlexey Dobriyan 
53fe9d4f57SAlexey Dobriyan /**
54fe9d4f57SAlexey Dobriyan  * notifier_call_chain - Informs the registered notifiers about an event.
55fe9d4f57SAlexey Dobriyan  *	@nl:		Pointer to head of the blocking notifier chain
56fe9d4f57SAlexey Dobriyan  *	@val:		Value passed unmodified to notifier function
57fe9d4f57SAlexey Dobriyan  *	@v:		Pointer passed unmodified to notifier function
58fe9d4f57SAlexey Dobriyan  *	@nr_to_call:	Number of notifier functions to be called. Don't care
59fe9d4f57SAlexey Dobriyan  *			value of this parameter is -1.
60fe9d4f57SAlexey Dobriyan  *	@nr_calls:	Records the number of notifications sent. Don't care
61fe9d4f57SAlexey Dobriyan  *			value of this field is NULL.
62f9b4dc92SLukas Bulwahn  *	Return:		notifier_call_chain returns the value returned by the
63fe9d4f57SAlexey Dobriyan  *			last notifier function called.
64fe9d4f57SAlexey Dobriyan  */
notifier_call_chain(struct notifier_block ** nl,unsigned long val,void * v,int nr_to_call,int * nr_calls)65b40a2cb6SMasami Hiramatsu static int notifier_call_chain(struct notifier_block **nl,
66fe9d4f57SAlexey Dobriyan 			       unsigned long val, void *v,
67fe9d4f57SAlexey Dobriyan 			       int nr_to_call, int *nr_calls)
68fe9d4f57SAlexey Dobriyan {
69fe9d4f57SAlexey Dobriyan 	int ret = NOTIFY_DONE;
70fe9d4f57SAlexey Dobriyan 	struct notifier_block *nb, *next_nb;
71fe9d4f57SAlexey Dobriyan 
72d11c563dSPaul E. McKenney 	nb = rcu_dereference_raw(*nl);
73fe9d4f57SAlexey Dobriyan 
74fe9d4f57SAlexey Dobriyan 	while (nb && nr_to_call) {
75d11c563dSPaul E. McKenney 		next_nb = rcu_dereference_raw(nb->next);
761b2439dbSArjan van de Ven 
771b2439dbSArjan van de Ven #ifdef CONFIG_DEBUG_NOTIFIERS
78ab7476cfSArjan van de Ven 		if (unlikely(!func_ptr_is_kernel_text(nb->notifier_call))) {
791b2439dbSArjan van de Ven 			WARN(1, "Invalid notifier called!");
801b2439dbSArjan van de Ven 			nb = next_nb;
811b2439dbSArjan van de Ven 			continue;
821b2439dbSArjan van de Ven 		}
831b2439dbSArjan van de Ven #endif
84*f4708a82SGuilherme G. Piccoli 		trace_notifier_run((void *)nb->notifier_call);
85fe9d4f57SAlexey Dobriyan 		ret = nb->notifier_call(nb, val, v);
86fe9d4f57SAlexey Dobriyan 
87fe9d4f57SAlexey Dobriyan 		if (nr_calls)
88fe9d4f57SAlexey Dobriyan 			(*nr_calls)++;
89fe9d4f57SAlexey Dobriyan 
903e6dadedSViresh Kumar 		if (ret & NOTIFY_STOP_MASK)
91fe9d4f57SAlexey Dobriyan 			break;
92fe9d4f57SAlexey Dobriyan 		nb = next_nb;
93fe9d4f57SAlexey Dobriyan 		nr_to_call--;
94fe9d4f57SAlexey Dobriyan 	}
95fe9d4f57SAlexey Dobriyan 	return ret;
96fe9d4f57SAlexey Dobriyan }
97b40a2cb6SMasami Hiramatsu NOKPROBE_SYMBOL(notifier_call_chain);
98fe9d4f57SAlexey Dobriyan 
9970d93298SPeter Zijlstra /**
10070d93298SPeter Zijlstra  * notifier_call_chain_robust - Inform the registered notifiers about an event
10170d93298SPeter Zijlstra  *                              and rollback on error.
10270d93298SPeter Zijlstra  * @nl:		Pointer to head of the blocking notifier chain
10370d93298SPeter Zijlstra  * @val_up:	Value passed unmodified to the notifier function
10470d93298SPeter Zijlstra  * @val_down:	Value passed unmodified to the notifier function when recovering
10570d93298SPeter Zijlstra  *              from an error on @val_up
106f9b4dc92SLukas Bulwahn  * @v:		Pointer passed unmodified to the notifier function
10770d93298SPeter Zijlstra  *
10870d93298SPeter Zijlstra  * NOTE:	It is important the @nl chain doesn't change between the two
10970d93298SPeter Zijlstra  *		invocations of notifier_call_chain() such that we visit the
11070d93298SPeter Zijlstra  *		exact same notifier callbacks; this rules out any RCU usage.
11170d93298SPeter Zijlstra  *
112f9b4dc92SLukas Bulwahn  * Return:	the return value of the @val_up call.
11370d93298SPeter Zijlstra  */
notifier_call_chain_robust(struct notifier_block ** nl,unsigned long val_up,unsigned long val_down,void * v)11470d93298SPeter Zijlstra static int notifier_call_chain_robust(struct notifier_block **nl,
11570d93298SPeter Zijlstra 				     unsigned long val_up, unsigned long val_down,
11670d93298SPeter Zijlstra 				     void *v)
11770d93298SPeter Zijlstra {
11870d93298SPeter Zijlstra 	int ret, nr = 0;
11970d93298SPeter Zijlstra 
12070d93298SPeter Zijlstra 	ret = notifier_call_chain(nl, val_up, v, -1, &nr);
12170d93298SPeter Zijlstra 	if (ret & NOTIFY_STOP_MASK)
12270d93298SPeter Zijlstra 		notifier_call_chain(nl, val_down, v, nr-1, NULL);
12370d93298SPeter Zijlstra 
12470d93298SPeter Zijlstra 	return ret;
12570d93298SPeter Zijlstra }
12670d93298SPeter Zijlstra 
127fe9d4f57SAlexey Dobriyan /*
128fe9d4f57SAlexey Dobriyan  *	Atomic notifier chain routines.  Registration and unregistration
129fe9d4f57SAlexey Dobriyan  *	use a spinlock, and call_chain is synchronized by RCU (no locks).
130fe9d4f57SAlexey Dobriyan  */
131fe9d4f57SAlexey Dobriyan 
132fe9d4f57SAlexey Dobriyan /**
133fe9d4f57SAlexey Dobriyan  *	atomic_notifier_chain_register - Add notifier to an atomic notifier chain
134fe9d4f57SAlexey Dobriyan  *	@nh: Pointer to head of the atomic notifier chain
135fe9d4f57SAlexey Dobriyan  *	@n: New entry in notifier chain
136fe9d4f57SAlexey Dobriyan  *
137fe9d4f57SAlexey Dobriyan  *	Adds a notifier to an atomic notifier chain.
138fe9d4f57SAlexey Dobriyan  *
1395abb065dSBorislav Petkov  *	Returns 0 on success, %-EEXIST on error.
140fe9d4f57SAlexey Dobriyan  */
atomic_notifier_chain_register(struct atomic_notifier_head * nh,struct notifier_block * n)141fe9d4f57SAlexey Dobriyan int atomic_notifier_chain_register(struct atomic_notifier_head *nh,
142fe9d4f57SAlexey Dobriyan 		struct notifier_block *n)
143fe9d4f57SAlexey Dobriyan {
144fe9d4f57SAlexey Dobriyan 	unsigned long flags;
145fe9d4f57SAlexey Dobriyan 	int ret;
146fe9d4f57SAlexey Dobriyan 
147fe9d4f57SAlexey Dobriyan 	spin_lock_irqsave(&nh->lock, flags);
148c82f898dSDmitry Osipenko 	ret = notifier_chain_register(&nh->head, n, false);
149fe9d4f57SAlexey Dobriyan 	spin_unlock_irqrestore(&nh->lock, flags);
150fe9d4f57SAlexey Dobriyan 	return ret;
151fe9d4f57SAlexey Dobriyan }
152fe9d4f57SAlexey Dobriyan EXPORT_SYMBOL_GPL(atomic_notifier_chain_register);
153fe9d4f57SAlexey Dobriyan 
154fe9d4f57SAlexey Dobriyan /**
155c82f898dSDmitry Osipenko  *	atomic_notifier_chain_register_unique_prio - Add notifier to an atomic notifier chain
156c82f898dSDmitry Osipenko  *	@nh: Pointer to head of the atomic notifier chain
157c82f898dSDmitry Osipenko  *	@n: New entry in notifier chain
158c82f898dSDmitry Osipenko  *
159c82f898dSDmitry Osipenko  *	Adds a notifier to an atomic notifier chain if there is no other
160c82f898dSDmitry Osipenko  *	notifier registered using the same priority.
161c82f898dSDmitry Osipenko  *
162c82f898dSDmitry Osipenko  *	Returns 0 on success, %-EEXIST or %-EBUSY on error.
163c82f898dSDmitry Osipenko  */
atomic_notifier_chain_register_unique_prio(struct atomic_notifier_head * nh,struct notifier_block * n)164c82f898dSDmitry Osipenko int atomic_notifier_chain_register_unique_prio(struct atomic_notifier_head *nh,
165c82f898dSDmitry Osipenko 					       struct notifier_block *n)
166c82f898dSDmitry Osipenko {
167c82f898dSDmitry Osipenko 	unsigned long flags;
168c82f898dSDmitry Osipenko 	int ret;
169c82f898dSDmitry Osipenko 
170c82f898dSDmitry Osipenko 	spin_lock_irqsave(&nh->lock, flags);
171c82f898dSDmitry Osipenko 	ret = notifier_chain_register(&nh->head, n, true);
172c82f898dSDmitry Osipenko 	spin_unlock_irqrestore(&nh->lock, flags);
173c82f898dSDmitry Osipenko 	return ret;
174c82f898dSDmitry Osipenko }
175c82f898dSDmitry Osipenko EXPORT_SYMBOL_GPL(atomic_notifier_chain_register_unique_prio);
176c82f898dSDmitry Osipenko 
177c82f898dSDmitry Osipenko /**
178fe9d4f57SAlexey Dobriyan  *	atomic_notifier_chain_unregister - Remove notifier from an atomic notifier chain
179fe9d4f57SAlexey Dobriyan  *	@nh: Pointer to head of the atomic notifier chain
180fe9d4f57SAlexey Dobriyan  *	@n: Entry to remove from notifier chain
181fe9d4f57SAlexey Dobriyan  *
182fe9d4f57SAlexey Dobriyan  *	Removes a notifier from an atomic notifier chain.
183fe9d4f57SAlexey Dobriyan  *
184fe9d4f57SAlexey Dobriyan  *	Returns zero on success or %-ENOENT on failure.
185fe9d4f57SAlexey Dobriyan  */
atomic_notifier_chain_unregister(struct atomic_notifier_head * nh,struct notifier_block * n)186fe9d4f57SAlexey Dobriyan int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh,
187fe9d4f57SAlexey Dobriyan 		struct notifier_block *n)
188fe9d4f57SAlexey Dobriyan {
189fe9d4f57SAlexey Dobriyan 	unsigned long flags;
190fe9d4f57SAlexey Dobriyan 	int ret;
191fe9d4f57SAlexey Dobriyan 
192fe9d4f57SAlexey Dobriyan 	spin_lock_irqsave(&nh->lock, flags);
193fe9d4f57SAlexey Dobriyan 	ret = notifier_chain_unregister(&nh->head, n);
194fe9d4f57SAlexey Dobriyan 	spin_unlock_irqrestore(&nh->lock, flags);
195fe9d4f57SAlexey Dobriyan 	synchronize_rcu();
196fe9d4f57SAlexey Dobriyan 	return ret;
197fe9d4f57SAlexey Dobriyan }
198fe9d4f57SAlexey Dobriyan EXPORT_SYMBOL_GPL(atomic_notifier_chain_unregister);
199fe9d4f57SAlexey Dobriyan 
200fe9d4f57SAlexey Dobriyan /**
20170d93298SPeter Zijlstra  *	atomic_notifier_call_chain - Call functions in an atomic notifier chain
202fe9d4f57SAlexey Dobriyan  *	@nh: Pointer to head of the atomic notifier chain
203fe9d4f57SAlexey Dobriyan  *	@val: Value passed unmodified to notifier function
204fe9d4f57SAlexey Dobriyan  *	@v: Pointer passed unmodified to notifier function
205fe9d4f57SAlexey Dobriyan  *
206fe9d4f57SAlexey Dobriyan  *	Calls each function in a notifier chain in turn.  The functions
207fe9d4f57SAlexey Dobriyan  *	run in an atomic context, so they must not block.
208fe9d4f57SAlexey Dobriyan  *	This routine uses RCU to synchronize with changes to the chain.
209fe9d4f57SAlexey Dobriyan  *
210fe9d4f57SAlexey Dobriyan  *	If the return value of the notifier can be and'ed
211fe9d4f57SAlexey Dobriyan  *	with %NOTIFY_STOP_MASK then atomic_notifier_call_chain()
212fe9d4f57SAlexey Dobriyan  *	will return immediately, with the return value of
213fe9d4f57SAlexey Dobriyan  *	the notifier function which halted execution.
214fe9d4f57SAlexey Dobriyan  *	Otherwise the return value is the return value
215fe9d4f57SAlexey Dobriyan  *	of the last notifier function called.
216fe9d4f57SAlexey Dobriyan  */
atomic_notifier_call_chain(struct atomic_notifier_head * nh,unsigned long val,void * v)21770d93298SPeter Zijlstra int atomic_notifier_call_chain(struct atomic_notifier_head *nh,
21870d93298SPeter Zijlstra 			       unsigned long val, void *v)
219fe9d4f57SAlexey Dobriyan {
220fe9d4f57SAlexey Dobriyan 	int ret;
221fe9d4f57SAlexey Dobriyan 
222fe9d4f57SAlexey Dobriyan 	rcu_read_lock();
22370d93298SPeter Zijlstra 	ret = notifier_call_chain(&nh->head, val, v, -1, NULL);
224fe9d4f57SAlexey Dobriyan 	rcu_read_unlock();
225fe9d4f57SAlexey Dobriyan 
22670d93298SPeter Zijlstra 	return ret;
227fe9d4f57SAlexey Dobriyan }
228fe9d4f57SAlexey Dobriyan EXPORT_SYMBOL_GPL(atomic_notifier_call_chain);
229b40a2cb6SMasami Hiramatsu NOKPROBE_SYMBOL(atomic_notifier_call_chain);
230fe9d4f57SAlexey Dobriyan 
23113dfd97aSDmitry Osipenko /**
23213dfd97aSDmitry Osipenko  *	atomic_notifier_call_chain_is_empty - Check whether notifier chain is empty
23313dfd97aSDmitry Osipenko  *	@nh: Pointer to head of the atomic notifier chain
23413dfd97aSDmitry Osipenko  *
23513dfd97aSDmitry Osipenko  *	Checks whether notifier chain is empty.
23613dfd97aSDmitry Osipenko  *
23713dfd97aSDmitry Osipenko  *	Returns true is notifier chain is empty, false otherwise.
23813dfd97aSDmitry Osipenko  */
atomic_notifier_call_chain_is_empty(struct atomic_notifier_head * nh)23913dfd97aSDmitry Osipenko bool atomic_notifier_call_chain_is_empty(struct atomic_notifier_head *nh)
24013dfd97aSDmitry Osipenko {
24113dfd97aSDmitry Osipenko 	return !rcu_access_pointer(nh->head);
24213dfd97aSDmitry Osipenko }
24313dfd97aSDmitry Osipenko 
244fe9d4f57SAlexey Dobriyan /*
245fe9d4f57SAlexey Dobriyan  *	Blocking notifier chain routines.  All access to the chain is
246fe9d4f57SAlexey Dobriyan  *	synchronized by an rwsem.
247fe9d4f57SAlexey Dobriyan  */
248fe9d4f57SAlexey Dobriyan 
__blocking_notifier_chain_register(struct blocking_notifier_head * nh,struct notifier_block * n,bool unique_priority)249c82f898dSDmitry Osipenko static int __blocking_notifier_chain_register(struct blocking_notifier_head *nh,
250c82f898dSDmitry Osipenko 					      struct notifier_block *n,
251c82f898dSDmitry Osipenko 					      bool unique_priority)
252c82f898dSDmitry Osipenko {
253c82f898dSDmitry Osipenko 	int ret;
254c82f898dSDmitry Osipenko 
255c82f898dSDmitry Osipenko 	/*
256c82f898dSDmitry Osipenko 	 * This code gets used during boot-up, when task switching is
257c82f898dSDmitry Osipenko 	 * not yet working and interrupts must remain disabled.  At
258c82f898dSDmitry Osipenko 	 * such times we must not call down_write().
259c82f898dSDmitry Osipenko 	 */
260c82f898dSDmitry Osipenko 	if (unlikely(system_state == SYSTEM_BOOTING))
261c82f898dSDmitry Osipenko 		return notifier_chain_register(&nh->head, n, unique_priority);
262c82f898dSDmitry Osipenko 
263c82f898dSDmitry Osipenko 	down_write(&nh->rwsem);
264c82f898dSDmitry Osipenko 	ret = notifier_chain_register(&nh->head, n, unique_priority);
265c82f898dSDmitry Osipenko 	up_write(&nh->rwsem);
266c82f898dSDmitry Osipenko 	return ret;
267c82f898dSDmitry Osipenko }
268c82f898dSDmitry Osipenko 
269fe9d4f57SAlexey Dobriyan /**
270fe9d4f57SAlexey Dobriyan  *	blocking_notifier_chain_register - Add notifier to a blocking notifier chain
271fe9d4f57SAlexey Dobriyan  *	@nh: Pointer to head of the blocking notifier chain
272fe9d4f57SAlexey Dobriyan  *	@n: New entry in notifier chain
273fe9d4f57SAlexey Dobriyan  *
274fe9d4f57SAlexey Dobriyan  *	Adds a notifier to a blocking notifier chain.
275fe9d4f57SAlexey Dobriyan  *	Must be called in process context.
276fe9d4f57SAlexey Dobriyan  *
2775abb065dSBorislav Petkov  *	Returns 0 on success, %-EEXIST on error.
278fe9d4f57SAlexey Dobriyan  */
blocking_notifier_chain_register(struct blocking_notifier_head * nh,struct notifier_block * n)279fe9d4f57SAlexey Dobriyan int blocking_notifier_chain_register(struct blocking_notifier_head *nh,
280fe9d4f57SAlexey Dobriyan 		struct notifier_block *n)
281fe9d4f57SAlexey Dobriyan {
282c82f898dSDmitry Osipenko 	return __blocking_notifier_chain_register(nh, n, false);
283fe9d4f57SAlexey Dobriyan }
284fe9d4f57SAlexey Dobriyan EXPORT_SYMBOL_GPL(blocking_notifier_chain_register);
285fe9d4f57SAlexey Dobriyan 
286fe9d4f57SAlexey Dobriyan /**
287c82f898dSDmitry Osipenko  *	blocking_notifier_chain_register_unique_prio - Add notifier to a blocking notifier chain
288c82f898dSDmitry Osipenko  *	@nh: Pointer to head of the blocking notifier chain
289c82f898dSDmitry Osipenko  *	@n: New entry in notifier chain
290c82f898dSDmitry Osipenko  *
291c82f898dSDmitry Osipenko  *	Adds a notifier to an blocking notifier chain if there is no other
292c82f898dSDmitry Osipenko  *	notifier registered using the same priority.
293c82f898dSDmitry Osipenko  *
294c82f898dSDmitry Osipenko  *	Returns 0 on success, %-EEXIST or %-EBUSY on error.
295c82f898dSDmitry Osipenko  */
blocking_notifier_chain_register_unique_prio(struct blocking_notifier_head * nh,struct notifier_block * n)296c82f898dSDmitry Osipenko int blocking_notifier_chain_register_unique_prio(struct blocking_notifier_head *nh,
297c82f898dSDmitry Osipenko 						 struct notifier_block *n)
298c82f898dSDmitry Osipenko {
299c82f898dSDmitry Osipenko 	return __blocking_notifier_chain_register(nh, n, true);
300c82f898dSDmitry Osipenko }
301c82f898dSDmitry Osipenko EXPORT_SYMBOL_GPL(blocking_notifier_chain_register_unique_prio);
302c82f898dSDmitry Osipenko 
303c82f898dSDmitry Osipenko /**
304fe9d4f57SAlexey Dobriyan  *	blocking_notifier_chain_unregister - Remove notifier from a blocking notifier chain
305fe9d4f57SAlexey Dobriyan  *	@nh: Pointer to head of the blocking notifier chain
306fe9d4f57SAlexey Dobriyan  *	@n: Entry to remove from notifier chain
307fe9d4f57SAlexey Dobriyan  *
308fe9d4f57SAlexey Dobriyan  *	Removes a notifier from a blocking notifier chain.
309fe9d4f57SAlexey Dobriyan  *	Must be called from process context.
310fe9d4f57SAlexey Dobriyan  *
311fe9d4f57SAlexey Dobriyan  *	Returns zero on success or %-ENOENT on failure.
312fe9d4f57SAlexey Dobriyan  */
blocking_notifier_chain_unregister(struct blocking_notifier_head * nh,struct notifier_block * n)313fe9d4f57SAlexey Dobriyan int blocking_notifier_chain_unregister(struct blocking_notifier_head *nh,
314fe9d4f57SAlexey Dobriyan 		struct notifier_block *n)
315fe9d4f57SAlexey Dobriyan {
316fe9d4f57SAlexey Dobriyan 	int ret;
317fe9d4f57SAlexey Dobriyan 
318fe9d4f57SAlexey Dobriyan 	/*
319fe9d4f57SAlexey Dobriyan 	 * This code gets used during boot-up, when task switching is
320fe9d4f57SAlexey Dobriyan 	 * not yet working and interrupts must remain disabled.  At
321fe9d4f57SAlexey Dobriyan 	 * such times we must not call down_write().
322fe9d4f57SAlexey Dobriyan 	 */
323fe9d4f57SAlexey Dobriyan 	if (unlikely(system_state == SYSTEM_BOOTING))
324fe9d4f57SAlexey Dobriyan 		return notifier_chain_unregister(&nh->head, n);
325fe9d4f57SAlexey Dobriyan 
326fe9d4f57SAlexey Dobriyan 	down_write(&nh->rwsem);
327fe9d4f57SAlexey Dobriyan 	ret = notifier_chain_unregister(&nh->head, n);
328fe9d4f57SAlexey Dobriyan 	up_write(&nh->rwsem);
329fe9d4f57SAlexey Dobriyan 	return ret;
330fe9d4f57SAlexey Dobriyan }
331fe9d4f57SAlexey Dobriyan EXPORT_SYMBOL_GPL(blocking_notifier_chain_unregister);
332fe9d4f57SAlexey Dobriyan 
blocking_notifier_call_chain_robust(struct blocking_notifier_head * nh,unsigned long val_up,unsigned long val_down,void * v)33370d93298SPeter Zijlstra int blocking_notifier_call_chain_robust(struct blocking_notifier_head *nh,
33470d93298SPeter Zijlstra 		unsigned long val_up, unsigned long val_down, void *v)
33570d93298SPeter Zijlstra {
33670d93298SPeter Zijlstra 	int ret = NOTIFY_DONE;
33770d93298SPeter Zijlstra 
33870d93298SPeter Zijlstra 	/*
33970d93298SPeter Zijlstra 	 * We check the head outside the lock, but if this access is
34070d93298SPeter Zijlstra 	 * racy then it does not matter what the result of the test
34170d93298SPeter Zijlstra 	 * is, we re-check the list after having taken the lock anyway:
34270d93298SPeter Zijlstra 	 */
34370d93298SPeter Zijlstra 	if (rcu_access_pointer(nh->head)) {
34470d93298SPeter Zijlstra 		down_read(&nh->rwsem);
34570d93298SPeter Zijlstra 		ret = notifier_call_chain_robust(&nh->head, val_up, val_down, v);
34670d93298SPeter Zijlstra 		up_read(&nh->rwsem);
34770d93298SPeter Zijlstra 	}
34870d93298SPeter Zijlstra 	return ret;
34970d93298SPeter Zijlstra }
35070d93298SPeter Zijlstra EXPORT_SYMBOL_GPL(blocking_notifier_call_chain_robust);
35170d93298SPeter Zijlstra 
352fe9d4f57SAlexey Dobriyan /**
35370d93298SPeter Zijlstra  *	blocking_notifier_call_chain - Call functions in a blocking notifier chain
354fe9d4f57SAlexey Dobriyan  *	@nh: Pointer to head of the blocking notifier chain
355fe9d4f57SAlexey Dobriyan  *	@val: Value passed unmodified to notifier function
356fe9d4f57SAlexey Dobriyan  *	@v: Pointer passed unmodified to notifier function
357fe9d4f57SAlexey Dobriyan  *
358fe9d4f57SAlexey Dobriyan  *	Calls each function in a notifier chain in turn.  The functions
359fe9d4f57SAlexey Dobriyan  *	run in a process context, so they are allowed to block.
360fe9d4f57SAlexey Dobriyan  *
361fe9d4f57SAlexey Dobriyan  *	If the return value of the notifier can be and'ed
362fe9d4f57SAlexey Dobriyan  *	with %NOTIFY_STOP_MASK then blocking_notifier_call_chain()
363fe9d4f57SAlexey Dobriyan  *	will return immediately, with the return value of
364fe9d4f57SAlexey Dobriyan  *	the notifier function which halted execution.
365fe9d4f57SAlexey Dobriyan  *	Otherwise the return value is the return value
366fe9d4f57SAlexey Dobriyan  *	of the last notifier function called.
367fe9d4f57SAlexey Dobriyan  */
blocking_notifier_call_chain(struct blocking_notifier_head * nh,unsigned long val,void * v)36870d93298SPeter Zijlstra int blocking_notifier_call_chain(struct blocking_notifier_head *nh,
36970d93298SPeter Zijlstra 		unsigned long val, void *v)
370fe9d4f57SAlexey Dobriyan {
371fe9d4f57SAlexey Dobriyan 	int ret = NOTIFY_DONE;
372fe9d4f57SAlexey Dobriyan 
373fe9d4f57SAlexey Dobriyan 	/*
374fe9d4f57SAlexey Dobriyan 	 * We check the head outside the lock, but if this access is
375fe9d4f57SAlexey Dobriyan 	 * racy then it does not matter what the result of the test
376fe9d4f57SAlexey Dobriyan 	 * is, we re-check the list after having taken the lock anyway:
377fe9d4f57SAlexey Dobriyan 	 */
3788857563bSPaul E. McKenney 	if (rcu_access_pointer(nh->head)) {
379fe9d4f57SAlexey Dobriyan 		down_read(&nh->rwsem);
38070d93298SPeter Zijlstra 		ret = notifier_call_chain(&nh->head, val, v, -1, NULL);
381fe9d4f57SAlexey Dobriyan 		up_read(&nh->rwsem);
382fe9d4f57SAlexey Dobriyan 	}
383fe9d4f57SAlexey Dobriyan 	return ret;
384fe9d4f57SAlexey Dobriyan }
385fe9d4f57SAlexey Dobriyan EXPORT_SYMBOL_GPL(blocking_notifier_call_chain);
386fe9d4f57SAlexey Dobriyan 
387fe9d4f57SAlexey Dobriyan /*
388fe9d4f57SAlexey Dobriyan  *	Raw notifier chain routines.  There is no protection;
389fe9d4f57SAlexey Dobriyan  *	the caller must provide it.  Use at your own risk!
390fe9d4f57SAlexey Dobriyan  */
391fe9d4f57SAlexey Dobriyan 
392fe9d4f57SAlexey Dobriyan /**
393fe9d4f57SAlexey Dobriyan  *	raw_notifier_chain_register - Add notifier to a raw notifier chain
394fe9d4f57SAlexey Dobriyan  *	@nh: Pointer to head of the raw notifier chain
395fe9d4f57SAlexey Dobriyan  *	@n: New entry in notifier chain
396fe9d4f57SAlexey Dobriyan  *
397fe9d4f57SAlexey Dobriyan  *	Adds a notifier to a raw notifier chain.
398fe9d4f57SAlexey Dobriyan  *	All locking must be provided by the caller.
399fe9d4f57SAlexey Dobriyan  *
4005abb065dSBorislav Petkov  *	Returns 0 on success, %-EEXIST on error.
401fe9d4f57SAlexey Dobriyan  */
raw_notifier_chain_register(struct raw_notifier_head * nh,struct notifier_block * n)402fe9d4f57SAlexey Dobriyan int raw_notifier_chain_register(struct raw_notifier_head *nh,
403fe9d4f57SAlexey Dobriyan 		struct notifier_block *n)
404fe9d4f57SAlexey Dobriyan {
405c82f898dSDmitry Osipenko 	return notifier_chain_register(&nh->head, n, false);
406fe9d4f57SAlexey Dobriyan }
407fe9d4f57SAlexey Dobriyan EXPORT_SYMBOL_GPL(raw_notifier_chain_register);
408fe9d4f57SAlexey Dobriyan 
409fe9d4f57SAlexey Dobriyan /**
410fe9d4f57SAlexey Dobriyan  *	raw_notifier_chain_unregister - Remove notifier from a raw notifier chain
411fe9d4f57SAlexey Dobriyan  *	@nh: Pointer to head of the raw notifier chain
412fe9d4f57SAlexey Dobriyan  *	@n: Entry to remove from notifier chain
413fe9d4f57SAlexey Dobriyan  *
414fe9d4f57SAlexey Dobriyan  *	Removes a notifier from a raw notifier chain.
415fe9d4f57SAlexey Dobriyan  *	All locking must be provided by the caller.
416fe9d4f57SAlexey Dobriyan  *
417fe9d4f57SAlexey Dobriyan  *	Returns zero on success or %-ENOENT on failure.
418fe9d4f57SAlexey Dobriyan  */
raw_notifier_chain_unregister(struct raw_notifier_head * nh,struct notifier_block * n)419fe9d4f57SAlexey Dobriyan int raw_notifier_chain_unregister(struct raw_notifier_head *nh,
420fe9d4f57SAlexey Dobriyan 		struct notifier_block *n)
421fe9d4f57SAlexey Dobriyan {
422fe9d4f57SAlexey Dobriyan 	return notifier_chain_unregister(&nh->head, n);
423fe9d4f57SAlexey Dobriyan }
424fe9d4f57SAlexey Dobriyan EXPORT_SYMBOL_GPL(raw_notifier_chain_unregister);
425fe9d4f57SAlexey Dobriyan 
raw_notifier_call_chain_robust(struct raw_notifier_head * nh,unsigned long val_up,unsigned long val_down,void * v)42670d93298SPeter Zijlstra int raw_notifier_call_chain_robust(struct raw_notifier_head *nh,
42770d93298SPeter Zijlstra 		unsigned long val_up, unsigned long val_down, void *v)
42870d93298SPeter Zijlstra {
42970d93298SPeter Zijlstra 	return notifier_call_chain_robust(&nh->head, val_up, val_down, v);
43070d93298SPeter Zijlstra }
43170d93298SPeter Zijlstra EXPORT_SYMBOL_GPL(raw_notifier_call_chain_robust);
43270d93298SPeter Zijlstra 
433fe9d4f57SAlexey Dobriyan /**
43470d93298SPeter Zijlstra  *	raw_notifier_call_chain - Call functions in a raw notifier chain
435fe9d4f57SAlexey Dobriyan  *	@nh: Pointer to head of the raw notifier chain
436fe9d4f57SAlexey Dobriyan  *	@val: Value passed unmodified to notifier function
437fe9d4f57SAlexey Dobriyan  *	@v: Pointer passed unmodified to notifier function
438fe9d4f57SAlexey Dobriyan  *
439fe9d4f57SAlexey Dobriyan  *	Calls each function in a notifier chain in turn.  The functions
440fe9d4f57SAlexey Dobriyan  *	run in an undefined context.
441fe9d4f57SAlexey Dobriyan  *	All locking must be provided by the caller.
442fe9d4f57SAlexey Dobriyan  *
443fe9d4f57SAlexey Dobriyan  *	If the return value of the notifier can be and'ed
444fe9d4f57SAlexey Dobriyan  *	with %NOTIFY_STOP_MASK then raw_notifier_call_chain()
445fe9d4f57SAlexey Dobriyan  *	will return immediately, with the return value of
446fe9d4f57SAlexey Dobriyan  *	the notifier function which halted execution.
447fe9d4f57SAlexey Dobriyan  *	Otherwise the return value is the return value
448fe9d4f57SAlexey Dobriyan  *	of the last notifier function called.
449fe9d4f57SAlexey Dobriyan  */
raw_notifier_call_chain(struct raw_notifier_head * nh,unsigned long val,void * v)450fe9d4f57SAlexey Dobriyan int raw_notifier_call_chain(struct raw_notifier_head *nh,
451fe9d4f57SAlexey Dobriyan 		unsigned long val, void *v)
452fe9d4f57SAlexey Dobriyan {
45370d93298SPeter Zijlstra 	return notifier_call_chain(&nh->head, val, v, -1, NULL);
454fe9d4f57SAlexey Dobriyan }
455fe9d4f57SAlexey Dobriyan EXPORT_SYMBOL_GPL(raw_notifier_call_chain);
456fe9d4f57SAlexey Dobriyan 
457fe9d4f57SAlexey Dobriyan /*
458fe9d4f57SAlexey Dobriyan  *	SRCU notifier chain routines.    Registration and unregistration
459fe9d4f57SAlexey Dobriyan  *	use a mutex, and call_chain is synchronized by SRCU (no locks).
460fe9d4f57SAlexey Dobriyan  */
461fe9d4f57SAlexey Dobriyan 
462fe9d4f57SAlexey Dobriyan /**
463fe9d4f57SAlexey Dobriyan  *	srcu_notifier_chain_register - Add notifier to an SRCU notifier chain
464fe9d4f57SAlexey Dobriyan  *	@nh: Pointer to head of the SRCU notifier chain
465fe9d4f57SAlexey Dobriyan  *	@n: New entry in notifier chain
466fe9d4f57SAlexey Dobriyan  *
467fe9d4f57SAlexey Dobriyan  *	Adds a notifier to an SRCU notifier chain.
468fe9d4f57SAlexey Dobriyan  *	Must be called in process context.
469fe9d4f57SAlexey Dobriyan  *
4705abb065dSBorislav Petkov  *	Returns 0 on success, %-EEXIST on error.
471fe9d4f57SAlexey Dobriyan  */
srcu_notifier_chain_register(struct srcu_notifier_head * nh,struct notifier_block * n)472fe9d4f57SAlexey Dobriyan int srcu_notifier_chain_register(struct srcu_notifier_head *nh,
473fe9d4f57SAlexey Dobriyan 		struct notifier_block *n)
474fe9d4f57SAlexey Dobriyan {
475fe9d4f57SAlexey Dobriyan 	int ret;
476fe9d4f57SAlexey Dobriyan 
477fe9d4f57SAlexey Dobriyan 	/*
478fe9d4f57SAlexey Dobriyan 	 * This code gets used during boot-up, when task switching is
479fe9d4f57SAlexey Dobriyan 	 * not yet working and interrupts must remain disabled.  At
480fe9d4f57SAlexey Dobriyan 	 * such times we must not call mutex_lock().
481fe9d4f57SAlexey Dobriyan 	 */
482fe9d4f57SAlexey Dobriyan 	if (unlikely(system_state == SYSTEM_BOOTING))
483c82f898dSDmitry Osipenko 		return notifier_chain_register(&nh->head, n, false);
484fe9d4f57SAlexey Dobriyan 
485fe9d4f57SAlexey Dobriyan 	mutex_lock(&nh->mutex);
486c82f898dSDmitry Osipenko 	ret = notifier_chain_register(&nh->head, n, false);
487fe9d4f57SAlexey Dobriyan 	mutex_unlock(&nh->mutex);
488fe9d4f57SAlexey Dobriyan 	return ret;
489fe9d4f57SAlexey Dobriyan }
490fe9d4f57SAlexey Dobriyan EXPORT_SYMBOL_GPL(srcu_notifier_chain_register);
491fe9d4f57SAlexey Dobriyan 
492fe9d4f57SAlexey Dobriyan /**
493fe9d4f57SAlexey Dobriyan  *	srcu_notifier_chain_unregister - Remove notifier from an SRCU notifier chain
494fe9d4f57SAlexey Dobriyan  *	@nh: Pointer to head of the SRCU notifier chain
495fe9d4f57SAlexey Dobriyan  *	@n: Entry to remove from notifier chain
496fe9d4f57SAlexey Dobriyan  *
497fe9d4f57SAlexey Dobriyan  *	Removes a notifier from an SRCU notifier chain.
498fe9d4f57SAlexey Dobriyan  *	Must be called from process context.
499fe9d4f57SAlexey Dobriyan  *
500fe9d4f57SAlexey Dobriyan  *	Returns zero on success or %-ENOENT on failure.
501fe9d4f57SAlexey Dobriyan  */
srcu_notifier_chain_unregister(struct srcu_notifier_head * nh,struct notifier_block * n)502fe9d4f57SAlexey Dobriyan int srcu_notifier_chain_unregister(struct srcu_notifier_head *nh,
503fe9d4f57SAlexey Dobriyan 		struct notifier_block *n)
504fe9d4f57SAlexey Dobriyan {
505fe9d4f57SAlexey Dobriyan 	int ret;
506fe9d4f57SAlexey Dobriyan 
507fe9d4f57SAlexey Dobriyan 	/*
508fe9d4f57SAlexey Dobriyan 	 * This code gets used during boot-up, when task switching is
509fe9d4f57SAlexey Dobriyan 	 * not yet working and interrupts must remain disabled.  At
510fe9d4f57SAlexey Dobriyan 	 * such times we must not call mutex_lock().
511fe9d4f57SAlexey Dobriyan 	 */
512fe9d4f57SAlexey Dobriyan 	if (unlikely(system_state == SYSTEM_BOOTING))
513fe9d4f57SAlexey Dobriyan 		return notifier_chain_unregister(&nh->head, n);
514fe9d4f57SAlexey Dobriyan 
515fe9d4f57SAlexey Dobriyan 	mutex_lock(&nh->mutex);
516fe9d4f57SAlexey Dobriyan 	ret = notifier_chain_unregister(&nh->head, n);
517fe9d4f57SAlexey Dobriyan 	mutex_unlock(&nh->mutex);
518fe9d4f57SAlexey Dobriyan 	synchronize_srcu(&nh->srcu);
519fe9d4f57SAlexey Dobriyan 	return ret;
520fe9d4f57SAlexey Dobriyan }
521fe9d4f57SAlexey Dobriyan EXPORT_SYMBOL_GPL(srcu_notifier_chain_unregister);
522fe9d4f57SAlexey Dobriyan 
523fe9d4f57SAlexey Dobriyan /**
52470d93298SPeter Zijlstra  *	srcu_notifier_call_chain - Call functions in an SRCU notifier chain
525fe9d4f57SAlexey Dobriyan  *	@nh: Pointer to head of the SRCU notifier chain
526fe9d4f57SAlexey Dobriyan  *	@val: Value passed unmodified to notifier function
527fe9d4f57SAlexey Dobriyan  *	@v: Pointer passed unmodified to notifier function
528fe9d4f57SAlexey Dobriyan  *
529fe9d4f57SAlexey Dobriyan  *	Calls each function in a notifier chain in turn.  The functions
530fe9d4f57SAlexey Dobriyan  *	run in a process context, so they are allowed to block.
531fe9d4f57SAlexey Dobriyan  *
532fe9d4f57SAlexey Dobriyan  *	If the return value of the notifier can be and'ed
533fe9d4f57SAlexey Dobriyan  *	with %NOTIFY_STOP_MASK then srcu_notifier_call_chain()
534fe9d4f57SAlexey Dobriyan  *	will return immediately, with the return value of
535fe9d4f57SAlexey Dobriyan  *	the notifier function which halted execution.
536fe9d4f57SAlexey Dobriyan  *	Otherwise the return value is the return value
537fe9d4f57SAlexey Dobriyan  *	of the last notifier function called.
538fe9d4f57SAlexey Dobriyan  */
srcu_notifier_call_chain(struct srcu_notifier_head * nh,unsigned long val,void * v)53970d93298SPeter Zijlstra int srcu_notifier_call_chain(struct srcu_notifier_head *nh,
54070d93298SPeter Zijlstra 		unsigned long val, void *v)
541fe9d4f57SAlexey Dobriyan {
542fe9d4f57SAlexey Dobriyan 	int ret;
543fe9d4f57SAlexey Dobriyan 	int idx;
544fe9d4f57SAlexey Dobriyan 
545fe9d4f57SAlexey Dobriyan 	idx = srcu_read_lock(&nh->srcu);
54670d93298SPeter Zijlstra 	ret = notifier_call_chain(&nh->head, val, v, -1, NULL);
547fe9d4f57SAlexey Dobriyan 	srcu_read_unlock(&nh->srcu, idx);
548fe9d4f57SAlexey Dobriyan 	return ret;
549fe9d4f57SAlexey Dobriyan }
550fe9d4f57SAlexey Dobriyan EXPORT_SYMBOL_GPL(srcu_notifier_call_chain);
551fe9d4f57SAlexey Dobriyan 
552fe9d4f57SAlexey Dobriyan /**
553fe9d4f57SAlexey Dobriyan  *	srcu_init_notifier_head - Initialize an SRCU notifier head
554fe9d4f57SAlexey Dobriyan  *	@nh: Pointer to head of the srcu notifier chain
555fe9d4f57SAlexey Dobriyan  *
556fe9d4f57SAlexey Dobriyan  *	Unlike other sorts of notifier heads, SRCU notifier heads require
557fe9d4f57SAlexey Dobriyan  *	dynamic initialization.  Be sure to call this routine before
558fe9d4f57SAlexey Dobriyan  *	calling any of the other SRCU notifier routines for this head.
559fe9d4f57SAlexey Dobriyan  *
560fe9d4f57SAlexey Dobriyan  *	If an SRCU notifier head is deallocated, it must first be cleaned
561fe9d4f57SAlexey Dobriyan  *	up by calling srcu_cleanup_notifier_head().  Otherwise the head's
562fe9d4f57SAlexey Dobriyan  *	per-cpu data (used by the SRCU mechanism) will leak.
563fe9d4f57SAlexey Dobriyan  */
srcu_init_notifier_head(struct srcu_notifier_head * nh)564fe9d4f57SAlexey Dobriyan void srcu_init_notifier_head(struct srcu_notifier_head *nh)
565fe9d4f57SAlexey Dobriyan {
566fe9d4f57SAlexey Dobriyan 	mutex_init(&nh->mutex);
567fe9d4f57SAlexey Dobriyan 	if (init_srcu_struct(&nh->srcu) < 0)
568fe9d4f57SAlexey Dobriyan 		BUG();
569fe9d4f57SAlexey Dobriyan 	nh->head = NULL;
570fe9d4f57SAlexey Dobriyan }
571fe9d4f57SAlexey Dobriyan EXPORT_SYMBOL_GPL(srcu_init_notifier_head);
572fe9d4f57SAlexey Dobriyan 
573fe9d4f57SAlexey Dobriyan static ATOMIC_NOTIFIER_HEAD(die_chain);
574fe9d4f57SAlexey Dobriyan 
notify_die(enum die_val val,const char * str,struct pt_regs * regs,long err,int trap,int sig)575b40a2cb6SMasami Hiramatsu int notrace notify_die(enum die_val val, const char *str,
576fe9d4f57SAlexey Dobriyan 	       struct pt_regs *regs, long err, int trap, int sig)
577fe9d4f57SAlexey Dobriyan {
578fe9d4f57SAlexey Dobriyan 	struct die_args args = {
579fe9d4f57SAlexey Dobriyan 		.regs	= regs,
580fe9d4f57SAlexey Dobriyan 		.str	= str,
581fe9d4f57SAlexey Dobriyan 		.err	= err,
582fe9d4f57SAlexey Dobriyan 		.trapnr	= trap,
583fe9d4f57SAlexey Dobriyan 		.signr	= sig,
584fe9d4f57SAlexey Dobriyan 
585fe9d4f57SAlexey Dobriyan 	};
5865778077dSLinus Torvalds 	RCU_LOCKDEP_WARN(!rcu_is_watching(),
587e727c7d7SAndy Lutomirski 			   "notify_die called but RCU thinks we're quiescent");
588fe9d4f57SAlexey Dobriyan 	return atomic_notifier_call_chain(&die_chain, val, &args);
589fe9d4f57SAlexey Dobriyan }
590b40a2cb6SMasami Hiramatsu NOKPROBE_SYMBOL(notify_die);
591fe9d4f57SAlexey Dobriyan 
register_die_notifier(struct notifier_block * nb)592fe9d4f57SAlexey Dobriyan int register_die_notifier(struct notifier_block *nb)
593fe9d4f57SAlexey Dobriyan {
594fe9d4f57SAlexey Dobriyan 	return atomic_notifier_chain_register(&die_chain, nb);
595fe9d4f57SAlexey Dobriyan }
596fe9d4f57SAlexey Dobriyan EXPORT_SYMBOL_GPL(register_die_notifier);
597fe9d4f57SAlexey Dobriyan 
unregister_die_notifier(struct notifier_block * nb)598fe9d4f57SAlexey Dobriyan int unregister_die_notifier(struct notifier_block *nb)
599fe9d4f57SAlexey Dobriyan {
600fe9d4f57SAlexey Dobriyan 	return atomic_notifier_chain_unregister(&die_chain, nb);
601fe9d4f57SAlexey Dobriyan }
602fe9d4f57SAlexey Dobriyan EXPORT_SYMBOL_GPL(unregister_die_notifier);
603