xref: /linux/include/linux/interrupt_rc.h (revision c8990f3179e5636832fc22e6a262de5d50c797e3)
1 /* SPDX-License-Identifier: GPL-2.0 */
2 #ifndef __LINUX_INTERRUPT_RC_H
3 #define __LINUX_INTERRUPT_RC_H
4 
5 /*
6  * include/linux/interrupt_rc.h - refcounted local processor interrupt
7  * management.
8  *
9  * Since the implementation of this API currently depends on
10  * local_irq_save()/local_irq_restore(), we split this into its own header to
11  * make it easier to include without hitting circular header dependencies.
12  */
13 
14 #include <linux/irqflags.h>
15 #include <linux/preempt.h>
16 #include <linux/processor.h>
17 #include <linux/smp.h>
18 
19 #ifndef MODULE
20 /* Per-CPU interrupt disabling state for local_interrupt_{disable,enable}(). */
21 DECLARE_PER_CPU(unsigned long, local_interrupt_disable_state);
22 
__local_interrupt_save_state(unsigned long flags)23 static __always_inline void __local_interrupt_save_state(unsigned long flags)
24 {
25 	raw_cpu_write(local_interrupt_disable_state, flags);
26 }
27 
__local_interrupt_enable(void)28 static __always_inline void __local_interrupt_enable(void)
29 {
30 	unsigned long flags = raw_cpu_read(local_interrupt_disable_state);
31 
32 	local_irq_restore(flags);
33 }
34 
35 #ifndef INSTANTIATE_EXPORTED_INTERRUPT_DISABLE
_local_interrupt_save_state(unsigned long flags)36 static __always_inline void _local_interrupt_save_state(unsigned long flags)
37 {
38 	__local_interrupt_save_state(flags);
39 }
40 
_local_interrupt_enable(void)41 static __always_inline void _local_interrupt_enable(void)
42 {
43 	__local_interrupt_enable();
44 }
45 #else
46 extern void _local_interrupt_save_state(unsigned long flags);
47 extern void _local_interrupt_enable(void);
48 #endif
49 
50 #else /* !MODULE */
51 extern void _local_interrupt_save_state(unsigned long flags);
52 extern void _local_interrupt_enable(void);
53 #endif /* !MODULE */
54 
55 #define hardirq_disable_enter()	__preempt_count_add_return(HARDIRQ_DISABLE_OFFSET)
56 #define hardirq_disable_exit()	__preempt_count_sub_return(HARDIRQ_DISABLE_OFFSET)
57 
local_interrupt_disable(void)58 static inline void local_interrupt_disable(void)
59 {
60 	int new_count;
61 	unsigned long flags;
62 
63 	WARN_ON_ONCE(in_nmi());
64 
65 	local_irq_save(flags);
66 	new_count = hardirq_disable_enter();
67 
68 	if ((new_count & HARDIRQ_DISABLE_MASK) == HARDIRQ_DISABLE_OFFSET)
69 		_local_interrupt_save_state(flags);
70 }
71 
local_interrupt_enable(void)72 static inline void local_interrupt_enable(void)
73 {
74 	int new_count;
75 
76 	new_count = hardirq_disable_exit();
77 
78 	if ((new_count & HARDIRQ_DISABLE_MASK) == 0)
79 		_local_interrupt_enable();
80 }
81 
82 #endif /* !__LINUX_INTERRUPT_RC_H */
83