x86, ptrace: rlimit BTS buffer allocation
authorMarkus Metzger <markus.t.metzger@intel.com>
Wed, 30 Jan 2008 12:32:03 +0000 (13:32 +0100)
committerIngo Molnar <mingo@elte.hu>
Wed, 30 Jan 2008 12:32:03 +0000 (13:32 +0100)
Check the rlimit of the tracing task for total and locked memory when allocating the BTS buffer.

Signed-off-by: Markus Metzger <markus.t.metzger@intel.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
arch/x86/kernel/ptrace.c

index 3e34b14e884657a20bf1d6b58e3da03e0419e4d6..88ed1e74cee962b49df1c5ab3f9c862fd8904bd0 100644 (file)
@@ -620,12 +620,80 @@ static int ptrace_bts_drain(struct task_struct *child,
        return i;
 }
 
+static int ptrace_bts_realloc(struct task_struct *child,
+                             int size, int reduce_size)
+{
+       unsigned long rlim, vm;
+       int ret, old_size;
+
+       if (size < 0)
+               return -EINVAL;
+
+       old_size = ds_get_bts_size((void *)child->thread.ds_area_msr);
+       if (old_size < 0)
+               return old_size;
+
+       ret = ds_free((void **)&child->thread.ds_area_msr);
+       if (ret < 0)
+               goto out;
+
+       size >>= PAGE_SHIFT;
+       old_size >>= PAGE_SHIFT;
+
+       current->mm->total_vm  -= old_size;
+       current->mm->locked_vm -= old_size;
+
+       if (size == 0)
+               goto out;
+
+       rlim = current->signal->rlim[RLIMIT_AS].rlim_cur >> PAGE_SHIFT;
+       vm = current->mm->total_vm  + size;
+       if (rlim < vm) {
+               ret = -ENOMEM;
+
+               if (!reduce_size)
+                       goto out;
+
+               size = rlim - current->mm->total_vm;
+               if (size <= 0)
+                       goto out;
+       }
+
+       rlim = current->signal->rlim[RLIMIT_MEMLOCK].rlim_cur >> PAGE_SHIFT;
+       vm = current->mm->locked_vm  + size;
+       if (rlim < vm) {
+               ret = -ENOMEM;
+
+               if (!reduce_size)
+                       goto out;
+
+               size = rlim - current->mm->locked_vm;
+               if (size <= 0)
+                       goto out;
+       }
+
+       ret = ds_allocate((void **)&child->thread.ds_area_msr,
+                         size << PAGE_SHIFT);
+       if (ret < 0)
+               goto out;
+
+       current->mm->total_vm  += size;
+       current->mm->locked_vm += size;
+
+out:
+       if (child->thread.ds_area_msr)
+               set_tsk_thread_flag(child, TIF_DS_AREA_MSR);
+       else
+               clear_tsk_thread_flag(child, TIF_DS_AREA_MSR);
+
+       return ret;
+}
+
 static int ptrace_bts_config(struct task_struct *child,
                             const struct ptrace_bts_config __user *ucfg)
 {
        struct ptrace_bts_config cfg;
-       unsigned long debugctl_mask;
-       int bts_size, ret;
+       int bts_size, ret = 0;
        void *ds;
 
        if (copy_from_user(&cfg, ucfg, sizeof(cfg)))
@@ -638,59 +706,46 @@ static int ptrace_bts_config(struct task_struct *child,
                if (bts_size < 0)
                        return bts_size;
        }
+       cfg.size = PAGE_ALIGN(cfg.size);
 
        if (bts_size != cfg.size) {
-               ret = ds_free((void **)&child->thread.ds_area_msr);
+               ret = ptrace_bts_realloc(child, cfg.size,
+                                        cfg.flags & PTRACE_BTS_O_CUT_SIZE);
                if (ret < 0)
-                       return ret;
+                       goto errout;
 
-               if (cfg.size > 0)
-                       ret = ds_allocate((void **)&child->thread.ds_area_msr,
-                                         cfg.size);
                ds = (void *)child->thread.ds_area_msr;
-               if (ds)
-                       set_tsk_thread_flag(child, TIF_DS_AREA_MSR);
-               else
-                       clear_tsk_thread_flag(child, TIF_DS_AREA_MSR);
-
-               if (ret < 0)
-                       return ret;
-
-               bts_size = ds_get_bts_size(ds);
-               if (bts_size <= 0)
-                       return bts_size;
        }
 
-       if (ds) {
-               if (cfg.flags & PTRACE_BTS_O_SIGNAL) {
-                       ret = ds_set_overflow(ds, DS_O_SIGNAL);
-               } else {
-                       ret = ds_set_overflow(ds, DS_O_WRAP);
-               }
-               if (ret < 0)
-                       return ret;
-       }
-
-       debugctl_mask = ds_debugctl_mask();
-       if (ds && (cfg.flags & PTRACE_BTS_O_TRACE)) {
-               child->thread.debugctlmsr |= debugctl_mask;
-               set_tsk_thread_flag(child, TIF_DEBUGCTLMSR);
-       } else {
-               /* there is no way for us to check whether we 'own'
-                * the respective bits in the DEBUGCTL MSR, we're
-                * about to clear */
-               child->thread.debugctlmsr &= ~debugctl_mask;
+       if (cfg.flags & PTRACE_BTS_O_SIGNAL)
+               ret = ds_set_overflow(ds, DS_O_SIGNAL);
+       else
+               ret = ds_set_overflow(ds, DS_O_WRAP);
+       if (ret < 0)
+               goto errout;
 
-               if (!child->thread.debugctlmsr)
-                       clear_tsk_thread_flag(child, TIF_DEBUGCTLMSR);
-       }
+       if (cfg.flags & PTRACE_BTS_O_TRACE)
+               child->thread.debugctlmsr |= ds_debugctl_mask();
+       else
+               child->thread.debugctlmsr &= ~ds_debugctl_mask();
 
-       if (ds && (cfg.flags & PTRACE_BTS_O_SCHED))
+       if (cfg.flags & PTRACE_BTS_O_SCHED)
                set_tsk_thread_flag(child, TIF_BTS_TRACE_TS);
        else
                clear_tsk_thread_flag(child, TIF_BTS_TRACE_TS);
 
-       return 0;
+out:
+       if (child->thread.debugctlmsr)
+               set_tsk_thread_flag(child, TIF_DEBUGCTLMSR);
+       else
+               clear_tsk_thread_flag(child, TIF_DEBUGCTLMSR);
+
+       return ret;
+
+errout:
+       child->thread.debugctlmsr &= ~ds_debugctl_mask();
+       clear_tsk_thread_flag(child, TIF_BTS_TRACE_TS);
+       goto out;
 }
 
 static int ptrace_bts_status(struct task_struct *child,
@@ -726,7 +781,7 @@ void ptrace_bts_take_timestamp(struct task_struct *tsk,
 {
        struct bts_struct rec = {
                .qualifier = qualifier,
-               .variant.jiffies = jiffies
+               .variant.jiffies = jiffies_64
        };
 
        ptrace_bts_write_record(tsk, &rec);
@@ -743,10 +798,12 @@ void ptrace_disable(struct task_struct *child)
 #ifdef TIF_SYSCALL_EMU
        clear_tsk_thread_flag(child, TIF_SYSCALL_EMU);
 #endif
-       ptrace_bts_config(child, /* options = */ 0);
        if (child->thread.ds_area_msr) {
-           ds_free((void **)&child->thread.ds_area_msr);
-           clear_tsk_thread_flag(child, TIF_DS_AREA_MSR);
+               ptrace_bts_realloc(child, 0, 0);
+               child->thread.debugctlmsr &= ~ds_debugctl_mask();
+               if (!child->thread.debugctlmsr)
+                       clear_tsk_thread_flag(child, TIF_DEBUGCTLMSR);
+               clear_tsk_thread_flag(child, TIF_BTS_TRACE_TS);
        }
 }