Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs-2.6
[linux-2.6.git] / fs / proc / base.c
index 9883f1e..9b094c1 100644 (file)
@@ -1025,28 +1025,47 @@ static ssize_t oom_adjust_write(struct file *file, const char __user *buf,
        memset(buffer, 0, sizeof(buffer));
        if (count > sizeof(buffer) - 1)
                count = sizeof(buffer) - 1;
-       if (copy_from_user(buffer, buf, count))
-               return -EFAULT;
+       if (copy_from_user(buffer, buf, count)) {
+               err = -EFAULT;
+               goto out;
+       }
 
        err = strict_strtol(strstrip(buffer), 0, &oom_adjust);
        if (err)
-               return -EINVAL;
+               goto out;
        if ((oom_adjust < OOM_ADJUST_MIN || oom_adjust > OOM_ADJUST_MAX) &&
-            oom_adjust != OOM_DISABLE)
-               return -EINVAL;
+            oom_adjust != OOM_DISABLE) {
+               err = -EINVAL;
+               goto out;
+       }
 
        task = get_proc_task(file->f_path.dentry->d_inode);
-       if (!task)
-               return -ESRCH;
+       if (!task) {
+               err = -ESRCH;
+               goto out;
+       }
+
+       task_lock(task);
+       if (!task->mm) {
+               err = -EINVAL;
+               goto err_task_lock;
+       }
+
        if (!lock_task_sighand(task, &flags)) {
-               put_task_struct(task);
-               return -ESRCH;
+               err = -ESRCH;
+               goto err_task_lock;
        }
 
        if (oom_adjust < task->signal->oom_adj && !capable(CAP_SYS_RESOURCE)) {
-               unlock_task_sighand(task, &flags);
-               put_task_struct(task);
-               return -EACCES;
+               err = -EACCES;
+               goto err_sighand;
+       }
+
+       if (oom_adjust != task->signal->oom_adj) {
+               if (oom_adjust == OOM_DISABLE)
+                       atomic_inc(&task->mm->oom_disable_count);
+               if (task->signal->oom_adj == OOM_DISABLE)
+                       atomic_dec(&task->mm->oom_disable_count);
        }
 
        /*
@@ -1067,10 +1086,13 @@ static ssize_t oom_adjust_write(struct file *file, const char __user *buf,
        else
                task->signal->oom_score_adj = (oom_adjust * OOM_SCORE_ADJ_MAX) /
                                                                -OOM_DISABLE;
+err_sighand:
        unlock_task_sighand(task, &flags);
+err_task_lock:
+       task_unlock(task);
        put_task_struct(task);
-
-       return count;
+out:
+       return err < 0 ? err : count;
 }
 
 static const struct file_operations proc_oom_adjust_operations = {
@@ -1111,30 +1133,49 @@ static ssize_t oom_score_adj_write(struct file *file, const char __user *buf,
        memset(buffer, 0, sizeof(buffer));
        if (count > sizeof(buffer) - 1)
                count = sizeof(buffer) - 1;
-       if (copy_from_user(buffer, buf, count))
-               return -EFAULT;
+       if (copy_from_user(buffer, buf, count)) {
+               err = -EFAULT;
+               goto out;
+       }
 
        err = strict_strtol(strstrip(buffer), 0, &oom_score_adj);
        if (err)
-               return -EINVAL;
+               goto out;
        if (oom_score_adj < OOM_SCORE_ADJ_MIN ||
-                       oom_score_adj > OOM_SCORE_ADJ_MAX)
-               return -EINVAL;
+                       oom_score_adj > OOM_SCORE_ADJ_MAX) {
+               err = -EINVAL;
+               goto out;
+       }
 
        task = get_proc_task(file->f_path.dentry->d_inode);
-       if (!task)
-               return -ESRCH;
+       if (!task) {
+               err = -ESRCH;
+               goto out;
+       }
+
+       task_lock(task);
+       if (!task->mm) {
+               err = -EINVAL;
+               goto err_task_lock;
+       }
+
        if (!lock_task_sighand(task, &flags)) {
-               put_task_struct(task);
-               return -ESRCH;
+               err = -ESRCH;
+               goto err_task_lock;
        }
+
        if (oom_score_adj < task->signal->oom_score_adj &&
                        !capable(CAP_SYS_RESOURCE)) {
-               unlock_task_sighand(task, &flags);
-               put_task_struct(task);
-               return -EACCES;
+               err = -EACCES;
+               goto err_sighand;
        }
 
+       if (oom_score_adj != task->signal->oom_score_adj) {
+               if (oom_score_adj == OOM_SCORE_ADJ_MIN)
+                       atomic_inc(&task->mm->oom_disable_count);
+               if (task->signal->oom_score_adj == OOM_SCORE_ADJ_MIN)
+                       atomic_dec(&task->mm->oom_disable_count);
+       }
        task->signal->oom_score_adj = oom_score_adj;
        /*
         * Scale /proc/pid/oom_adj appropriately ensuring that OOM_DISABLE is
@@ -1145,9 +1186,13 @@ static ssize_t oom_score_adj_write(struct file *file, const char __user *buf,
        else
                task->signal->oom_adj = (oom_score_adj * OOM_ADJUST_MAX) /
                                                        OOM_SCORE_ADJ_MAX;
+err_sighand:
        unlock_task_sighand(task, &flags);
+err_task_lock:
+       task_unlock(task);
        put_task_struct(task);
-       return count;
+out:
+       return err < 0 ? err : count;
 }
 
 static const struct file_operations proc_oom_score_adj_operations = {