diff options
author | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2013-06-17 08:54:02 -0400 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2013-06-26 15:10:23 -0400 |
commit | 48f6b00c6e3190b786c44731b25ac124c81c2247 (patch) | |
tree | c06d6ba767544eba04a2cd47db1c303f6a35d9f7 | |
parent | 4bdb613ff45ef5f2848584b250f8a65f6d6c5755 (diff) |
s390/irq: store interrupt information in pt_regs
Copy the interrupt parameters from the lowcore to the pt_regs structure
in entry[64].S and reduce the arguments of the low level interrupt handler
to the pt_regs pointer only. In addition move the test-pending-interrupt
loop from do_IRQ to entry[64].S to make sure that interrupt information
is always delivered via pt_regs.
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
-rw-r--r-- | arch/s390/include/asm/ptrace.h | 1 | ||||
-rw-r--r-- | arch/s390/kernel/asm-offsets.c | 1 | ||||
-rw-r--r-- | arch/s390/kernel/entry.S | 12 | ||||
-rw-r--r-- | arch/s390/kernel/entry.h | 2 | ||||
-rw-r--r-- | arch/s390/kernel/entry64.S | 16 | ||||
-rw-r--r-- | arch/s390/kernel/irq.c | 8 | ||||
-rw-r--r-- | drivers/s390/cio/cio.c | 68 |
7 files changed, 58 insertions, 50 deletions
diff --git a/arch/s390/include/asm/ptrace.h b/arch/s390/include/asm/ptrace.h index 559512a455da..52b56533c57c 100644 --- a/arch/s390/include/asm/ptrace.h +++ b/arch/s390/include/asm/ptrace.h | |||
@@ -24,6 +24,7 @@ struct pt_regs | |||
24 | unsigned long gprs[NUM_GPRS]; | 24 | unsigned long gprs[NUM_GPRS]; |
25 | unsigned long orig_gpr2; | 25 | unsigned long orig_gpr2; |
26 | unsigned int int_code; | 26 | unsigned int int_code; |
27 | unsigned int int_parm; | ||
27 | unsigned long int_parm_long; | 28 | unsigned long int_parm_long; |
28 | }; | 29 | }; |
29 | 30 | ||
diff --git a/arch/s390/kernel/asm-offsets.c b/arch/s390/kernel/asm-offsets.c index 7a82f9f70100..d6de844bc30a 100644 --- a/arch/s390/kernel/asm-offsets.c +++ b/arch/s390/kernel/asm-offsets.c | |||
@@ -47,6 +47,7 @@ int main(void) | |||
47 | DEFINE(__PT_GPRS, offsetof(struct pt_regs, gprs)); | 47 | DEFINE(__PT_GPRS, offsetof(struct pt_regs, gprs)); |
48 | DEFINE(__PT_ORIG_GPR2, offsetof(struct pt_regs, orig_gpr2)); | 48 | DEFINE(__PT_ORIG_GPR2, offsetof(struct pt_regs, orig_gpr2)); |
49 | DEFINE(__PT_INT_CODE, offsetof(struct pt_regs, int_code)); | 49 | DEFINE(__PT_INT_CODE, offsetof(struct pt_regs, int_code)); |
50 | DEFINE(__PT_INT_PARM, offsetof(struct pt_regs, int_parm)); | ||
50 | DEFINE(__PT_INT_PARM_LONG, offsetof(struct pt_regs, int_parm_long)); | 51 | DEFINE(__PT_INT_PARM_LONG, offsetof(struct pt_regs, int_parm_long)); |
51 | DEFINE(__PT_SIZE, sizeof(struct pt_regs)); | 52 | DEFINE(__PT_SIZE, sizeof(struct pt_regs)); |
52 | BLANK(); | 53 | BLANK(); |
diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S index 4d5e6f8a7978..be7a408be7a1 100644 --- a/arch/s390/kernel/entry.S +++ b/arch/s390/kernel/entry.S | |||
@@ -429,11 +429,19 @@ io_skip: | |||
429 | stm %r0,%r7,__PT_R0(%r11) | 429 | stm %r0,%r7,__PT_R0(%r11) |
430 | mvc __PT_R8(32,%r11),__LC_SAVE_AREA_ASYNC | 430 | mvc __PT_R8(32,%r11),__LC_SAVE_AREA_ASYNC |
431 | stm %r8,%r9,__PT_PSW(%r11) | 431 | stm %r8,%r9,__PT_PSW(%r11) |
432 | mvc __PT_INT_CODE(12,%r11),__LC_SUBCHANNEL_ID | ||
432 | TRACE_IRQS_OFF | 433 | TRACE_IRQS_OFF |
433 | xc __SF_BACKCHAIN(4,%r15),__SF_BACKCHAIN(%r15) | 434 | xc __SF_BACKCHAIN(4,%r15),__SF_BACKCHAIN(%r15) |
435 | io_loop: | ||
434 | l %r1,BASED(.Ldo_IRQ) | 436 | l %r1,BASED(.Ldo_IRQ) |
435 | lr %r2,%r11 # pass pointer to pt_regs | 437 | lr %r2,%r11 # pass pointer to pt_regs |
436 | basr %r14,%r1 # call do_IRQ | 438 | basr %r14,%r1 # call do_IRQ |
439 | tm __LC_MACHINE_FLAGS+2,0x10 # MACHINE_FLAG_LPAR | ||
440 | jz io_return | ||
441 | tpi 0 | ||
442 | jz io_return | ||
443 | mvc __PT_INT_CODE(12,%r11),__LC_SUBCHANNEL_ID | ||
444 | j io_loop | ||
437 | io_return: | 445 | io_return: |
438 | LOCKDEP_SYS_EXIT | 446 | LOCKDEP_SYS_EXIT |
439 | TRACE_IRQS_ON | 447 | TRACE_IRQS_ON |
@@ -573,10 +581,10 @@ ext_skip: | |||
573 | stm %r0,%r7,__PT_R0(%r11) | 581 | stm %r0,%r7,__PT_R0(%r11) |
574 | mvc __PT_R8(32,%r11),__LC_SAVE_AREA_ASYNC | 582 | mvc __PT_R8(32,%r11),__LC_SAVE_AREA_ASYNC |
575 | stm %r8,%r9,__PT_PSW(%r11) | 583 | stm %r8,%r9,__PT_PSW(%r11) |
584 | mvc __PT_INT_CODE(4,%r11),__LC_EXT_CPU_ADDR | ||
585 | mvc __PT_INT_PARM(4,%r11),__LC_EXT_PARAMS | ||
576 | TRACE_IRQS_OFF | 586 | TRACE_IRQS_OFF |
577 | lr %r2,%r11 # pass pointer to pt_regs | 587 | lr %r2,%r11 # pass pointer to pt_regs |
578 | l %r3,__LC_EXT_CPU_ADDR # get cpu address + interruption code | ||
579 | l %r4,__LC_EXT_PARAMS # get external parameters | ||
580 | l %r1,BASED(.Ldo_extint) | 588 | l %r1,BASED(.Ldo_extint) |
581 | basr %r14,%r1 # call do_extint | 589 | basr %r14,%r1 # call do_extint |
582 | j io_return | 590 | j io_return |
diff --git a/arch/s390/kernel/entry.h b/arch/s390/kernel/entry.h index aa0ab02e9595..3ddbc26d246e 100644 --- a/arch/s390/kernel/entry.h +++ b/arch/s390/kernel/entry.h | |||
@@ -54,7 +54,7 @@ void handle_signal32(unsigned long sig, struct k_sigaction *ka, | |||
54 | void do_notify_resume(struct pt_regs *regs); | 54 | void do_notify_resume(struct pt_regs *regs); |
55 | 55 | ||
56 | struct ext_code; | 56 | struct ext_code; |
57 | void do_extint(struct pt_regs *regs, struct ext_code, unsigned int, unsigned long); | 57 | void do_extint(struct pt_regs *regs); |
58 | void do_restart(void); | 58 | void do_restart(void); |
59 | void __init startup_init(void); | 59 | void __init startup_init(void); |
60 | void die(struct pt_regs *regs, const char *str); | 60 | void die(struct pt_regs *regs, const char *str); |
diff --git a/arch/s390/kernel/entry64.S b/arch/s390/kernel/entry64.S index 4c17eece707e..bc5864c5148b 100644 --- a/arch/s390/kernel/entry64.S +++ b/arch/s390/kernel/entry64.S | |||
@@ -460,10 +460,18 @@ io_skip: | |||
460 | stmg %r0,%r7,__PT_R0(%r11) | 460 | stmg %r0,%r7,__PT_R0(%r11) |
461 | mvc __PT_R8(64,%r11),__LC_SAVE_AREA_ASYNC | 461 | mvc __PT_R8(64,%r11),__LC_SAVE_AREA_ASYNC |
462 | stmg %r8,%r9,__PT_PSW(%r11) | 462 | stmg %r8,%r9,__PT_PSW(%r11) |
463 | mvc __PT_INT_CODE(12,%r11),__LC_SUBCHANNEL_ID | ||
463 | TRACE_IRQS_OFF | 464 | TRACE_IRQS_OFF |
464 | xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15) | 465 | xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15) |
466 | io_loop: | ||
465 | lgr %r2,%r11 # pass pointer to pt_regs | 467 | lgr %r2,%r11 # pass pointer to pt_regs |
466 | brasl %r14,do_IRQ | 468 | brasl %r14,do_IRQ |
469 | tm __LC_MACHINE_FLAGS+6,0x10 # MACHINE_FLAG_LPAR | ||
470 | jz io_return | ||
471 | tpi 0 | ||
472 | jz io_return | ||
473 | mvc __PT_INT_CODE(12,%r11),__LC_SUBCHANNEL_ID | ||
474 | j io_loop | ||
467 | io_return: | 475 | io_return: |
468 | LOCKDEP_SYS_EXIT | 476 | LOCKDEP_SYS_EXIT |
469 | TRACE_IRQS_ON | 477 | TRACE_IRQS_ON |
@@ -605,13 +613,13 @@ ext_skip: | |||
605 | stmg %r0,%r7,__PT_R0(%r11) | 613 | stmg %r0,%r7,__PT_R0(%r11) |
606 | mvc __PT_R8(64,%r11),__LC_SAVE_AREA_ASYNC | 614 | mvc __PT_R8(64,%r11),__LC_SAVE_AREA_ASYNC |
607 | stmg %r8,%r9,__PT_PSW(%r11) | 615 | stmg %r8,%r9,__PT_PSW(%r11) |
616 | lghi %r1,__LC_EXT_PARAMS2 | ||
617 | mvc __PT_INT_CODE(4,%r11),__LC_EXT_CPU_ADDR | ||
618 | mvc __PT_INT_PARM(4,%r11),__LC_EXT_PARAMS | ||
619 | mvc __PT_INT_PARM_LONG(8,%r11),0(%r1) | ||
608 | TRACE_IRQS_OFF | 620 | TRACE_IRQS_OFF |
609 | xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15) | 621 | xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15) |
610 | lghi %r1,4096 | ||
611 | lgr %r2,%r11 # pass pointer to pt_regs | 622 | lgr %r2,%r11 # pass pointer to pt_regs |
612 | llgf %r3,__LC_EXT_CPU_ADDR # get cpu address + interruption code | ||
613 | llgf %r4,__LC_EXT_PARAMS # get external parameter | ||
614 | lg %r5,__LC_EXT_PARAMS2-4096(%r1) # get 64 bit external parameter | ||
615 | brasl %r14,do_extint | 623 | brasl %r14,do_extint |
616 | j io_return | 624 | j io_return |
617 | 625 | ||
diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c index dd3c1994b8bd..54b0995514e8 100644 --- a/arch/s390/kernel/irq.c +++ b/arch/s390/kernel/irq.c | |||
@@ -234,9 +234,9 @@ int unregister_external_interrupt(u16 code, ext_int_handler_t handler) | |||
234 | } | 234 | } |
235 | EXPORT_SYMBOL(unregister_external_interrupt); | 235 | EXPORT_SYMBOL(unregister_external_interrupt); |
236 | 236 | ||
237 | void __irq_entry do_extint(struct pt_regs *regs, struct ext_code ext_code, | 237 | void __irq_entry do_extint(struct pt_regs *regs) |
238 | unsigned int param32, unsigned long param64) | ||
239 | { | 238 | { |
239 | struct ext_code ext_code; | ||
240 | struct pt_regs *old_regs; | 240 | struct pt_regs *old_regs; |
241 | struct ext_int_info *p; | 241 | struct ext_int_info *p; |
242 | int index; | 242 | int index; |
@@ -248,6 +248,7 @@ void __irq_entry do_extint(struct pt_regs *regs, struct ext_code ext_code, | |||
248 | clock_comparator_work(); | 248 | clock_comparator_work(); |
249 | } | 249 | } |
250 | kstat_incr_irqs_this_cpu(EXTERNAL_INTERRUPT, NULL); | 250 | kstat_incr_irqs_this_cpu(EXTERNAL_INTERRUPT, NULL); |
251 | ext_code = *(struct ext_code *) ®s->int_code; | ||
251 | if (ext_code.code != 0x1004) | 252 | if (ext_code.code != 0x1004) |
252 | __get_cpu_var(s390_idle).nohz_delay = 1; | 253 | __get_cpu_var(s390_idle).nohz_delay = 1; |
253 | 254 | ||
@@ -255,7 +256,8 @@ void __irq_entry do_extint(struct pt_regs *regs, struct ext_code ext_code, | |||
255 | rcu_read_lock(); | 256 | rcu_read_lock(); |
256 | list_for_each_entry_rcu(p, &ext_int_hash[index], entry) | 257 | list_for_each_entry_rcu(p, &ext_int_hash[index], entry) |
257 | if (likely(p->code == ext_code.code)) | 258 | if (likely(p->code == ext_code.code)) |
258 | p->handler(ext_code, param32, param64); | 259 | p->handler(ext_code, regs->int_parm, |
260 | regs->int_parm_long); | ||
259 | rcu_read_unlock(); | 261 | rcu_read_unlock(); |
260 | irq_exit(); | 262 | irq_exit(); |
261 | set_irq_regs(old_regs); | 263 | set_irq_regs(old_regs); |
diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index 935d80b4e9ce..4eeb4a6bf207 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c | |||
@@ -568,7 +568,7 @@ out: | |||
568 | */ | 568 | */ |
569 | void __irq_entry do_IRQ(struct pt_regs *regs) | 569 | void __irq_entry do_IRQ(struct pt_regs *regs) |
570 | { | 570 | { |
571 | struct tpi_info *tpi_info; | 571 | struct tpi_info *tpi_info = (struct tpi_info *) ®s->int_code; |
572 | struct subchannel *sch; | 572 | struct subchannel *sch; |
573 | struct irb *irb; | 573 | struct irb *irb; |
574 | struct pt_regs *old_regs; | 574 | struct pt_regs *old_regs; |
@@ -579,46 +579,34 @@ void __irq_entry do_IRQ(struct pt_regs *regs) | |||
579 | if (S390_lowcore.int_clock >= S390_lowcore.clock_comparator) | 579 | if (S390_lowcore.int_clock >= S390_lowcore.clock_comparator) |
580 | /* Serve timer interrupts first. */ | 580 | /* Serve timer interrupts first. */ |
581 | clock_comparator_work(); | 581 | clock_comparator_work(); |
582 | /* | 582 | |
583 | * Get interrupt information from lowcore | 583 | kstat_incr_irqs_this_cpu(IO_INTERRUPT, NULL); |
584 | */ | 584 | irb = (struct irb *) &S390_lowcore.irb; |
585 | tpi_info = (struct tpi_info *)&S390_lowcore.subchannel_id; | 585 | if (tpi_info->adapter_IO) { |
586 | irb = (struct irb *)&S390_lowcore.irb; | 586 | do_adapter_IO(tpi_info->isc); |
587 | do { | 587 | goto out; |
588 | kstat_incr_irqs_this_cpu(IO_INTERRUPT, NULL); | 588 | } |
589 | if (tpi_info->adapter_IO) { | 589 | sch = (struct subchannel *)(unsigned long) tpi_info->intparm; |
590 | do_adapter_IO(tpi_info->isc); | 590 | if (!sch) { |
591 | continue; | 591 | /* Clear pending interrupt condition. */ |
592 | } | 592 | inc_irq_stat(IRQIO_CIO); |
593 | sch = (struct subchannel *)(unsigned long)tpi_info->intparm; | 593 | tsch(tpi_info->schid, irb); |
594 | if (!sch) { | 594 | goto out; |
595 | /* Clear pending interrupt condition. */ | 595 | } |
596 | inc_irq_stat(IRQIO_CIO); | 596 | spin_lock(sch->lock); |
597 | tsch(tpi_info->schid, irb); | 597 | /* Store interrupt response block to lowcore. */ |
598 | continue; | 598 | if (tsch(tpi_info->schid, irb) == 0) { |
599 | } | 599 | /* Keep subchannel information word up to date. */ |
600 | spin_lock(sch->lock); | 600 | memcpy (&sch->schib.scsw, &irb->scsw, sizeof (irb->scsw)); |
601 | /* Store interrupt response block to lowcore. */ | 601 | /* Call interrupt handler if there is one. */ |
602 | if (tsch(tpi_info->schid, irb) == 0) { | 602 | if (sch->driver && sch->driver->irq) |
603 | /* Keep subchannel information word up to date. */ | 603 | sch->driver->irq(sch); |
604 | memcpy (&sch->schib.scsw, &irb->scsw, | 604 | else |
605 | sizeof (irb->scsw)); | ||
606 | /* Call interrupt handler if there is one. */ | ||
607 | if (sch->driver && sch->driver->irq) | ||
608 | sch->driver->irq(sch); | ||
609 | else | ||
610 | inc_irq_stat(IRQIO_CIO); | ||
611 | } else | ||
612 | inc_irq_stat(IRQIO_CIO); | 605 | inc_irq_stat(IRQIO_CIO); |
613 | spin_unlock(sch->lock); | 606 | } else |
614 | /* | 607 | inc_irq_stat(IRQIO_CIO); |
615 | * Are more interrupts pending? | 608 | spin_unlock(sch->lock); |
616 | * If so, the tpi instruction will update the lowcore | 609 | out: |
617 | * to hold the info for the next interrupt. | ||
618 | * We don't do this for VM because a tpi drops the cpu | ||
619 | * out of the sie which costs more cycles than it saves. | ||
620 | */ | ||
621 | } while (MACHINE_IS_LPAR && tpi(NULL) != 0); | ||
622 | irq_exit(); | 610 | irq_exit(); |
623 | set_irq_regs(old_regs); | 611 | set_irq_regs(old_regs); |
624 | } | 612 | } |