vserver 1.9.3
[linux-2.6.git] / arch / i386 / kernel / process.c
index 75b2009..0095fa1 100644 (file)
@@ -142,13 +142,21 @@ void cpu_idle (void)
        /* endless idle loop with no priority at all */
        while (1) {
                while (!need_resched()) {
-                       void (*idle)(void) = pm_idle;
+                       void (*idle)(void);
+                       /*
+                        * Mark this as an RCU critical section so that
+                        * synchronize_kernel() in the unload path waits
+                        * for our completion.
+                        */
+                       rcu_read_lock();
+                       idle = pm_idle;
 
                        if (!idle)
                                idle = default_idle;
 
                        irq_stat[smp_processor_id()].idle_timestamp = jiffies;
                        idle();
+                       rcu_read_unlock();
                }
                schedule();
        }
@@ -183,18 +191,13 @@ void __init select_idle_routine(const struct cpuinfo_x86 *c)
                printk("monitor/mwait feature present.\n");
                /*
                 * Skip, if setup has overridden idle.
-                * Also, take care of system with asymmetric CPUs.
-                * Use, mwait_idle only if all cpus support it.
-                * If not, we fallback to default_idle()
+                * One CPU supports mwait => All CPUs supports mwait
                 */
                if (!pm_idle) {
                        printk("using mwait in idle threads.\n");
                        pm_idle = mwait_idle;
                }
-               return;
        }
-       pm_idle = default_idle;
-       return;
 }
 
 static int __init idle_setup (char *str)
@@ -294,13 +297,22 @@ int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
 void exit_thread(void)
 {
        struct task_struct *tsk = current;
+       struct thread_struct *t = &tsk->thread;
 
        /* The process may have allocated an io port bitmap... nuke it. */
-       if (unlikely(NULL != tsk->thread.io_bitmap_ptr)) {
+       if (unlikely(NULL != t->io_bitmap_ptr)) {
                int cpu = get_cpu();
-               struct tss_struct *tss = init_tss + cpu;
-               kfree(tsk->thread.io_bitmap_ptr);
-               tsk->thread.io_bitmap_ptr = NULL;
+               struct tss_struct *tss = &per_cpu(init_tss, cpu);
+
+               kfree(t->io_bitmap_ptr);
+               t->io_bitmap_ptr = NULL;
+               /*
+                * Careful, clear this in the TSS too:
+                */
+               memset(tss->io_bitmap, 0xff, tss->io_bitmap_max);
+               t->io_bitmap_max = 0;
+               tss->io_bitmap_owner = NULL;
+               tss->io_bitmap_max = 0;
                tss->io_bitmap_base = INVALID_IO_BITMAP_OFFSET;
                put_cpu();
        }
@@ -369,8 +381,10 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long esp,
        tsk = current;
        if (unlikely(NULL != tsk->thread.io_bitmap_ptr)) {
                p->thread.io_bitmap_ptr = kmalloc(IO_BITMAP_BYTES, GFP_KERNEL);
-               if (!p->thread.io_bitmap_ptr)
+               if (!p->thread.io_bitmap_ptr) {
+                       p->thread.io_bitmap_max = 0;
                        return -ENOMEM;
+               }
                memcpy(p->thread.io_bitmap_ptr, tsk->thread.io_bitmap_ptr,
                        IO_BITMAP_BYTES);
        }
@@ -401,8 +415,10 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long esp,
 
        err = 0;
  out:
-       if (err && p->thread.io_bitmap_ptr)
+       if (err && p->thread.io_bitmap_ptr) {
                kfree(p->thread.io_bitmap_ptr);
+               p->thread.io_bitmap_max = 0;
+       }
        return err;
 }
 
@@ -467,6 +483,37 @@ int dump_task_regs(struct task_struct *tsk, elf_gregset_t *regs)
        return 1;
 }
 
+static inline void
+handle_io_bitmap(struct thread_struct *next, struct tss_struct *tss)
+{
+       if (!next->io_bitmap_ptr) {
+               /*
+                * Disable the bitmap via an invalid offset. We still cache
+                * the previous bitmap owner and the IO bitmap contents:
+                */
+               tss->io_bitmap_base = INVALID_IO_BITMAP_OFFSET;
+               return;
+       }
+       if (likely(next == tss->io_bitmap_owner)) {
+               /*
+                * Previous owner of the bitmap (hence the bitmap content)
+                * matches the next task, we dont have to do anything but
+                * to set a valid offset in the TSS:
+                */
+               tss->io_bitmap_base = IO_BITMAP_OFFSET;
+               return;
+       }
+       /*
+        * Lazy TSS's I/O bitmap copy. We set an invalid offset here
+        * and we let the task to get a GPF in case an I/O instruction
+        * is performed.  The handler of the GPF will verify that the
+        * faulting task has a valid I/O bitmap and, it true, does the
+        * real copy and restart the instruction.  This will save us
+        * redundant copies when the currently switched task does not
+        * perform any I/O during its timeslice.
+        */
+       tss->io_bitmap_base = INVALID_IO_BITMAP_OFFSET_LAZY;
+}
 /*
  * This special macro can be used to load a debugging register
  */
@@ -507,7 +554,7 @@ struct task_struct fastcall * __switch_to(struct task_struct *prev_p, struct tas
        struct thread_struct *prev = &prev_p->thread,
                                 *next = &next_p->thread;
        int cpu = smp_processor_id();
-       struct tss_struct *tss = init_tss + cpu;
+       struct tss_struct *tss = &per_cpu(init_tss, cpu);
 
        /* never put a printk in __switch_to... printk() calls wake_up*() indirectly */
 
@@ -551,28 +598,9 @@ struct task_struct fastcall * __switch_to(struct task_struct *prev_p, struct tas
                loaddebug(next, 7);
        }
 
-       if (unlikely(prev->io_bitmap_ptr || next->io_bitmap_ptr)) {
-               if (next->io_bitmap_ptr) {
-                       /*
-                        * 4 cachelines copy ... not good, but not that
-                        * bad either. Anyone got something better?
-                        * This only affects processes which use ioperm().
-                        * [Putting the TSSs into 4k-tlb mapped regions
-                        * and playing VM tricks to switch the IO bitmap
-                        * is not really acceptable.]
-                        */
-                       memcpy(tss->io_bitmap, next->io_bitmap_ptr,
-                               IO_BITMAP_BYTES);
-                       tss->io_bitmap_base = IO_BITMAP_OFFSET;
-               } else
-                       /*
-                        * a bitmap offset pointing outside of the TSS limit
-                        * causes a nicely controllable SIGSEGV if a process
-                        * tries to use a port IO instruction. The first
-                        * sys_ioperm() call sets up the bitmap properly.
-                        */
-                       tss->io_bitmap_base = INVALID_IO_BITMAP_OFFSET;
-       }
+       if (unlikely(prev->io_bitmap_ptr || next->io_bitmap_ptr))
+               handle_io_bitmap(next, tss);
+
        return prev_p;
 }
 
@@ -593,7 +621,7 @@ asmlinkage int sys_clone(struct pt_regs regs)
        child_tidptr = (int __user *)regs.edi;
        if (!newsp)
                newsp = regs.esp;
-       return do_fork(clone_flags & ~CLONE_IDLETASK, newsp, &regs, 0, parent_tidptr, child_tidptr);
+       return do_fork(clone_flags, newsp, &regs, 0, parent_tidptr, child_tidptr);
 }
 
 /*