asm volatile("ltr %w0"::"q" (GDT_ENTRY_TSS*8));
 }
 
+static inline void force_reload_TR(void)
+{
+       struct desc_struct *d = get_cpu_gdt_table(smp_processor_id());
+       tss_desc tss;
+
+       memcpy(&tss, &d[GDT_ENTRY_TSS], sizeof(tss_desc));
+
+       /*
+        * LTR requires an available TSS, and the TSS is currently
+        * busy.  Make it be available so that LTR will work.
+        */
+       tss.type = DESC_TSS;
+       write_gdt_entry(d, GDT_ENTRY_TSS, &tss, DESC_TSS);
+
+       load_TR_desc();
+}
+
+DECLARE_PER_CPU(bool, need_tr_refresh);
+
+static inline void refresh_TR(void)
+{
+       DEBUG_LOCKS_WARN_ON(preemptible());
+
+       if (unlikely(this_cpu_read(need_tr_refresh))) {
+               force_reload_TR();
+               this_cpu_write(need_tr_refresh, false);
+       }
+}
+
+/*
+ * If you do something evil that corrupts the cached TSS limit (I'm looking
+ * at you, VMX exits), call this function.
+ *
+ * The optimization here is that the TSS limit only matters for Linux if the
+ * IO bitmap is in use.  If the TSS limit gets forced to its minimum value,
+ * everything works except that IO bitmap will be ignored and all CPL 3 IO
+ * instructions will #GP, which is exactly what we want for normal tasks.
+ */
+static inline void invalidate_tss_limit(void)
+{
+       DEBUG_LOCKS_WARN_ON(preemptible());
+
+       if (unlikely(test_thread_flag(TIF_IO_BITMAP)))
+               force_reload_TR();
+       else
+               this_cpu_write(need_tr_refresh, true);
+}
+
 static inline void native_load_gdt(const struct desc_ptr *dtr)
 {
        asm volatile("lgdt %0"::"m" (*dtr));
 
 #include <linux/syscalls.h>
 #include <linux/bitmap.h>
 #include <asm/syscalls.h>
+#include <asm/desc.h>
 
 /*
  * this changes the io permissions bitmap in the current task.
                memset(bitmap, 0xff, IO_BITMAP_BYTES);
                t->io_bitmap_ptr = bitmap;
                set_thread_flag(TIF_IO_BITMAP);
+
+               preempt_disable();
+               refresh_TR();
+               preempt_enable();
        }
 
        /*
 
 #include <asm/mce.h>
 #include <asm/vm86.h>
 #include <asm/switch_to.h>
+#include <asm/desc.h>
 
 /*
  * per-CPU TSS segments. Threads are completely 'soft' on Linux,
 };
 EXPORT_PER_CPU_SYMBOL(cpu_tss);
 
+DEFINE_PER_CPU(bool, need_tr_refresh);
+EXPORT_PER_CPU_SYMBOL_GPL(need_tr_refresh);
+
 /*
  * this gets called so that we can store lazy state into memory and copy the
  * current task into the new thread.
                 */
                memcpy(tss->io_bitmap, next->io_bitmap_ptr,
                       max(prev->io_bitmap_max, next->io_bitmap_max));
+
+               /*
+                * Make sure that the TSS limit is correct for the CPU
+                * to notice the IO bitmap.
+                */
+               refresh_TR();
        } else if (test_tsk_thread_flag(prev_p, TIF_IO_BITMAP)) {
                /*
                 * Clear any possible leftover bits:
 
        m->host[i].value = host_val;
 }
 
-static void reload_tss(void)
-{
-       /*
-        * VT restores TR but not its size.  Useless.
-        */
-       struct desc_ptr *gdt = this_cpu_ptr(&host_gdt);
-       struct desc_struct *descs;
-
-       descs = (void *)gdt->address;
-       descs[GDT_ENTRY_TSS].type = 9; /* available TSS */
-       load_TR_desc();
-}
-
 static bool update_transition_efer(struct vcpu_vmx *vmx, int efer_offset)
 {
        u64 guest_efer = vmx->vcpu.arch.efer;
                loadsegment(es, vmx->host_state.es_sel);
        }
 #endif
-       reload_tss();
+       invalidate_tss_limit();
 #ifdef CONFIG_X86_64
        wrmsrl(MSR_KERNEL_GS_BASE, vmx->msr_host_kernel_gs_base);
 #endif
                            (unsigned long)this_cpu_ptr(&cpu_tss));
                vmcs_writel(HOST_GDTR_BASE, gdt->address);
 
+               /*
+                * VM exits change the host TR limit to 0x67 after a VM
+                * exit.  This is okay, since 0x67 covers everything except
+                * the IO bitmap and have have code to handle the IO bitmap
+                * being lost after a VM exit.
+                */
+               BUILD_BUG_ON(IO_BITMAP_OFFSET - 1 != 0x67);
+
                rdmsrl(MSR_IA32_SYSENTER_ESP, sysenter_esp);
                vmcs_writel(HOST_IA32_SYSENTER_ESP, sysenter_esp); /* 22.2.3 */