s390/irq: store interrupt information in pt_regs
authorMartin Schwidefsky <schwidefsky@de.ibm.com>
Mon, 17 Jun 2013 12:54:02 +0000 (14:54 +0200)
committerMartin Schwidefsky <schwidefsky@de.ibm.com>
Wed, 26 Jun 2013 19:10:23 +0000 (21:10 +0200)
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>
arch/s390/include/asm/ptrace.h
arch/s390/kernel/asm-offsets.c
arch/s390/kernel/entry.S
arch/s390/kernel/entry.h
arch/s390/kernel/entry64.S
arch/s390/kernel/irq.c
drivers/s390/cio/cio.c

index 559512a455dabba842d5f4ade3b580fa61ea4b44..52b56533c57cda08f4d8283253ed682a13536f5c 100644 (file)
@@ -24,6 +24,7 @@ struct pt_regs
        unsigned long gprs[NUM_GPRS];
        unsigned long orig_gpr2;
        unsigned int int_code;
+       unsigned int int_parm;
        unsigned long int_parm_long;
 };
 
index 7a82f9f7010015e4d73349a1a46426017a4088dd..d6de844bc30ad6ef99ee9e28f943679d1dc99e51 100644 (file)
@@ -47,6 +47,7 @@ int main(void)
        DEFINE(__PT_GPRS, offsetof(struct pt_regs, gprs));
        DEFINE(__PT_ORIG_GPR2, offsetof(struct pt_regs, orig_gpr2));
        DEFINE(__PT_INT_CODE, offsetof(struct pt_regs, int_code));
+       DEFINE(__PT_INT_PARM, offsetof(struct pt_regs, int_parm));
        DEFINE(__PT_INT_PARM_LONG, offsetof(struct pt_regs, int_parm_long));
        DEFINE(__PT_SIZE, sizeof(struct pt_regs));
        BLANK();
index 4d5e6f8a7978cb005d0ae15fe3d1837175f285d1..be7a408be7a16bafde657665edb36fcde01c8866 100644 (file)
@@ -429,11 +429,19 @@ io_skip:
        stm     %r0,%r7,__PT_R0(%r11)
        mvc     __PT_R8(32,%r11),__LC_SAVE_AREA_ASYNC
        stm     %r8,%r9,__PT_PSW(%r11)
+       mvc     __PT_INT_CODE(12,%r11),__LC_SUBCHANNEL_ID
        TRACE_IRQS_OFF
        xc      __SF_BACKCHAIN(4,%r15),__SF_BACKCHAIN(%r15)
+io_loop:
        l       %r1,BASED(.Ldo_IRQ)
        lr      %r2,%r11                # pass pointer to pt_regs
        basr    %r14,%r1                # call do_IRQ
+       tm      __LC_MACHINE_FLAGS+2,0x10       # MACHINE_FLAG_LPAR
+       jz      io_return
+       tpi     0
+       jz      io_return
+       mvc     __PT_INT_CODE(12,%r11),__LC_SUBCHANNEL_ID
+       j       io_loop
 io_return:
        LOCKDEP_SYS_EXIT
        TRACE_IRQS_ON
@@ -573,10 +581,10 @@ ext_skip:
        stm     %r0,%r7,__PT_R0(%r11)
        mvc     __PT_R8(32,%r11),__LC_SAVE_AREA_ASYNC
        stm     %r8,%r9,__PT_PSW(%r11)
+       mvc     __PT_INT_CODE(4,%r11),__LC_EXT_CPU_ADDR
+       mvc     __PT_INT_PARM(4,%r11),__LC_EXT_PARAMS
        TRACE_IRQS_OFF
        lr      %r2,%r11                # pass pointer to pt_regs
-       l       %r3,__LC_EXT_CPU_ADDR   # get cpu address + interruption code
-       l       %r4,__LC_EXT_PARAMS     # get external parameters
        l       %r1,BASED(.Ldo_extint)
        basr    %r14,%r1                # call do_extint
        j       io_return
index aa0ab02e9595f8c6d21d6827a8b0ca261c0d71ec..3ddbc26d246e399a0e01bd295c1d9bbec741f034 100644 (file)
@@ -54,7 +54,7 @@ void handle_signal32(unsigned long sig, struct k_sigaction *ka,
 void do_notify_resume(struct pt_regs *regs);
 
 struct ext_code;
-void do_extint(struct pt_regs *regs, struct ext_code, unsigned int, unsigned long);
+void do_extint(struct pt_regs *regs);
 void do_restart(void);
 void __init startup_init(void);
 void die(struct pt_regs *regs, const char *str);
index 4c17eece707eb94d0b08c55b63a25b7788fb0c56..bc5864c5148b89c0de39d2291c83ad877d690e11 100644 (file)
@@ -460,10 +460,18 @@ io_skip:
        stmg    %r0,%r7,__PT_R0(%r11)
        mvc     __PT_R8(64,%r11),__LC_SAVE_AREA_ASYNC
        stmg    %r8,%r9,__PT_PSW(%r11)
+       mvc     __PT_INT_CODE(12,%r11),__LC_SUBCHANNEL_ID
        TRACE_IRQS_OFF
        xc      __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15)
+io_loop:
        lgr     %r2,%r11                # pass pointer to pt_regs
        brasl   %r14,do_IRQ
+       tm      __LC_MACHINE_FLAGS+6,0x10       # MACHINE_FLAG_LPAR
+       jz      io_return
+       tpi     0
+       jz      io_return
+       mvc     __PT_INT_CODE(12,%r11),__LC_SUBCHANNEL_ID
+       j       io_loop
 io_return:
        LOCKDEP_SYS_EXIT
        TRACE_IRQS_ON
@@ -605,13 +613,13 @@ ext_skip:
        stmg    %r0,%r7,__PT_R0(%r11)
        mvc     __PT_R8(64,%r11),__LC_SAVE_AREA_ASYNC
        stmg    %r8,%r9,__PT_PSW(%r11)
+       lghi    %r1,__LC_EXT_PARAMS2
+       mvc     __PT_INT_CODE(4,%r11),__LC_EXT_CPU_ADDR
+       mvc     __PT_INT_PARM(4,%r11),__LC_EXT_PARAMS
+       mvc     __PT_INT_PARM_LONG(8,%r11),0(%r1)
        TRACE_IRQS_OFF
        xc      __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15)
-       lghi    %r1,4096
        lgr     %r2,%r11                # pass pointer to pt_regs
-       llgf    %r3,__LC_EXT_CPU_ADDR   # get cpu address + interruption code
-       llgf    %r4,__LC_EXT_PARAMS     # get external parameter
-       lg      %r5,__LC_EXT_PARAMS2-4096(%r1)  # get 64 bit external parameter
        brasl   %r14,do_extint
        j       io_return
 
index dd3c1994b8bd405c5fe986d99f5746893fa585a5..54b0995514e8721508d9c98cb801d3bf36195c0a 100644 (file)
@@ -234,9 +234,9 @@ int unregister_external_interrupt(u16 code, ext_int_handler_t handler)
 }
 EXPORT_SYMBOL(unregister_external_interrupt);
 
-void __irq_entry do_extint(struct pt_regs *regs, struct ext_code ext_code,
-                          unsigned int param32, unsigned long param64)
+void __irq_entry do_extint(struct pt_regs *regs)
 {
+       struct ext_code ext_code;
        struct pt_regs *old_regs;
        struct ext_int_info *p;
        int index;
@@ -248,6 +248,7 @@ void __irq_entry do_extint(struct pt_regs *regs, struct ext_code ext_code,
                clock_comparator_work();
        }
        kstat_incr_irqs_this_cpu(EXTERNAL_INTERRUPT, NULL);
+       ext_code = *(struct ext_code *) &regs->int_code;
        if (ext_code.code != 0x1004)
                __get_cpu_var(s390_idle).nohz_delay = 1;
 
@@ -255,7 +256,8 @@ void __irq_entry do_extint(struct pt_regs *regs, struct ext_code ext_code,
        rcu_read_lock();
        list_for_each_entry_rcu(p, &ext_int_hash[index], entry)
                if (likely(p->code == ext_code.code))
-                       p->handler(ext_code, param32, param64);
+                       p->handler(ext_code, regs->int_parm,
+                                  regs->int_parm_long);
        rcu_read_unlock();
        irq_exit();
        set_irq_regs(old_regs);
index 935d80b4e9cedb5361d6d18aab013c73f4eab456..4eeb4a6bf2074cd0f72a988ab6ee5f5ea23cb764 100644 (file)
@@ -568,7 +568,7 @@ out:
  */
 void __irq_entry do_IRQ(struct pt_regs *regs)
 {
-       struct tpi_info *tpi_info;
+       struct tpi_info *tpi_info = (struct tpi_info *) &regs->int_code;
        struct subchannel *sch;
        struct irb *irb;
        struct pt_regs *old_regs;
@@ -579,46 +579,34 @@ void __irq_entry do_IRQ(struct pt_regs *regs)
        if (S390_lowcore.int_clock >= S390_lowcore.clock_comparator)
                /* Serve timer interrupts first. */
                clock_comparator_work();
-       /*
-        * Get interrupt information from lowcore
-        */
-       tpi_info = (struct tpi_info *)&S390_lowcore.subchannel_id;
-       irb = (struct irb *)&S390_lowcore.irb;
-       do {
-               kstat_incr_irqs_this_cpu(IO_INTERRUPT, NULL);
-               if (tpi_info->adapter_IO) {
-                       do_adapter_IO(tpi_info->isc);
-                       continue;
-               }
-               sch = (struct subchannel *)(unsigned long)tpi_info->intparm;
-               if (!sch) {
-                       /* Clear pending interrupt condition. */
-                       inc_irq_stat(IRQIO_CIO);
-                       tsch(tpi_info->schid, irb);
-                       continue;
-               }
-               spin_lock(sch->lock);
-               /* Store interrupt response block to lowcore. */
-               if (tsch(tpi_info->schid, irb) == 0) {
-                       /* Keep subchannel information word up to date. */
-                       memcpy (&sch->schib.scsw, &irb->scsw,
-                               sizeof (irb->scsw));
-                       /* Call interrupt handler if there is one. */
-                       if (sch->driver && sch->driver->irq)
-                               sch->driver->irq(sch);
-                       else
-                               inc_irq_stat(IRQIO_CIO);
-               } else
+
+       kstat_incr_irqs_this_cpu(IO_INTERRUPT, NULL);
+       irb = (struct irb *) &S390_lowcore.irb;
+       if (tpi_info->adapter_IO) {
+               do_adapter_IO(tpi_info->isc);
+               goto out;
+       }
+       sch = (struct subchannel *)(unsigned long) tpi_info->intparm;
+       if (!sch) {
+               /* Clear pending interrupt condition. */
+               inc_irq_stat(IRQIO_CIO);
+               tsch(tpi_info->schid, irb);
+               goto out;
+       }
+       spin_lock(sch->lock);
+       /* Store interrupt response block to lowcore. */
+       if (tsch(tpi_info->schid, irb) == 0) {
+               /* Keep subchannel information word up to date. */
+               memcpy (&sch->schib.scsw, &irb->scsw, sizeof (irb->scsw));
+               /* Call interrupt handler if there is one. */
+               if (sch->driver && sch->driver->irq)
+                       sch->driver->irq(sch);
+               else
                        inc_irq_stat(IRQIO_CIO);
-               spin_unlock(sch->lock);
-               /*
-                * Are more interrupts pending?
-                * If so, the tpi instruction will update the lowcore
-                * to hold the info for the next interrupt.
-                * We don't do this for VM because a tpi drops the cpu
-                * out of the sie which costs more cycles than it saves.
-                */
-       } while (MACHINE_IS_LPAR && tpi(NULL) != 0);
+       } else
+               inc_irq_stat(IRQIO_CIO);
+       spin_unlock(sch->lock);
+out:
        irq_exit();
        set_irq_regs(old_regs);
 }