diff options
Diffstat (limited to 'fs/proc/base.c')
| -rw-r--r-- | fs/proc/base.c | 109 | 
1 files changed, 109 insertions, 0 deletions
diff --git a/fs/proc/base.c b/fs/proc/base.c index 144a96732dd..3c231adf845 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -873,6 +873,113 @@ static const struct file_operations proc_environ_operations = {  	.release	= mem_release,  }; +static ssize_t oom_adj_read(struct file *file, char __user *buf, size_t count, +			    loff_t *ppos) +{ +	struct task_struct *task = get_proc_task(file->f_path.dentry->d_inode); +	char buffer[PROC_NUMBUF]; +	int oom_adj = OOM_ADJUST_MIN; +	size_t len; +	unsigned long flags; + +	if (!task) +		return -ESRCH; +	if (lock_task_sighand(task, &flags)) { +		if (task->signal->oom_score_adj == OOM_SCORE_ADJ_MAX) +			oom_adj = OOM_ADJUST_MAX; +		else +			oom_adj = (task->signal->oom_score_adj * -OOM_DISABLE) / +				  OOM_SCORE_ADJ_MAX; +		unlock_task_sighand(task, &flags); +	} +	put_task_struct(task); +	len = snprintf(buffer, sizeof(buffer), "%d\n", oom_adj); +	return simple_read_from_buffer(buf, count, ppos, buffer, len); +} + +static ssize_t oom_adj_write(struct file *file, const char __user *buf, +			     size_t count, loff_t *ppos) +{ +	struct task_struct *task; +	char buffer[PROC_NUMBUF]; +	int oom_adj; +	unsigned long flags; +	int err; + +	memset(buffer, 0, sizeof(buffer)); +	if (count > sizeof(buffer) - 1) +		count = sizeof(buffer) - 1; +	if (copy_from_user(buffer, buf, count)) { +		err = -EFAULT; +		goto out; +	} + +	err = kstrtoint(strstrip(buffer), 0, &oom_adj); +	if (err) +		goto out; +	if ((oom_adj < OOM_ADJUST_MIN || oom_adj > OOM_ADJUST_MAX) && +	     oom_adj != OOM_DISABLE) { +		err = -EINVAL; +		goto out; +	} + +	task = get_proc_task(file->f_path.dentry->d_inode); +	if (!task) { +		err = -ESRCH; +		goto out; +	} + +	task_lock(task); +	if (!task->mm) { +		err = -EINVAL; +		goto err_task_lock; +	} + +	if (!lock_task_sighand(task, &flags)) { +		err = -ESRCH; +		goto err_task_lock; +	} + +	/* +	 * Scale /proc/pid/oom_score_adj appropriately ensuring that a maximum +	 * value is always attainable. +	 */ +	if (oom_adj == OOM_ADJUST_MAX) +		oom_adj = OOM_SCORE_ADJ_MAX; +	else +		oom_adj = (oom_adj * OOM_SCORE_ADJ_MAX) / -OOM_DISABLE; + +	if (oom_adj < task->signal->oom_score_adj && +	    !capable(CAP_SYS_RESOURCE)) { +		err = -EACCES; +		goto err_sighand; +	} + +	/* +	 * /proc/pid/oom_adj is provided for legacy purposes, ask users to use +	 * /proc/pid/oom_score_adj instead. +	 */ +	printk_once(KERN_WARNING "%s (%d): /proc/%d/oom_adj is deprecated, please use /proc/%d/oom_score_adj instead.\n", +		  current->comm, task_pid_nr(current), task_pid_nr(task), +		  task_pid_nr(task)); + +	task->signal->oom_score_adj = oom_adj; +	trace_oom_score_adj_update(task); +err_sighand: +	unlock_task_sighand(task, &flags); +err_task_lock: +	task_unlock(task); +	put_task_struct(task); +out: +	return err < 0 ? err : count; +} + +static const struct file_operations proc_oom_adj_operations = { +	.read		= oom_adj_read, +	.write		= oom_adj_write, +	.llseek		= generic_file_llseek, +}; +  static ssize_t oom_score_adj_read(struct file *file, char __user *buf,  					size_t count, loff_t *ppos)  { @@ -2598,6 +2705,7 @@ static const struct pid_entry tgid_base_stuff[] = {  	REG("cgroup",  S_IRUGO, proc_cgroup_operations),  #endif  	INF("oom_score",  S_IRUGO, proc_oom_score), +	REG("oom_adj",    S_IRUGO|S_IWUSR, proc_oom_adj_operations),  	REG("oom_score_adj", S_IRUGO|S_IWUSR, proc_oom_score_adj_operations),  #ifdef CONFIG_AUDITSYSCALL  	REG("loginuid",   S_IWUSR|S_IRUGO, proc_loginuid_operations), @@ -2964,6 +3072,7 @@ static const struct pid_entry tid_base_stuff[] = {  	REG("cgroup",  S_IRUGO, proc_cgroup_operations),  #endif  	INF("oom_score", S_IRUGO, proc_oom_score), +	REG("oom_adj",   S_IRUGO|S_IWUSR, proc_oom_adj_operations),  	REG("oom_score_adj", S_IRUGO|S_IWUSR, proc_oom_score_adj_operations),  #ifdef CONFIG_AUDITSYSCALL  	REG("loginuid",  S_IWUSR|S_IRUGO, proc_loginuid_operations),  |