diff options
Diffstat (limited to 'arch/i386/kernel/process.c')
-rw-r--r-- | arch/i386/kernel/process.c | 99 |
1 files changed, 83 insertions, 16 deletions
diff --git a/arch/i386/kernel/process.c b/arch/i386/kernel/process.c index c641056233a6..7845d480c293 100644 --- a/arch/i386/kernel/process.c +++ b/arch/i386/kernel/process.c | |||
@@ -48,6 +48,7 @@ | |||
48 | #include <asm/i387.h> | 48 | #include <asm/i387.h> |
49 | #include <asm/desc.h> | 49 | #include <asm/desc.h> |
50 | #include <asm/vm86.h> | 50 | #include <asm/vm86.h> |
51 | #include <asm/idle.h> | ||
51 | #ifdef CONFIG_MATH_EMULATION | 52 | #ifdef CONFIG_MATH_EMULATION |
52 | #include <asm/math_emu.h> | 53 | #include <asm/math_emu.h> |
53 | #endif | 54 | #endif |
@@ -80,6 +81,42 @@ void (*pm_idle)(void); | |||
80 | EXPORT_SYMBOL(pm_idle); | 81 | EXPORT_SYMBOL(pm_idle); |
81 | static DEFINE_PER_CPU(unsigned int, cpu_idle_state); | 82 | static DEFINE_PER_CPU(unsigned int, cpu_idle_state); |
82 | 83 | ||
84 | static ATOMIC_NOTIFIER_HEAD(idle_notifier); | ||
85 | |||
86 | void idle_notifier_register(struct notifier_block *n) | ||
87 | { | ||
88 | atomic_notifier_chain_register(&idle_notifier, n); | ||
89 | } | ||
90 | |||
91 | void idle_notifier_unregister(struct notifier_block *n) | ||
92 | { | ||
93 | atomic_notifier_chain_unregister(&idle_notifier, n); | ||
94 | } | ||
95 | |||
96 | static DEFINE_PER_CPU(volatile unsigned long, idle_state); | ||
97 | |||
98 | void enter_idle(void) | ||
99 | { | ||
100 | /* needs to be atomic w.r.t. interrupts, not against other CPUs */ | ||
101 | __set_bit(0, &__get_cpu_var(idle_state)); | ||
102 | atomic_notifier_call_chain(&idle_notifier, IDLE_START, NULL); | ||
103 | } | ||
104 | |||
105 | static void __exit_idle(void) | ||
106 | { | ||
107 | /* needs to be atomic w.r.t. interrupts, not against other CPUs */ | ||
108 | if (__test_and_clear_bit(0, &__get_cpu_var(idle_state)) == 0) | ||
109 | return; | ||
110 | atomic_notifier_call_chain(&idle_notifier, IDLE_END, NULL); | ||
111 | } | ||
112 | |||
113 | void exit_idle(void) | ||
114 | { | ||
115 | if (current->pid) | ||
116 | return; | ||
117 | __exit_idle(); | ||
118 | } | ||
119 | |||
83 | void disable_hlt(void) | 120 | void disable_hlt(void) |
84 | { | 121 | { |
85 | hlt_counter++; | 122 | hlt_counter++; |
@@ -130,6 +167,7 @@ EXPORT_SYMBOL(default_idle); | |||
130 | */ | 167 | */ |
131 | static void poll_idle (void) | 168 | static void poll_idle (void) |
132 | { | 169 | { |
170 | local_irq_enable(); | ||
133 | cpu_relax(); | 171 | cpu_relax(); |
134 | } | 172 | } |
135 | 173 | ||
@@ -189,7 +227,16 @@ void cpu_idle(void) | |||
189 | play_dead(); | 227 | play_dead(); |
190 | 228 | ||
191 | __get_cpu_var(irq_stat).idle_timestamp = jiffies; | 229 | __get_cpu_var(irq_stat).idle_timestamp = jiffies; |
230 | |||
231 | /* | ||
232 | * Idle routines should keep interrupts disabled | ||
233 | * from here on, until they go to idle. | ||
234 | * Otherwise, idle callbacks can misfire. | ||
235 | */ | ||
236 | local_irq_disable(); | ||
237 | enter_idle(); | ||
192 | idle(); | 238 | idle(); |
239 | __exit_idle(); | ||
193 | } | 240 | } |
194 | preempt_enable_no_resched(); | 241 | preempt_enable_no_resched(); |
195 | schedule(); | 242 | schedule(); |
@@ -243,7 +290,11 @@ void mwait_idle_with_hints(unsigned long eax, unsigned long ecx) | |||
243 | __monitor((void *)¤t_thread_info()->flags, 0, 0); | 290 | __monitor((void *)¤t_thread_info()->flags, 0, 0); |
244 | smp_mb(); | 291 | smp_mb(); |
245 | if (!need_resched()) | 292 | if (!need_resched()) |
246 | __mwait(eax, ecx); | 293 | __sti_mwait(eax, ecx); |
294 | else | ||
295 | local_irq_enable(); | ||
296 | } else { | ||
297 | local_irq_enable(); | ||
247 | } | 298 | } |
248 | } | 299 | } |
249 | 300 | ||
@@ -308,8 +359,8 @@ void show_regs(struct pt_regs * regs) | |||
308 | regs->eax,regs->ebx,regs->ecx,regs->edx); | 359 | regs->eax,regs->ebx,regs->ecx,regs->edx); |
309 | printk("ESI: %08lx EDI: %08lx EBP: %08lx", | 360 | printk("ESI: %08lx EDI: %08lx EBP: %08lx", |
310 | regs->esi, regs->edi, regs->ebp); | 361 | regs->esi, regs->edi, regs->ebp); |
311 | printk(" DS: %04x ES: %04x GS: %04x\n", | 362 | printk(" DS: %04x ES: %04x FS: %04x\n", |
312 | 0xffff & regs->xds,0xffff & regs->xes, 0xffff & regs->xgs); | 363 | 0xffff & regs->xds,0xffff & regs->xes, 0xffff & regs->xfs); |
313 | 364 | ||
314 | cr0 = read_cr0(); | 365 | cr0 = read_cr0(); |
315 | cr2 = read_cr2(); | 366 | cr2 = read_cr2(); |
@@ -340,7 +391,7 @@ int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) | |||
340 | 391 | ||
341 | regs.xds = __USER_DS; | 392 | regs.xds = __USER_DS; |
342 | regs.xes = __USER_DS; | 393 | regs.xes = __USER_DS; |
343 | regs.xgs = __KERNEL_PDA; | 394 | regs.xfs = __KERNEL_PDA; |
344 | regs.orig_eax = -1; | 395 | regs.orig_eax = -1; |
345 | regs.eip = (unsigned long) kernel_thread_helper; | 396 | regs.eip = (unsigned long) kernel_thread_helper; |
346 | regs.xcs = __KERNEL_CS | get_kernel_rpl(); | 397 | regs.xcs = __KERNEL_CS | get_kernel_rpl(); |
@@ -425,7 +476,7 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long esp, | |||
425 | 476 | ||
426 | p->thread.eip = (unsigned long) ret_from_fork; | 477 | p->thread.eip = (unsigned long) ret_from_fork; |
427 | 478 | ||
428 | savesegment(fs,p->thread.fs); | 479 | savesegment(gs,p->thread.gs); |
429 | 480 | ||
430 | tsk = current; | 481 | tsk = current; |
431 | if (unlikely(test_tsk_thread_flag(tsk, TIF_IO_BITMAP))) { | 482 | if (unlikely(test_tsk_thread_flag(tsk, TIF_IO_BITMAP))) { |
@@ -501,8 +552,8 @@ void dump_thread(struct pt_regs * regs, struct user * dump) | |||
501 | dump->regs.eax = regs->eax; | 552 | dump->regs.eax = regs->eax; |
502 | dump->regs.ds = regs->xds; | 553 | dump->regs.ds = regs->xds; |
503 | dump->regs.es = regs->xes; | 554 | dump->regs.es = regs->xes; |
504 | savesegment(fs,dump->regs.fs); | 555 | dump->regs.fs = regs->xfs; |
505 | dump->regs.gs = regs->xgs; | 556 | savesegment(gs,dump->regs.gs); |
506 | dump->regs.orig_eax = regs->orig_eax; | 557 | dump->regs.orig_eax = regs->orig_eax; |
507 | dump->regs.eip = regs->eip; | 558 | dump->regs.eip = regs->eip; |
508 | dump->regs.cs = regs->xcs; | 559 | dump->regs.cs = regs->xcs; |
@@ -653,7 +704,7 @@ struct task_struct fastcall * __switch_to(struct task_struct *prev_p, struct tas | |||
653 | load_esp0(tss, next); | 704 | load_esp0(tss, next); |
654 | 705 | ||
655 | /* | 706 | /* |
656 | * Save away %fs. No need to save %gs, as it was saved on the | 707 | * Save away %gs. No need to save %fs, as it was saved on the |
657 | * stack on entry. No need to save %es and %ds, as those are | 708 | * stack on entry. No need to save %es and %ds, as those are |
658 | * always kernel segments while inside the kernel. Doing this | 709 | * always kernel segments while inside the kernel. Doing this |
659 | * before setting the new TLS descriptors avoids the situation | 710 | * before setting the new TLS descriptors avoids the situation |
@@ -662,7 +713,7 @@ struct task_struct fastcall * __switch_to(struct task_struct *prev_p, struct tas | |||
662 | * used %fs or %gs (it does not today), or if the kernel is | 713 | * used %fs or %gs (it does not today), or if the kernel is |
663 | * running inside of a hypervisor layer. | 714 | * running inside of a hypervisor layer. |
664 | */ | 715 | */ |
665 | savesegment(fs, prev->fs); | 716 | savesegment(gs, prev->gs); |
666 | 717 | ||
667 | /* | 718 | /* |
668 | * Load the per-thread Thread-Local Storage descriptor. | 719 | * Load the per-thread Thread-Local Storage descriptor. |
@@ -670,14 +721,13 @@ struct task_struct fastcall * __switch_to(struct task_struct *prev_p, struct tas | |||
670 | load_TLS(next, cpu); | 721 | load_TLS(next, cpu); |
671 | 722 | ||
672 | /* | 723 | /* |
673 | * Restore %fs if needed. | 724 | * Restore IOPL if needed. In normal use, the flags restore |
674 | * | 725 | * in the switch assembly will handle this. But if the kernel |
675 | * Glibc normally makes %fs be zero. | 726 | * is running virtualized at a non-zero CPL, the popf will |
727 | * not restore flags, so it must be done in a separate step. | ||
676 | */ | 728 | */ |
677 | if (unlikely(prev->fs | next->fs)) | 729 | if (get_kernel_rpl() && unlikely(prev->iopl != next->iopl)) |
678 | loadsegment(fs, next->fs); | 730 | set_iopl_mask(next->iopl); |
679 | |||
680 | write_pda(pcurrent, next_p); | ||
681 | 731 | ||
682 | /* | 732 | /* |
683 | * Now maybe handle debug registers and/or IO bitmaps | 733 | * Now maybe handle debug registers and/or IO bitmaps |
@@ -688,6 +738,15 @@ struct task_struct fastcall * __switch_to(struct task_struct *prev_p, struct tas | |||
688 | 738 | ||
689 | disable_tsc(prev_p, next_p); | 739 | disable_tsc(prev_p, next_p); |
690 | 740 | ||
741 | /* | ||
742 | * Leave lazy mode, flushing any hypercalls made here. | ||
743 | * This must be done before restoring TLS segments so | ||
744 | * the GDT and LDT are properly updated, and must be | ||
745 | * done before math_state_restore, so the TS bit is up | ||
746 | * to date. | ||
747 | */ | ||
748 | arch_leave_lazy_cpu_mode(); | ||
749 | |||
691 | /* If the task has used fpu the last 5 timeslices, just do a full | 750 | /* If the task has used fpu the last 5 timeslices, just do a full |
692 | * restore of the math state immediately to avoid the trap; the | 751 | * restore of the math state immediately to avoid the trap; the |
693 | * chances of needing FPU soon are obviously high now | 752 | * chances of needing FPU soon are obviously high now |
@@ -695,6 +754,14 @@ struct task_struct fastcall * __switch_to(struct task_struct *prev_p, struct tas | |||
695 | if (next_p->fpu_counter > 5) | 754 | if (next_p->fpu_counter > 5) |
696 | math_state_restore(); | 755 | math_state_restore(); |
697 | 756 | ||
757 | /* | ||
758 | * Restore %gs if needed (which is common) | ||
759 | */ | ||
760 | if (prev->gs | next->gs) | ||
761 | loadsegment(gs, next->gs); | ||
762 | |||
763 | write_pda(pcurrent, next_p); | ||
764 | |||
698 | return prev_p; | 765 | return prev_p; |
699 | } | 766 | } |
700 | 767 | ||