sh: Add support for 4K stacks.
Paul Mundt [Wed, 27 Sep 2006 09:22:14 +0000 (18:22 +0900)]
This enables support for 4K stacks on SH.

Currently this depends on DEBUG_KERNEL, but likely all boards
will switch to this as the default in the future.

Signed-off-by: Paul Mundt <lethal@linux-sh.org>

arch/sh/Kconfig.debug
arch/sh/kernel/cpu/irq/ipr.c
arch/sh/kernel/entry.S
arch/sh/kernel/head.S
arch/sh/kernel/irq.c
arch/sh/kernel/traps.c
arch/sh/kernel/vmlinux.lds.S
include/asm-sh/irq.h
include/asm-sh/thread_info.h

index f0188e6..48479e0 100644 (file)
@@ -46,6 +46,16 @@ config DEBUG_STACK_USAGE
 
          This option will slow down process creation somewhat.
 
+config 4KSTACKS
+       bool "Use 4Kb for kernel stacks instead of 8Kb"
+       depends on DEBUG_KERNEL
+       help
+         If you say Y here the kernel will use a 4Kb stacksize for the
+         kernel stack attached to each process/thread. This facilitates
+         running more threads on a system and also reduces the pressure
+         on the VM subsystem for higher order allocations. This option
+         will also use IRQ stacks to compensate for the reduced stackspace.
+
 config KGDB
        bool "Include KGDB kernel debugger"
        select FRAME_POINTER
index 4c4fd41..f785822 100644 (file)
@@ -190,6 +190,8 @@ void __init init_IRQ(void)
        /* Perform the machine specific initialisation */
        if (sh_mv.mv_init_irq != NULL)
                sh_mv.mv_init_irq();
+
+       irq_ctx_init(smp_processor_id());
 }
 
 #if !defined(CONFIG_CPU_HAS_PINT_IRQ)
index fd5fe23..fe82218 100644 (file)
@@ -716,8 +716,8 @@ ENTRY(handle_exception)
        bt/s    1f              ! It's a kernel to kernel transition.
         mov    r15, k0         ! save original stack to k0
        /* User space to kernel */
-       mov     #0x20, k1
-       shll8   k1              ! k1 := 8192 (== THREAD_SIZE)
+       mov     #(THREAD_SIZE >> 8), k1
+       shll8   k1              ! k1 := THREAD_SIZE
        add     current, k1
        mov     k1, r15         ! change to kernel stack
        !
index 3e7d00b..f5f53d1 100644 (file)
@@ -12,7 +12,6 @@
  */
 #include <linux/linkage.h>
 #include <asm/thread_info.h>
-#include <asm/page.h>
 
 #ifdef CONFIG_CPU_SH4A
 #define SYNCO()                synco
@@ -69,8 +68,8 @@ ENTRY(_stext)
        !
        mov.l   2f, r0
        mov     r0, r15         ! Set initial r15 (stack pointer)
-       mov     #0x20, r1       !
-       shll8   r1              ! r1 = 8192
+       mov     #(THREAD_SIZE >> 8), r1
+       shll8   r1              ! r1 = THREAD_SIZE
        sub     r1, r0          !
        ldc     r0, r7_bank     ! ... and initial thread_info
 
index 7066611..c7ebd6a 100644 (file)
@@ -1,5 +1,4 @@
-/* $Id: irq.c,v 1.20 2004/01/13 05:52:11 kkojima Exp $
- *
+/*
  * linux/arch/sh/kernel/irq.c
  *
  *     Copyright (C) 1992, 1998 Linus Torvalds, Ingo Molnar
@@ -7,13 +6,15 @@
  *
  * SuperH version:  Copyright (C) 1999  Niibe Yutaka
  */
-
 #include <linux/irq.h>
 #include <linux/interrupt.h>
+#include <linux/module.h>
 #include <linux/kernel_stat.h>
 #include <linux/seq_file.h>
 #include <asm/irq.h>
 #include <asm/processor.h>
+#include <asm/uaccess.h>
+#include <asm/thread_info.h>
 #include <asm/cpu/mmu_context.h>
 
 /*
@@ -60,11 +61,27 @@ unlock:
 }
 #endif
 
+#ifdef CONFIG_4KSTACKS
+/*
+ * per-CPU IRQ handling contexts (thread information and stack)
+ */
+union irq_ctx {
+       struct thread_info      tinfo;
+       u32                     stack[THREAD_SIZE/sizeof(u32)];
+};
+
+static union irq_ctx *hardirq_ctx[NR_CPUS];
+static union irq_ctx *softirq_ctx[NR_CPUS];
+#endif
+
 asmlinkage int do_IRQ(unsigned long r4, unsigned long r5,
                      unsigned long r6, unsigned long r7,
                      struct pt_regs regs)
 {
        int irq = r4;
+#ifdef CONFIG_4KSTACKS
+       union irq_ctx *curctx, *irqctx;
+#endif
 
        irq_enter();
 
@@ -102,7 +119,135 @@ asmlinkage int do_IRQ(unsigned long r4, unsigned long r5,
 #endif
 
        irq = irq_demux(irq);
-       __do_IRQ(irq, &regs);
+
+#ifdef CONFIG_4KSTACKS
+       curctx = (union irq_ctx *)current_thread_info();
+       irqctx = hardirq_ctx[smp_processor_id()];
+
+       /*
+        * this is where we switch to the IRQ stack. However, if we are
+        * already using the IRQ stack (because we interrupted a hardirq
+        * handler) we can't do that and just have to keep using the
+        * current stack (which is the irq stack already after all)
+        */
+       if (curctx != irqctx) {
+               u32 *isp;
+
+               isp = (u32 *)((char *)irqctx + sizeof(*irqctx));
+               irqctx->tinfo.task = curctx->tinfo.task;
+               irqctx->tinfo.previous_sp = current_stack_pointer;
+
+               __asm__ __volatile__ (
+                       "mov    %0, r4          \n"
+                       "mov    %1, r5          \n"
+                       "mov    r15, r9         \n"
+                       "jsr    @%2             \n"
+                       /* swith to the irq stack */
+                       " mov   %3, r15         \n"
+                       /* restore the stack (ring zero) */
+                       "mov    r9, r15         \n"
+                       : /* no outputs */
+                       : "r" (irq), "r" (&regs), "r" (__do_IRQ), "r" (isp)
+                       /* XXX: A somewhat excessive clobber list? -PFM */
+                       : "memory", "r0", "r1", "r2", "r3", "r4",
+                         "r5", "r6", "r7", "r8", "t", "pr"
+               );
+       } else
+#endif
+               __do_IRQ(irq, &regs);
+
        irq_exit();
+
        return 1;
 }
+
+#ifdef CONFIG_4KSTACKS
+/*
+ * These should really be __section__(".bss.page_aligned") as well, but
+ * gcc's 3.0 and earlier don't handle that correctly.
+ */
+static char softirq_stack[NR_CPUS * THREAD_SIZE]
+               __attribute__((__aligned__(THREAD_SIZE)));
+
+static char hardirq_stack[NR_CPUS * THREAD_SIZE]
+               __attribute__((__aligned__(THREAD_SIZE)));
+
+/*
+ * allocate per-cpu stacks for hardirq and for softirq processing
+ */
+void irq_ctx_init(int cpu)
+{
+       union irq_ctx *irqctx;
+
+       if (hardirq_ctx[cpu])
+               return;
+
+       irqctx = (union irq_ctx *)&hardirq_stack[cpu * THREAD_SIZE];
+       irqctx->tinfo.task              = NULL;
+       irqctx->tinfo.exec_domain       = NULL;
+       irqctx->tinfo.cpu               = cpu;
+       irqctx->tinfo.preempt_count     = HARDIRQ_OFFSET;
+       irqctx->tinfo.addr_limit        = MAKE_MM_SEG(0);
+
+       hardirq_ctx[cpu] = irqctx;
+
+       irqctx = (union irq_ctx *)&softirq_stack[cpu * THREAD_SIZE];
+       irqctx->tinfo.task              = NULL;
+       irqctx->tinfo.exec_domain       = NULL;
+       irqctx->tinfo.cpu               = cpu;
+       irqctx->tinfo.preempt_count     = SOFTIRQ_OFFSET;
+       irqctx->tinfo.addr_limit        = MAKE_MM_SEG(0);
+
+       softirq_ctx[cpu] = irqctx;
+
+       printk("CPU %u irqstacks, hard=%p soft=%p\n",
+               cpu, hardirq_ctx[cpu], softirq_ctx[cpu]);
+}
+
+void irq_ctx_exit(int cpu)
+{
+       hardirq_ctx[cpu] = NULL;
+}
+
+extern asmlinkage void __do_softirq(void);
+
+asmlinkage void do_softirq(void)
+{
+       unsigned long flags;
+       struct thread_info *curctx;
+       union irq_ctx *irqctx;
+       u32 *isp;
+
+       if (in_interrupt())
+               return;
+
+       local_irq_save(flags);
+
+       if (local_softirq_pending()) {
+               curctx = current_thread_info();
+               irqctx = softirq_ctx[smp_processor_id()];
+               irqctx->tinfo.task = curctx->task;
+               irqctx->tinfo.previous_sp = current_stack_pointer;
+
+               /* build the stack frame on the softirq stack */
+               isp = (u32 *)((char *)irqctx + sizeof(*irqctx));
+
+               __asm__ __volatile__ (
+                       "mov    r15, r9         \n"
+                       "jsr    @%0             \n"
+                       /* switch to the softirq stack */
+                       " mov   %1, r15         \n"
+                       /* restore the thread stack */
+                       "mov    r9, r15         \n"
+                       : /* no outputs */
+                       : "r" (__do_softirq), "r" (isp)
+                       /* XXX: A somewhat excessive clobber list? -PFM */
+                       : "memory", "r0", "r1", "r2", "r3", "r4",
+                         "r5", "r6", "r7", "r8", "r9", "r15", "t", "pr"
+               );
+       }
+
+       local_irq_restore(flags);
+}
+EXPORT_SYMBOL(do_softirq);
+#endif
index 95c810b..c2c597e 100644 (file)
@@ -741,20 +741,12 @@ void show_stack(struct task_struct *tsk, unsigned long *sp)
        unsigned long module_end = VMALLOC_END;
        int i = 1;
 
-       if (tsk && !sp) {
+       if (!tsk)
+               tsk = current;
+       if (tsk == current)
+               sp = (unsigned long *)current_stack_pointer;
+       else
                sp = (unsigned long *)tsk->thread.sp;
-       }
-
-       if (!sp) {
-               __asm__ __volatile__ (
-                       "mov r15, %0\n\t"
-                       "stc r7_bank, %1\n\t"
-                       : "=r" (module_start),
-                         "=r" (module_end)
-               );
-               
-               sp = (unsigned long *)module_start;
-       }
 
        stack = sp;
 
index 0220d8a..5eb9309 100644 (file)
@@ -3,7 +3,6 @@
  * Written by Niibe Yutaka
  */
 #include <asm/thread_info.h>
-#include <asm/page.h>
 #include <asm-generic/vmlinux.lds.h>
 
 #ifdef CONFIG_CPU_LITTLE_ENDIAN
index dd05e10..0e5f365 100644 (file)
@@ -719,6 +719,15 @@ static inline int generic_irq_demux(int irq)
 #define irq_canonicalize(irq)  (irq)
 #define irq_demux(irq)         __irq_demux(sh_mv.mv_irq_demux(irq))
 
+#ifdef CONFIG_4KSTACKS
+extern void irq_ctx_init(int cpu);
+extern void irq_ctx_exit(int cpu);
+# define __ARCH_HAS_DO_SOFTIRQ
+#else
+# define irq_ctx_init(cpu) do { } while (0)
+# define irq_ctx_exit(cpu) do { } while (0)
+#endif
+
 #if defined(CONFIG_CPU_SUBTYPE_SH73180)
 #include <asm/irq-sh73180.h>
 #endif
index 605259f..3ebc3f9 100644 (file)
@@ -9,8 +9,8 @@
  *  Copyright (C) 2002  David Howells (dhowells@redhat.com)
  *  - Incorporating suggestions made by Linus Torvalds and Dave Miller
  */
-
 #ifdef __KERNEL__
+#include <asm/page.h>
 
 #ifndef __ASSEMBLY__
 #include <asm/processor.h>
@@ -23,13 +23,20 @@ struct thread_info {
        int                     preempt_count; /* 0 => preemptable, <0 => BUG */
        mm_segment_t            addr_limit;     /* thread address space */
        struct restart_block    restart_block;
+       unsigned long           previous_sp;    /* sp of previous stack in case
+                                                  of nested IRQ stacks */
        __u8                    supervisor_stack[0];
 };
 
 #endif
 
 #define PREEMPT_ACTIVE         0x10000000
+
+#ifdef CONFIG_4KSTACKS
+#define THREAD_SIZE            (PAGE_SIZE)
+#else
 #define THREAD_SIZE            (PAGE_SIZE * 2)
+#endif
 #define STACK_WARN             (THREAD_SIZE / 8)
 
 /*
@@ -52,6 +59,9 @@ struct thread_info {
 #define init_thread_info       (init_thread_union.thread_info)
 #define init_stack             (init_thread_union.stack)
 
+/* how to get the current stack pointer from C */
+register unsigned long current_stack_pointer asm("r15") __attribute_used__;
+
 /* how to get the thread information struct from C */
 static inline struct thread_info *current_thread_info(void)
 {