Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit db7e007f authored by Heiko Carstens's avatar Heiko Carstens Committed by Martin Schwidefsky
Browse files

s390/udelay: make udelay have busy loop semantics



When using systemtap it was observed that our udelay implementation is
rather suboptimal if being called from a kprobe handler installed by
systemtap.

The problem observed when a kprobe was installed on lock_acquired().
When the probe was hit the kprobe handler did call udelay, which set
up an (internal) timer and reenabled interrupts (only the clock comparator
interrupt) and waited for the interrupt.
This is an optimization to avoid that the cpu is busy looping while waiting
that enough time passes. The problem is that the interrupt handler still
does call irq_enter()/irq_exit() which then again can lead to a deadlock,
since some accounting functions may take locks as well.

If one of these locks is the same, which caused lock_acquired() to be
called, we have a nice deadlock.

This patch reworks the udelay code for the interrupts disabled case to
immediately leave the low level interrupt handler when the clock
comparator interrupt happens. That way no C code is being called and the
deadlock cannot happen anymore.

Signed-off-by: default avatarHeiko Carstens <heiko.carstens@de.ibm.com>
Reviewed-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent 0c5a69f4
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -24,4 +24,6 @@ struct s390_idle_data {
extern struct device_attribute dev_attr_idle_count;
extern struct device_attribute dev_attr_idle_time_us;

void psw_idle(struct s390_idle_data *, unsigned long);

#endif /* _S390_IDLE_H */
+13 −0
Original line number Diff line number Diff line
@@ -96,6 +96,19 @@ enum irq_subclass {
	IRQ_SUBCLASS_SERVICE_SIGNAL = 9,
};

#define CR0_IRQ_SUBCLASS_MASK					  \
	((1UL << (63 - 30))  /* Warning Track */		| \
	 (1UL << (63 - 48))  /* Malfunction Alert */		| \
	 (1UL << (63 - 49))  /* Emergency Signal */		| \
	 (1UL << (63 - 50))  /* External Call */		| \
	 (1UL << (63 - 52))  /* Clock Comparator */		| \
	 (1UL << (63 - 53))  /* CPU Timer */			| \
	 (1UL << (63 - 54))  /* Service Signal */		| \
	 (1UL << (63 - 57))  /* Interrupt Key */		| \
	 (1UL << (63 - 58))  /* Measurement Alert */		| \
	 (1UL << (63 - 59))  /* Timing Alert */			| \
	 (1UL << (63 - 62))) /* IUCV */

void irq_subclass_register(enum irq_subclass subclass);
void irq_subclass_unregister(enum irq_subclass subclass);

+2 −0
Original line number Diff line number Diff line
@@ -15,11 +15,13 @@
#define CIF_ASCE		1	/* user asce needs fixup / uaccess */
#define CIF_NOHZ_DELAY		2	/* delay HZ disable for a tick */
#define CIF_FPU			3	/* restore vector registers */
#define CIF_IGNORE_IRQ		4	/* ignore interrupt (for udelay) */

#define _CIF_MCCK_PENDING	(1<<CIF_MCCK_PENDING)
#define _CIF_ASCE		(1<<CIF_ASCE)
#define _CIF_NOHZ_DELAY		(1<<CIF_NOHZ_DELAY)
#define _CIF_FPU		(1<<CIF_FPU)
#define _CIF_IGNORE_IRQ		(1<<CIF_IGNORE_IRQ)

#ifndef __ASSEMBLY__

+4 −0
Original line number Diff line number Diff line
@@ -563,6 +563,8 @@ ENTRY(io_int_handler)
	stmg	%r8,%r9,__PT_PSW(%r11)
	mvc	__PT_INT_CODE(12,%r11),__LC_SUBCHANNEL_ID
	xc	__PT_FLAGS(8,%r11),__PT_FLAGS(%r11)
	TSTMSK	__LC_CPU_FLAGS,_CIF_IGNORE_IRQ
	jo	.Lio_restore
	TRACE_IRQS_OFF
	xc	__SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15)
.Lio_loop:
@@ -738,6 +740,8 @@ ENTRY(ext_int_handler)
	mvc	__PT_INT_PARM(4,%r11),__LC_EXT_PARAMS
	mvc	__PT_INT_PARM_LONG(8,%r11),0(%r1)
	xc	__PT_FLAGS(8,%r11),__PT_FLAGS(%r11)
	TSTMSK	__LC_CPU_FLAGS,_CIF_IGNORE_IRQ
	jo	.Lio_restore
	TRACE_IRQS_OFF
	xc	__SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15)
	lgr	%r2,%r11		# pass pointer to pt_regs
+0 −1
Original line number Diff line number Diff line
@@ -16,7 +16,6 @@ void io_int_handler(void);
void mcck_int_handler(void);
void restart_int_handler(void);
void restart_call_handler(void);
void psw_idle(struct s390_idle_data *, unsigned long);

asmlinkage long do_syscall_trace_enter(struct pt_regs *regs);
asmlinkage void do_syscall_trace_exit(struct pt_regs *regs);
Loading