]> err.no Git - linux-2.6/commitdiff
[MIPS] MT: Enable coexistence of AP/SP with VSMP and SMTC.
authorRalf Baechle <ralf@linux-mips.org>
Fri, 27 Jul 2007 18:31:10 +0000 (19:31 +0100)
committerRalf Baechle <ralf@linux-mips.org>
Tue, 31 Jul 2007 20:35:24 +0000 (21:35 +0100)
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
arch/mips/Kconfig
arch/mips/kernel/kspd.c
arch/mips/kernel/mips-mt.c
arch/mips/kernel/rtlx.c
arch/mips/kernel/smtc.c
arch/mips/kernel/vpe.c
include/asm-mips/mips_mt.h

index 0893e084150ea023b7fefecfaf8266c585c40642..3513e226837bcbaea16da5105bb4112de8184aa0 100644 (file)
@@ -1377,17 +1377,6 @@ config MIPS_MT_SMTC
          This is a kernel model which is known a SMTC or lately has been
          marketesed into SMVP.
 
-config MIPS_VPE_LOADER
-       bool "VPE loader support."
-       depends on SYS_SUPPORTS_MULTITHREADING
-       select CPU_MIPSR2_IRQ_VI
-       select CPU_MIPSR2_IRQ_EI
-       select CPU_MIPSR2_SRS
-       select MIPS_MT
-       help
-         Includes a loader for loading an elf relocatable object
-         onto another VPE and running it.
-
 endchoice
 
 config MIPS_MT
@@ -1398,8 +1387,19 @@ config SYS_SUPPORTS_MULTITHREADING
 
 config MIPS_MT_FPAFF
        bool "Dynamic FPU affinity for FP-intensive threads"
-       depends on MIPS_MT
        default y
+       depends on MIPS_MT_SMP || MIPS_MT_SMTC
+
+config MIPS_VPE_LOADER
+       bool "VPE loader support."
+       depends on SYS_SUPPORTS_MULTITHREADING
+       select CPU_MIPSR2_IRQ_VI
+       select CPU_MIPSR2_IRQ_EI
+       select CPU_MIPSR2_SRS
+       select MIPS_MT
+       help
+         Includes a loader for loading an elf relocatable object
+         onto another VPE and running it.
 
 config MIPS_MT_SMTC_INSTANT_REPLAY
        bool "Low-latency Dispatch of Deferred SMTC IPIs"
index c6580018c94b3b5a7bf690046bc58ee86a29ee41..cb9a14a1ca5bba1039953f536d7a3834198c789e 100644 (file)
@@ -89,7 +89,7 @@ static int sp_stopping = 0;
 #define MTSP_O_EXCL            0x0800
 #define MTSP_O_BINARY          0x8000
 
-#define SP_VPE 1
+extern int tclimit;
 
 struct apsp_table  {
        int sp;
@@ -225,8 +225,8 @@ void sp_work_handle_request(void)
        /* Run the syscall at the priviledge of the user who loaded the
           SP program */
 
-       if (vpe_getuid(SP_VPE))
-               sp_setfsuidgid( vpe_getuid(SP_VPE), vpe_getgid(SP_VPE));
+       if (vpe_getuid(tclimit))
+               sp_setfsuidgid(vpe_getuid(tclimit), vpe_getgid(tclimit));
 
        switch (sc.cmd) {
        /* needs the flags argument translating from SDE kit to
@@ -245,7 +245,7 @@ void sp_work_handle_request(void)
 
        case MTSP_SYSCALL_EXIT:
                list_for_each_entry(n, &kspd_notifylist, list)
-                       n->kspd_sp_exit(SP_VPE);
+                       n->kspd_sp_exit(tclimit);
                sp_stopping = 1;
 
                printk(KERN_DEBUG "KSPD got exit syscall from SP exitcode %d\n",
@@ -255,7 +255,7 @@ void sp_work_handle_request(void)
        case MTSP_SYSCALL_OPEN:
                generic.arg1 = translate_open_flags(generic.arg1);
 
-               vcwd = vpe_getcwd(SP_VPE);
+               vcwd = vpe_getcwd(tclimit);
 
                /* change to the cwd of the process that loaded the SP program */
                old_fs = get_fs();
@@ -283,7 +283,7 @@ void sp_work_handle_request(void)
                break;
        } /* switch */
 
-       if (vpe_getuid(SP_VPE))
+       if (vpe_getuid(tclimit))
                sp_setfsuidgid( 0, 0);
 
        old_fs = get_fs();
@@ -364,10 +364,9 @@ static void startwork(int vpe)
                }
 
                INIT_WORK(&work, sp_work);
-               queue_work(workqueue, &work);
-       } else
-               queue_work(workqueue, &work);
+       }
 
+       queue_work(workqueue, &work);
 }
 
 static void stopwork(int vpe)
@@ -389,7 +388,7 @@ static int kspd_module_init(void)
 
        notify.start = startwork;
        notify.stop = stopwork;
-       vpe_notify(SP_VPE, &notify);
+       vpe_notify(tclimit, &notify);
 
        return 0;
 }
index 1a7d89231299f23dc6aaffff47e8fdbe86b3a42e..7169a4db37b8d6f3ab0742ae2053ef047245b6f7 100644 (file)
 #include <asm/r4kcache.h>
 #include <asm/cacheflush.h>
 
+int vpelimit;
+
+static int __init maxvpes(char *str)
+{
+       get_option(&str, &vpelimit);
+
+       return 1;
+}
+
+__setup("maxvpes=", maxvpes);
+
+int tclimit;
+
+static int __init maxtcs(char *str)
+{
+       get_option(&str, &tclimit);
+
+       return 1;
+}
+
+__setup("maxtcs=", maxtcs);
+
 /*
  * Dump new MIPS MT state for the core. Does not leave TCs halted.
  * Takes an argument which taken to be a pre-call MVPControl value.
index 8cf24d716d418f56c1221de16154fa242724c4d8..5c040060560efe23f429cc54f2a36fbb37ee75ea 100644 (file)
 #include <asm/atomic.h>
 #include <asm/cpu.h>
 #include <asm/processor.h>
+#include <asm/mips_mt.h>
 #include <asm/system.h>
 #include <asm/vpe.h>
 #include <asm/rtlx.h>
 
-#define RTLX_TARG_VPE 1
-
 static struct rtlx_info *rtlx;
 static int major;
 static char module_name[] = "rtlx";
@@ -165,10 +164,10 @@ int rtlx_open(int index, int can_sleep)
        }
 
        if (rtlx == NULL) {
-               if( (p = vpe_get_shared(RTLX_TARG_VPE)) == NULL) {
+               if( (p = vpe_get_shared(tclimit)) == NULL) {
                        if (can_sleep) {
                                __wait_event_interruptible(channel_wqs[index].lx_queue,
-                                                          (p = vpe_get_shared(RTLX_TARG_VPE)),
+                                                          (p = vpe_get_shared(tclimit)),
                                                           ret);
                                if (ret)
                                        goto out_fail;
@@ -477,6 +476,19 @@ static int rtlx_module_init(void)
        struct device *dev;
        int i, err;
 
+       if (!cpu_has_mipsmt) {
+               printk("VPE loader: not a MIPS MT capable processor\n");
+               return -ENODEV;
+       }
+
+       if (tclimit == 0) {
+               printk(KERN_WARNING "No TCs reserved for AP/SP, not "
+                      "initializing RTLX.\nPass maxtcs=<n> argument as kernel "
+                      "argument\n");
+
+               return -ENODEV;
+       }
+
        major = register_chrdev(0, module_name, &rtlx_fops);
        if (major < 0) {
                printk(register_chrdev_failed);
@@ -501,7 +513,7 @@ static int rtlx_module_init(void)
        /* set up notifiers */
        notify.start = starting;
        notify.stop = stopping;
-       vpe_notify(RTLX_TARG_VPE, &notify);
+       vpe_notify(tclimit, &notify);
 
        if (cpu_has_vint)
                set_vi_handler(MIPS_CPU_RTLX_IRQ, rtlx_dispatch);
index f2c7aed663e7875a83885f5bcdf83e3d757c693e..6b3c3ea8bc61fbf7e885af3d9b5f9dbbe0fef825 100644 (file)
@@ -86,25 +86,11 @@ unsigned int smtc_status = 0;
 
 /* Boot command line configuration overrides */
 
-static int vpelimit = 0;
-static int tclimit = 0;
 static int ipibuffers = 0;
 static int nostlb = 0;
 static int asidmask = 0;
 unsigned long smtc_asid_mask = 0xff;
 
-static int __init maxvpes(char *str)
-{
-       get_option(&str, &vpelimit);
-       return 1;
-}
-
-static int __init maxtcs(char *str)
-{
-       get_option(&str, &tclimit);
-       return 1;
-}
-
 static int __init ipibufs(char *str)
 {
        get_option(&str, &ipibuffers);
@@ -137,8 +123,6 @@ static int __init asidmask_set(char *str)
        return 1;
 }
 
-__setup("maxvpes=", maxvpes);
-__setup("maxtcs=", maxtcs);
 __setup("ipibufs=", ipibufs);
 __setup("nostlb", stlb_disable);
 __setup("asidmask=", asidmask_set);
index a2bee10f04cf6ae3441b5b0f71e331383209cc1a..c726c47cd2c359f38080ecdb455348a603742ec7 100644 (file)
@@ -27,7 +27,6 @@
  * To load and run, simply cat a SP 'program file' to /dev/vpe1.
  * i.e cat spapp >/dev/vpe1.
  */
-
 #include <linux/kernel.h>
 #include <linux/device.h>
 #include <linux/module.h>
@@ -54,6 +53,7 @@
 #include <asm/system.h>
 #include <asm/vpe.h>
 #include <asm/kspd.h>
+#include <asm/mips_mt.h>
 
 typedef void *vpe_handle;
 
@@ -132,14 +132,9 @@ struct tc {
        enum tc_state state;
        int index;
 
-       /* parent VPE */
-       struct vpe *pvpe;
-
-       /* The list of TC's with this VPE */
-       struct list_head tc;
-
-       /* The global list of tc's */
-       struct list_head list;
+       struct vpe *pvpe;       /* parent VPE */
+       struct list_head tc;    /* The list of TC's with this VPE */
+       struct list_head list;  /* The global list of tc's */
 };
 
 struct {
@@ -217,18 +212,17 @@ struct vpe *alloc_vpe(int minor)
 /* allocate a tc. At startup only tc0 is running, all other can be halted. */
 struct tc *alloc_tc(int index)
 {
-       struct tc *t;
-
-       if ((t = kzalloc(sizeof(struct tc), GFP_KERNEL)) == NULL) {
-               return NULL;
-       }
+       struct tc *tc;
 
-       INIT_LIST_HEAD(&t->tc);
-       list_add_tail(&t->list, &vpecontrol.tc_list);
+       if ((tc = kzalloc(sizeof(struct tc), GFP_KERNEL)) == NULL)
+               goto out;
 
-       t->index = index;
+       INIT_LIST_HEAD(&tc->tc);
+       tc->index = index;
+       list_add_tail(&tc->list, &vpecontrol.tc_list);
 
-       return t;
+out:
+       return tc;
 }
 
 /* clean up and free everything */
@@ -663,66 +657,48 @@ static void dump_elfsymbols(Elf_Shdr * sechdrs, unsigned int symindex,
 }
 #endif
 
-static void dump_tc(struct tc *t)
-{
-       unsigned long val;
-
-       settc(t->index);
-       printk(KERN_DEBUG "VPE loader: TC index %d targtc %ld "
-              "TCStatus 0x%lx halt 0x%lx\n",
-              t->index, read_c0_vpecontrol() & VPECONTROL_TARGTC,
-              read_tc_c0_tcstatus(), read_tc_c0_tchalt());
-
-       printk(KERN_DEBUG " tcrestart 0x%lx\n", read_tc_c0_tcrestart());
-       printk(KERN_DEBUG " tcbind 0x%lx\n", read_tc_c0_tcbind());
-
-       val = read_c0_vpeconf0();
-       printk(KERN_DEBUG " VPEConf0 0x%lx MVP %ld\n", val,
-              (val & VPECONF0_MVP) >> VPECONF0_MVP_SHIFT);
-
-       printk(KERN_DEBUG " c0 status 0x%lx\n", read_vpe_c0_status());
-       printk(KERN_DEBUG " c0 cause 0x%lx\n", read_vpe_c0_cause());
-
-       printk(KERN_DEBUG " c0 badvaddr 0x%lx\n", read_vpe_c0_badvaddr());
-       printk(KERN_DEBUG " c0 epc 0x%lx\n", read_vpe_c0_epc());
-}
-
-static void dump_tclist(void)
-{
-       struct tc *t;
-
-       list_for_each_entry(t, &vpecontrol.tc_list, list) {
-               dump_tc(t);
-       }
-}
-
 /* We are prepared so configure and start the VPE... */
 static int vpe_run(struct vpe * v)
 {
+       unsigned long flags, val, dmt_flag;
        struct vpe_notifications *n;
-       unsigned long val, dmt_flag;
+       unsigned int vpeflags;
        struct tc *t;
 
        /* check we are the Master VPE */
+       local_irq_save(flags);
        val = read_c0_vpeconf0();
        if (!(val & VPECONF0_MVP)) {
                printk(KERN_WARNING
                       "VPE loader: only Master VPE's are allowed to configure MT\n");
+               local_irq_restore(flags);
+
                return -1;
        }
 
-       /* disable MT (using dvpe) */
-       dvpe();
+       dmt_flag = dmt();
+       vpeflags = dvpe();
 
        if (!list_empty(&v->tc)) {
                if ((t = list_entry(v->tc.next, struct tc, tc)) == NULL) {
-                       printk(KERN_WARNING "VPE loader: TC %d is already in use.\n",
-                              t->index);
+                       evpe(vpeflags);
+                       emt(dmt_flag);
+                       local_irq_restore(flags);
+
+                       printk(KERN_WARNING
+                              "VPE loader: TC %d is already in use.\n",
+                               t->index);
                        return -ENOEXEC;
                }
        } else {
-               printk(KERN_WARNING "VPE loader: No TC's associated with VPE %d\n",
+               evpe(vpeflags);
+               emt(dmt_flag);
+               local_irq_restore(flags);
+
+               printk(KERN_WARNING
+                      "VPE loader: No TC's associated with VPE %d\n",
                       v->minor);
+
                return -ENOEXEC;
        }
 
@@ -733,21 +709,20 @@ static int vpe_run(struct vpe * v)
 
        /* should check it is halted, and not activated */
        if ((read_tc_c0_tcstatus() & TCSTATUS_A) || !(read_tc_c0_tchalt() & TCHALT_H)) {
-               printk(KERN_WARNING "VPE loader: TC %d is already doing something!\n",
+               evpe(vpeflags);
+               emt(dmt_flag);
+               local_irq_restore(flags);
+
+               printk(KERN_WARNING "VPE loader: TC %d is already active!\n",
                       t->index);
-               dump_tclist();
+
                return -ENOEXEC;
        }
 
-       /*
-        * Disable multi-threaded execution whilst we activate, clear the
-        * halt bit and bound the tc to the other VPE...
-        */
-       dmt_flag = dmt();
-
        /* Write the address we want it to start running from in the TCPC register. */
        write_tc_c0_tcrestart((unsigned long)v->__start);
        write_tc_c0_tccontext((unsigned long)0);
+
        /*
         * Mark the TC as activated, not interrupt exempt and not dynamically
         * allocatable
@@ -763,15 +738,14 @@ static int vpe_run(struct vpe * v)
         * here...  Or set $a3 to zero and define DFLT_STACK_SIZE and
         * DFLT_HEAP_SIZE when you compile your program
         */
-       mttgpr(7, physical_memsize);
-
+       mttgpr(7, physical_memsize);
 
        /* set up VPE1 */
        /*
         * bind the TC to VPE 1 as late as possible so we only have the final
         * VPE registers to set up, and so an EJTAG probe can trigger on it
         */
-       write_tc_c0_tcbind((read_tc_c0_tcbind() & ~TCBIND_CURVPE) | v->minor);
+       write_tc_c0_tcbind((read_tc_c0_tcbind() & ~TCBIND_CURVPE) | 1);
 
        write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() & ~(VPECONF0_VPA));
 
@@ -793,15 +767,16 @@ static int vpe_run(struct vpe * v)
        /* take system out of configuration state */
        clear_c0_mvpcontrol(MVPCONTROL_VPC);
 
-       /* now safe to re-enable multi-threading */
-       emt(dmt_flag);
-
-       /* set it running */
+#ifdef CONFIG_SMP
        evpe(EVPE_ENABLE);
+#else
+       evpe(vpeflags);
+#endif
+       emt(dmt_flag);
+       local_irq_restore(flags);
 
-       list_for_each_entry(n, &v->notify, list) {
-               n->start(v->minor);
-       }
+       list_for_each_entry(n, &v->notify, list)
+               n->start(minor);
 
        return 0;
 }
@@ -1023,23 +998,15 @@ static int vpe_elfload(struct vpe * v)
        return 0;
 }
 
-void __used dump_vpe(struct vpe * v)
-{
-       struct tc *t;
-
-       settc(v->minor);
-
-       printk(KERN_DEBUG "VPEControl 0x%lx\n", read_vpe_c0_vpecontrol());
-       printk(KERN_DEBUG "VPEConf0 0x%lx\n", read_vpe_c0_vpeconf0());
-
-       list_for_each_entry(t, &vpecontrol.tc_list, list)
-               dump_tc(t);
-}
-
 static void cleanup_tc(struct tc *tc)
 {
+       unsigned long flags;
+       unsigned int mtflags, vpflags;
        int tmp;
 
+       local_irq_save(flags);
+       mtflags = dmt();
+       vpflags = dvpe();
        /* Put MVPE's into 'configuration state' */
        set_c0_mvpcontrol(MVPCONTROL_VPC);
 
@@ -1054,9 +1021,12 @@ static void cleanup_tc(struct tc *tc)
        write_tc_c0_tchalt(TCHALT_H);
 
        /* bind it to anything other than VPE1 */
-       write_tc_c0_tcbind(read_tc_c0_tcbind() & ~TCBIND_CURVPE); // | TCBIND_CURVPE
+//     write_tc_c0_tcbind(read_tc_c0_tcbind() & ~TCBIND_CURVPE); // | TCBIND_CURVPE
 
        clear_c0_mvpcontrol(MVPCONTROL_VPC);
+       evpe(vpflags);
+       emt(mtflags);
+       local_irq_restore(flags);
 }
 
 static int getcwd(char *buff, int size)
@@ -1077,36 +1047,32 @@ static int getcwd(char *buff, int size)
 /* checks VPE is unused and gets ready to load program  */
 static int vpe_open(struct inode *inode, struct file *filp)
 {
-       int minor, ret;
        enum vpe_state state;
-       struct vpe *v;
        struct vpe_notifications *not;
+       struct vpe *v;
+       int ret;
 
-       /* assume only 1 device at the mo. */
-       if ((minor = iminor(inode)) != 1) {
+       if (minor != iminor(inode)) {
+               /* assume only 1 device at the moment. */
                printk(KERN_WARNING "VPE loader: only vpe1 is supported\n");
                return -ENODEV;
        }
 
-       if ((v = get_vpe(minor)) == NULL) {
+       if ((v = get_vpe(tclimit)) == NULL) {
                printk(KERN_WARNING "VPE loader: unable to get vpe\n");
                return -ENODEV;
        }
 
        state = xchg(&v->state, VPE_STATE_INUSE);
        if (state != VPE_STATE_UNUSED) {
-               dvpe();
-
                printk(KERN_DEBUG "VPE loader: tc in use dumping regs\n");
 
-               dump_tc(get_tc(minor));
-
                list_for_each_entry(not, &v->notify, list) {
-                       not->stop(minor);
+                       not->stop(tclimit);
                }
 
                release_progmem(v->load_addr);
-               cleanup_tc(get_tc(minor));
+               cleanup_tc(get_tc(tclimit));
        }
 
        /* this of-course trashes what was there before... */
@@ -1133,26 +1099,25 @@ static int vpe_open(struct inode *inode, struct file *filp)
 
        v->shared_ptr = NULL;
        v->__start = 0;
+
        return 0;
 }
 
 static int vpe_release(struct inode *inode, struct file *filp)
 {
-       int minor, ret = 0;
        struct vpe *v;
        Elf_Ehdr *hdr;
+       int ret = 0;
 
-       minor = iminor(inode);
-       if ((v = get_vpe(minor)) == NULL)
+       v = get_vpe(tclimit);
+       if (v == NULL)
                return -ENODEV;
 
-       // simple case of fire and forget, so tell the VPE to run...
-
        hdr = (Elf_Ehdr *) v->pbuffer;
        if (memcmp(hdr->e_ident, ELFMAG, 4) == 0) {
-               if (vpe_elfload(v) >= 0)
+               if (vpe_elfload(v) >= 0) {
                        vpe_run(v);
-               else {
+               else {
                        printk(KERN_WARNING "VPE loader: ELF load failed.\n");
                        ret = -ENOEXEC;
                }
@@ -1179,12 +1144,14 @@ static int vpe_release(struct inode *inode, struct file *filp)
 static ssize_t vpe_write(struct file *file, const char __user * buffer,
                         size_t count, loff_t * ppos)
 {
-       int minor;
        size_t ret = count;
        struct vpe *v;
 
-       minor = iminor(file->f_path.dentry->d_inode);
-       if ((v = get_vpe(minor)) == NULL)
+       if (iminor(file->f_path.dentry->d_inode) != minor)
+               return -ENODEV;
+
+       v = get_vpe(tclimit);
+       if (v == NULL)
                return -ENODEV;
 
        if (v->pbuffer == NULL) {
@@ -1370,17 +1337,34 @@ static struct device *vpe_dev;
 
 static int __init vpe_module_init(void)
 {
+       unsigned int mtflags, vpflags;
+       int hw_tcs, hw_vpes, tc, err = 0;
+       unsigned long flags, val;
        struct vpe *v = NULL;
        struct device *dev;
        struct tc *t;
-       unsigned long val;
-       int i, err;
 
        if (!cpu_has_mipsmt) {
                printk("VPE loader: not a MIPS MT capable processor\n");
                return -ENODEV;
        }
 
+       if (vpelimit == 0) {
+               printk(KERN_WARNING "No VPEs reserved for AP/SP, not "
+                      "initializing VPE loader.\nPass maxvpes=<n> argument as "
+                      "kernel argument\n");
+
+               return -ENODEV;
+       }
+
+       if (tclimit == 0) {
+               printk(KERN_WARNING "No TCs reserved for AP/SP, not "
+                      "initializing VPE loader.\nPass maxtcs=<n> argument as "
+                      "kernel argument\n");
+
+               return -ENODEV;
+       }
+
        major = register_chrdev(0, module_name, &vpe_fops);
        if (major < 0) {
                printk("VPE loader: unable to register character device\n");
@@ -1388,40 +1372,61 @@ static int __init vpe_module_init(void)
        }
 
        dev = device_create(mt_class, NULL, MKDEV(major, minor),
-                           "tc%d", minor);
+                           "vpe%d", minor);
        if (IS_ERR(dev)) {
                err = PTR_ERR(dev);
                goto out_chrdev;
        }
        vpe_dev = dev;
 
-       dmt();
-       dvpe();
+       local_irq_save(flags);
+       mtflags = dmt();
+       vpflags = dvpe();
 
        /* Put MVPE's into 'configuration state' */
        set_c0_mvpcontrol(MVPCONTROL_VPC);
 
        /* dump_mtregs(); */
 
-
        val = read_c0_mvpconf0();
-       for (i = 0; i < ((val & MVPCONF0_PTC) + 1); i++) {
-               t = alloc_tc(i);
+       hw_tcs = (val & MVPCONF0_PTC) + 1;
+       hw_vpes = ((val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT) + 1;
+
+       for (tc = tclimit; tc < hw_tcs; tc++) {
+               /*
+                * Must re-enable multithreading temporarily or in case we
+                * reschedule send IPIs or similar we might hang.
+                */
+               clear_c0_mvpcontrol(MVPCONTROL_VPC);
+               evpe(vpflags);
+               emt(mtflags);
+               local_irq_restore(flags);
+               t = alloc_tc(tc);
+               if (!t) {
+                       err = -ENOMEM;
+                       goto out;
+               }
+
+               local_irq_save(flags);
+               mtflags = dmt();
+               vpflags = dvpe();
+               set_c0_mvpcontrol(MVPCONTROL_VPC);
 
                /* VPE's */
-               if (i < ((val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT) + 1) {
-                       settc(i);
+               if (tc < hw_tcs) {
+                       settc(tc);
 
-                       if ((v = alloc_vpe(i)) == NULL) {
+                       if ((v = alloc_vpe(tc)) == NULL) {
                                printk(KERN_WARNING "VPE: unable to allocate VPE\n");
-                               return -ENODEV;
+
+                               goto out_reenable;
                        }
 
                        /* add the tc to the list of this vpe's tc's. */
                        list_add(&t->tc, &v->tc);
 
                        /* deactivate all but vpe0 */
-                       if (i != 0) {
+                       if (tc >= tclimit) {
                                unsigned long tmp = read_vpe_c0_vpeconf0();
 
                                tmp &= ~VPECONF0_VPA;
@@ -1434,7 +1439,7 @@ static int __init vpe_module_init(void)
                        /* disable multi-threading with TC's */
                        write_vpe_c0_vpecontrol(read_vpe_c0_vpecontrol() & ~VPECONTROL_TE);
 
-                       if (i != 0) {
+                       if (tc >= vpelimit) {
                                /*
                                 * Set config to be the same as vpe0,
                                 * particularly kseg0 coherency alg
@@ -1446,10 +1451,10 @@ static int __init vpe_module_init(void)
                /* TC's */
                t->pvpe = v;    /* set the parent vpe */
 
-               if (i != 0) {
+               if (tc >= tclimit) {
                        unsigned long tmp;
 
-                       settc(i);
+                       settc(tc);
 
                        /* Any TC that is bound to VPE0 gets left as is - in case
                           we are running SMTC on VPE0. A TC that is bound to any
@@ -1479,9 +1484,14 @@ static int __init vpe_module_init(void)
                }
        }
 
+out_reenable:
        /* release config state */
        clear_c0_mvpcontrol(MVPCONTROL_VPC);
 
+       evpe(vpflags);
+       emt(mtflags);
+       local_irq_restore(flags);
+
 #ifdef CONFIG_MIPS_APSP_KSPD
        kspd_events.kspd_sp_exit = kspd_sp_exit;
 #endif
@@ -1490,6 +1500,7 @@ static int __init vpe_module_init(void)
 out_chrdev:
        unregister_chrdev(major, module_name);
 
+out:
        return err;
 }
 
index 8045abc78d0fc2a6eca35ba721f1e4e923307d36..ac7935203f8993e272f96d54de6d78c5a574f221 100644 (file)
@@ -8,6 +8,12 @@
 
 #include <linux/cpumask.h>
 
+/*
+ * How many VPEs and TCs is Linux allowed to use?  0 means no limit.
+ */
+extern int tclimit;
+extern int vpelimit;
+
 extern cpumask_t mt_fpu_cpumask;
 extern unsigned long mt_fpemul_threshold;