Merge branches 'x86/acpi', 'x86/apic', 'x86/cpudetect', 'x86/headers', 'x86/paravirt...
[safe/jmp/linux-2.6] / arch / x86 / kernel / ptrace.c
index 6ad2bb6..d2f7cd5 100644 (file)
@@ -75,10 +75,7 @@ static inline bool invalid_selector(u16 value)
 static unsigned long *pt_regs_access(struct pt_regs *regs, unsigned long regno)
 {
        BUILD_BUG_ON(offsetof(struct pt_regs, bx) != 0);
-       regno >>= 2;
-       if (regno > FS)
-               --regno;
-       return &regs->bx + regno;
+       return &regs->bx + (regno >> 2);
 }
 
 static u16 get_segment_reg(struct task_struct *task, unsigned long offset)
@@ -90,9 +87,10 @@ static u16 get_segment_reg(struct task_struct *task, unsigned long offset)
        if (offset != offsetof(struct user_regs_struct, gs))
                retval = *pt_regs_access(task_pt_regs(task), offset);
        else {
-               retval = task->thread.gs;
                if (task == current)
-                       savesegment(gs, retval);
+                       retval = get_user_gs(task_pt_regs(task));
+               else
+                       retval = task_user_gs(task);
        }
        return retval;
 }
@@ -126,13 +124,10 @@ static int set_segment_reg(struct task_struct *task,
                break;
 
        case offsetof(struct user_regs_struct, gs):
-               task->thread.gs = value;
                if (task == current)
-                       /*
-                        * The user-mode %gs is not affected by
-                        * kernel entry, so we must update the CPU.
-                        */
-                       loadsegment(gs, value);
+                       set_user_gs(task_pt_regs(task), value);
+               else
+                       task_user_gs(task) = value;
        }
 
        return 0;
@@ -650,6 +645,24 @@ static int ptrace_bts_drain(struct task_struct *child,
        return drained;
 }
 
+static int ptrace_bts_allocate_buffer(struct task_struct *child, size_t size)
+{
+       child->bts_buffer = alloc_locked_buffer(size);
+       if (!child->bts_buffer)
+               return -ENOMEM;
+
+       child->bts_size = size;
+
+       return 0;
+}
+
+static void ptrace_bts_free_buffer(struct task_struct *child)
+{
+       free_locked_buffer(child->bts_buffer, child->bts_size);
+       child->bts_buffer = NULL;
+       child->bts_size = 0;
+}
+
 static int ptrace_bts_config(struct task_struct *child,
                             long cfg_size,
                             const struct ptrace_bts_config __user *ucfg)
@@ -679,14 +692,13 @@ static int ptrace_bts_config(struct task_struct *child,
 
        if ((cfg.flags & PTRACE_BTS_O_ALLOC) &&
            (cfg.size != child->bts_size)) {
-               kfree(child->bts_buffer);
+               int error;
 
-               child->bts_size = cfg.size;
-               child->bts_buffer = kzalloc(cfg.size, GFP_KERNEL);
-               if (!child->bts_buffer) {
-                       child->bts_size = 0;
-                       return -ENOMEM;
-               }
+               ptrace_bts_free_buffer(child);
+
+               error = ptrace_bts_allocate_buffer(child, cfg.size);
+               if (error < 0)
+                       return error;
        }
 
        if (cfg.flags & PTRACE_BTS_O_TRACE)
@@ -701,10 +713,8 @@ static int ptrace_bts_config(struct task_struct *child,
        if (IS_ERR(child->bts)) {
                int error = PTR_ERR(child->bts);
 
-               kfree(child->bts_buffer);
+               ptrace_bts_free_buffer(child);
                child->bts = NULL;
-               child->bts_buffer = NULL;
-               child->bts_size = 0;
 
                return error;
        }
@@ -784,6 +794,9 @@ static void ptrace_bts_untrace(struct task_struct *child)
                ds_release_bts(child->bts);
                child->bts = NULL;
 
+               /* We cannot update total_vm and locked_vm since
+                  child's mm is already gone. But we can reclaim the
+                  memory. */
                kfree(child->bts_buffer);
                child->bts_buffer = NULL;
                child->bts_size = 0;
@@ -792,7 +805,16 @@ static void ptrace_bts_untrace(struct task_struct *child)
 
 static void ptrace_bts_detach(struct task_struct *child)
 {
-       ptrace_bts_untrace(child);
+       /*
+        * Ptrace_detach() races with ptrace_untrace() in case
+        * the child dies and is reaped by another thread.
+        *
+        * We only do the memory accounting at this point and
+        * leave the buffer deallocation and the bts tracer
+        * release to ptrace_bts_untrace() which will be called
+        * later on with tasklist_lock held.
+        */
+       release_locked_buffer(child->bts_buffer, child->bts_size);
 }
 #else
 static inline void ptrace_bts_fork(struct task_struct *tsk) {}