|
@@ -2817,6 +2817,7 @@ out_unlock:
|
|
__task_rq_unlock(rq);
|
|
__task_rq_unlock(rq);
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
+
|
|
void set_user_nice(struct task_struct *p, long nice)
|
|
void set_user_nice(struct task_struct *p, long nice)
|
|
{
|
|
{
|
|
int old_prio, delta, on_rq;
|
|
int old_prio, delta, on_rq;
|
|
@@ -2991,22 +2992,29 @@ static struct task_struct *find_process_by_pid(pid_t pid)
|
|
return pid ? find_task_by_vpid(pid) : current;
|
|
return pid ? find_task_by_vpid(pid) : current;
|
|
}
|
|
}
|
|
|
|
|
|
-/* Actually do priority change: must hold rq lock. */
|
|
|
|
-static void
|
|
|
|
-__setscheduler(struct rq *rq, struct task_struct *p, int policy, int prio)
|
|
|
|
|
|
+/* Actually do priority change: must hold pi & rq lock. */
|
|
|
|
+static void __setscheduler(struct rq *rq, struct task_struct *p,
|
|
|
|
+ const struct sched_attr *attr)
|
|
{
|
|
{
|
|
|
|
+ int policy = attr->sched_policy;
|
|
|
|
+
|
|
p->policy = policy;
|
|
p->policy = policy;
|
|
- p->rt_priority = prio;
|
|
|
|
|
|
+
|
|
|
|
+ if (rt_policy(policy))
|
|
|
|
+ p->rt_priority = attr->sched_priority;
|
|
|
|
+ else
|
|
|
|
+ p->static_prio = NICE_TO_PRIO(attr->sched_nice);
|
|
|
|
+
|
|
p->normal_prio = normal_prio(p);
|
|
p->normal_prio = normal_prio(p);
|
|
- /* we are holding p->pi_lock already */
|
|
|
|
p->prio = rt_mutex_getprio(p);
|
|
p->prio = rt_mutex_getprio(p);
|
|
|
|
+
|
|
if (rt_prio(p->prio))
|
|
if (rt_prio(p->prio))
|
|
p->sched_class = &rt_sched_class;
|
|
p->sched_class = &rt_sched_class;
|
|
else
|
|
else
|
|
p->sched_class = &fair_sched_class;
|
|
p->sched_class = &fair_sched_class;
|
|
|
|
+
|
|
set_load_weight(p);
|
|
set_load_weight(p);
|
|
}
|
|
}
|
|
-
|
|
|
|
/*
|
|
/*
|
|
* check the target process has a UID that matches the current process's
|
|
* check the target process has a UID that matches the current process's
|
|
*/
|
|
*/
|
|
@@ -3023,10 +3031,12 @@ static bool check_same_owner(struct task_struct *p)
|
|
return match;
|
|
return match;
|
|
}
|
|
}
|
|
|
|
|
|
-static int __sched_setscheduler(struct task_struct *p, int policy,
|
|
|
|
- const struct sched_param *param, bool user)
|
|
|
|
|
|
+static int __sched_setscheduler(struct task_struct *p,
|
|
|
|
+ const struct sched_attr *attr,
|
|
|
|
+ bool user)
|
|
{
|
|
{
|
|
int retval, oldprio, oldpolicy = -1, on_rq, running;
|
|
int retval, oldprio, oldpolicy = -1, on_rq, running;
|
|
|
|
+ int policy = attr->sched_policy;
|
|
unsigned long flags;
|
|
unsigned long flags;
|
|
const struct sched_class *prev_class;
|
|
const struct sched_class *prev_class;
|
|
struct rq *rq;
|
|
struct rq *rq;
|
|
@@ -3054,17 +3064,22 @@ recheck:
|
|
* 1..MAX_USER_RT_PRIO-1, valid priority for SCHED_NORMAL,
|
|
* 1..MAX_USER_RT_PRIO-1, valid priority for SCHED_NORMAL,
|
|
* SCHED_BATCH and SCHED_IDLE is 0.
|
|
* SCHED_BATCH and SCHED_IDLE is 0.
|
|
*/
|
|
*/
|
|
- if (param->sched_priority < 0 ||
|
|
|
|
- (p->mm && param->sched_priority > MAX_USER_RT_PRIO-1) ||
|
|
|
|
- (!p->mm && param->sched_priority > MAX_RT_PRIO-1))
|
|
|
|
|
|
+ if (attr->sched_priority < 0 ||
|
|
|
|
+ (p->mm && attr->sched_priority > MAX_USER_RT_PRIO-1) ||
|
|
|
|
+ (!p->mm && attr->sched_priority > MAX_RT_PRIO-1))
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
- if (rt_policy(policy) != (param->sched_priority != 0))
|
|
|
|
|
|
+ if (rt_policy(policy) != (attr->sched_priority != 0))
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
|
|
|
|
/*
|
|
/*
|
|
* Allow unprivileged RT tasks to decrease priority:
|
|
* Allow unprivileged RT tasks to decrease priority:
|
|
*/
|
|
*/
|
|
if (user && !capable(CAP_SYS_NICE)) {
|
|
if (user && !capable(CAP_SYS_NICE)) {
|
|
|
|
+ if (fair_policy(policy)) {
|
|
|
|
+ if (!can_nice(p, attr->sched_nice))
|
|
|
|
+ return -EPERM;
|
|
|
|
+ }
|
|
|
|
+
|
|
if (rt_policy(policy)) {
|
|
if (rt_policy(policy)) {
|
|
unsigned long rlim_rtprio =
|
|
unsigned long rlim_rtprio =
|
|
task_rlimit(p, RLIMIT_RTPRIO);
|
|
task_rlimit(p, RLIMIT_RTPRIO);
|
|
@@ -3074,8 +3089,8 @@ recheck:
|
|
return -EPERM;
|
|
return -EPERM;
|
|
|
|
|
|
/* can't increase priority */
|
|
/* can't increase priority */
|
|
- if (param->sched_priority > p->rt_priority &&
|
|
|
|
- param->sched_priority > rlim_rtprio)
|
|
|
|
|
|
+ if (attr->sched_priority > p->rt_priority &&
|
|
|
|
+ attr->sched_priority > rlim_rtprio)
|
|
return -EPERM;
|
|
return -EPERM;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -3123,11 +3138,16 @@ recheck:
|
|
/*
|
|
/*
|
|
* If not changing anything there's no need to proceed further:
|
|
* If not changing anything there's no need to proceed further:
|
|
*/
|
|
*/
|
|
- if (unlikely(policy == p->policy && (!rt_policy(policy) ||
|
|
|
|
- param->sched_priority == p->rt_priority))) {
|
|
|
|
|
|
+ if (unlikely(policy == p->policy)) {
|
|
|
|
+ if (fair_policy(policy) && attr->sched_nice != TASK_NICE(p))
|
|
|
|
+ goto change;
|
|
|
|
+ if (rt_policy(policy) && attr->sched_priority != p->rt_priority)
|
|
|
|
+ goto change;
|
|
|
|
+
|
|
task_rq_unlock(rq, p, &flags);
|
|
task_rq_unlock(rq, p, &flags);
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
+change:
|
|
|
|
|
|
#ifdef CONFIG_RT_GROUP_SCHED
|
|
#ifdef CONFIG_RT_GROUP_SCHED
|
|
if (user) {
|
|
if (user) {
|
|
@@ -3161,7 +3181,7 @@ recheck:
|
|
|
|
|
|
oldprio = p->prio;
|
|
oldprio = p->prio;
|
|
prev_class = p->sched_class;
|
|
prev_class = p->sched_class;
|
|
- __setscheduler(rq, p, policy, param->sched_priority);
|
|
|
|
|
|
+ __setscheduler(rq, p, attr);
|
|
|
|
|
|
if (running)
|
|
if (running)
|
|
p->sched_class->set_curr_task(rq);
|
|
p->sched_class->set_curr_task(rq);
|
|
@@ -3189,10 +3209,20 @@ recheck:
|
|
int sched_setscheduler(struct task_struct *p, int policy,
|
|
int sched_setscheduler(struct task_struct *p, int policy,
|
|
const struct sched_param *param)
|
|
const struct sched_param *param)
|
|
{
|
|
{
|
|
- return __sched_setscheduler(p, policy, param, true);
|
|
|
|
|
|
+ struct sched_attr attr = {
|
|
|
|
+ .sched_policy = policy,
|
|
|
|
+ .sched_priority = param->sched_priority
|
|
|
|
+ };
|
|
|
|
+ return __sched_setscheduler(p, &attr, true);
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(sched_setscheduler);
|
|
EXPORT_SYMBOL_GPL(sched_setscheduler);
|
|
|
|
|
|
|
|
+int sched_setattr(struct task_struct *p, const struct sched_attr *attr)
|
|
|
|
+{
|
|
|
|
+ return __sched_setscheduler(p, attr, true);
|
|
|
|
+}
|
|
|
|
+EXPORT_SYMBOL_GPL(sched_setattr);
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* sched_setscheduler_nocheck - change the scheduling policy and/or RT priority of a thread from kernelspace.
|
|
* sched_setscheduler_nocheck - change the scheduling policy and/or RT priority of a thread from kernelspace.
|
|
* @p: the task in question.
|
|
* @p: the task in question.
|
|
@@ -3209,7 +3239,11 @@ EXPORT_SYMBOL_GPL(sched_setscheduler);
|
|
int sched_setscheduler_nocheck(struct task_struct *p, int policy,
|
|
int sched_setscheduler_nocheck(struct task_struct *p, int policy,
|
|
const struct sched_param *param)
|
|
const struct sched_param *param)
|
|
{
|
|
{
|
|
- return __sched_setscheduler(p, policy, param, false);
|
|
|
|
|
|
+ struct sched_attr attr = {
|
|
|
|
+ .sched_policy = policy,
|
|
|
|
+ .sched_priority = param->sched_priority
|
|
|
|
+ };
|
|
|
|
+ return __sched_setscheduler(p, &attr, false);
|
|
}
|
|
}
|
|
|
|
|
|
static int
|
|
static int
|
|
@@ -3234,6 +3268,79 @@ do_sched_setscheduler(pid_t pid, int policy, struct sched_param __user *param)
|
|
return retval;
|
|
return retval;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * Mimics kernel/events/core.c perf_copy_attr().
|
|
|
|
+ */
|
|
|
|
+static int sched_copy_attr(struct sched_attr __user *uattr,
|
|
|
|
+ struct sched_attr *attr)
|
|
|
|
+{
|
|
|
|
+ u32 size;
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ if (!access_ok(VERIFY_WRITE, uattr, SCHED_ATTR_SIZE_VER0))
|
|
|
|
+ return -EFAULT;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * zero the full structure, so that a short copy will be nice.
|
|
|
|
+ */
|
|
|
|
+ memset(attr, 0, sizeof(*attr));
|
|
|
|
+
|
|
|
|
+ ret = get_user(size, &uattr->size);
|
|
|
|
+ if (ret)
|
|
|
|
+ return ret;
|
|
|
|
+
|
|
|
|
+ if (size > PAGE_SIZE) /* silly large */
|
|
|
|
+ goto err_size;
|
|
|
|
+
|
|
|
|
+ if (!size) /* abi compat */
|
|
|
|
+ size = SCHED_ATTR_SIZE_VER0;
|
|
|
|
+
|
|
|
|
+ if (size < SCHED_ATTR_SIZE_VER0)
|
|
|
|
+ goto err_size;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * If we're handed a bigger struct than we know of,
|
|
|
|
+ * ensure all the unknown bits are 0 - i.e. new
|
|
|
|
+ * user-space does not rely on any kernel feature
|
|
|
|
+ * extensions we dont know about yet.
|
|
|
|
+ */
|
|
|
|
+ if (size > sizeof(*attr)) {
|
|
|
|
+ unsigned char __user *addr;
|
|
|
|
+ unsigned char __user *end;
|
|
|
|
+ unsigned char val;
|
|
|
|
+
|
|
|
|
+ addr = (void __user *)uattr + sizeof(*attr);
|
|
|
|
+ end = (void __user *)uattr + size;
|
|
|
|
+
|
|
|
|
+ for (; addr < end; addr++) {
|
|
|
|
+ ret = get_user(val, addr);
|
|
|
|
+ if (ret)
|
|
|
|
+ return ret;
|
|
|
|
+ if (val)
|
|
|
|
+ goto err_size;
|
|
|
|
+ }
|
|
|
|
+ size = sizeof(*attr);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ret = copy_from_user(attr, uattr, size);
|
|
|
|
+ if (ret)
|
|
|
|
+ return -EFAULT;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * XXX: do we want to be lenient like existing syscalls; or do we want
|
|
|
|
+ * to be strict and return an error on out-of-bounds values?
|
|
|
|
+ */
|
|
|
|
+ attr->sched_nice = clamp(attr->sched_nice, -20, 19);
|
|
|
|
+
|
|
|
|
+out:
|
|
|
|
+ return ret;
|
|
|
|
+
|
|
|
|
+err_size:
|
|
|
|
+ put_user(sizeof(*attr), &uattr->size);
|
|
|
|
+ ret = -E2BIG;
|
|
|
|
+ goto out;
|
|
|
|
+}
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* sys_sched_setscheduler - set/change the scheduler policy and RT priority
|
|
* sys_sched_setscheduler - set/change the scheduler policy and RT priority
|
|
* @pid: the pid in question.
|
|
* @pid: the pid in question.
|
|
@@ -3264,6 +3371,33 @@ SYSCALL_DEFINE2(sched_setparam, pid_t, pid, struct sched_param __user *, param)
|
|
return do_sched_setscheduler(pid, -1, param);
|
|
return do_sched_setscheduler(pid, -1, param);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/**
|
|
|
|
+ * sys_sched_setattr - same as above, but with extended sched_attr
|
|
|
|
+ * @pid: the pid in question.
|
|
|
|
+ * @attr: structure containing the extended parameters.
|
|
|
|
+ */
|
|
|
|
+SYSCALL_DEFINE2(sched_setattr, pid_t, pid, struct sched_attr __user *, uattr)
|
|
|
|
+{
|
|
|
|
+ struct sched_attr attr;
|
|
|
|
+ struct task_struct *p;
|
|
|
|
+ int retval;
|
|
|
|
+
|
|
|
|
+ if (!uattr || pid < 0)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ if (sched_copy_attr(uattr, &attr))
|
|
|
|
+ return -EFAULT;
|
|
|
|
+
|
|
|
|
+ rcu_read_lock();
|
|
|
|
+ retval = -ESRCH;
|
|
|
|
+ p = find_process_by_pid(pid);
|
|
|
|
+ if (p != NULL)
|
|
|
|
+ retval = sched_setattr(p, &attr);
|
|
|
|
+ rcu_read_unlock();
|
|
|
|
+
|
|
|
|
+ return retval;
|
|
|
|
+}
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* sys_sched_getscheduler - get the policy (scheduling class) of a thread
|
|
* sys_sched_getscheduler - get the policy (scheduling class) of a thread
|
|
* @pid: the pid in question.
|
|
* @pid: the pid in question.
|
|
@@ -3334,6 +3468,92 @@ out_unlock:
|
|
return retval;
|
|
return retval;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int sched_read_attr(struct sched_attr __user *uattr,
|
|
|
|
+ struct sched_attr *attr,
|
|
|
|
+ unsigned int usize)
|
|
|
|
+{
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ if (!access_ok(VERIFY_WRITE, uattr, usize))
|
|
|
|
+ return -EFAULT;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * If we're handed a smaller struct than we know of,
|
|
|
|
+ * ensure all the unknown bits are 0 - i.e. old
|
|
|
|
+ * user-space does not get uncomplete information.
|
|
|
|
+ */
|
|
|
|
+ if (usize < sizeof(*attr)) {
|
|
|
|
+ unsigned char *addr;
|
|
|
|
+ unsigned char *end;
|
|
|
|
+
|
|
|
|
+ addr = (void *)attr + usize;
|
|
|
|
+ end = (void *)attr + sizeof(*attr);
|
|
|
|
+
|
|
|
|
+ for (; addr < end; addr++) {
|
|
|
|
+ if (*addr)
|
|
|
|
+ goto err_size;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ attr->size = usize;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ret = copy_to_user(uattr, attr, usize);
|
|
|
|
+ if (ret)
|
|
|
|
+ return -EFAULT;
|
|
|
|
+
|
|
|
|
+out:
|
|
|
|
+ return ret;
|
|
|
|
+
|
|
|
|
+err_size:
|
|
|
|
+ ret = -E2BIG;
|
|
|
|
+ goto out;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * sys_sched_getattr - same as above, but with extended "sched_param"
|
|
|
|
+ * @pid: the pid in question.
|
|
|
|
+ * @attr: structure containing the extended parameters.
|
|
|
|
+ * @size: sizeof(attr) for fwd/bwd comp.
|
|
|
|
+ */
|
|
|
|
+SYSCALL_DEFINE3(sched_getattr, pid_t, pid, struct sched_attr __user *, uattr,
|
|
|
|
+ unsigned int, size)
|
|
|
|
+{
|
|
|
|
+ struct sched_attr attr = {
|
|
|
|
+ .size = sizeof(struct sched_attr),
|
|
|
|
+ };
|
|
|
|
+ struct task_struct *p;
|
|
|
|
+ int retval;
|
|
|
|
+
|
|
|
|
+ if (!uattr || pid < 0 || size > PAGE_SIZE ||
|
|
|
|
+ size < SCHED_ATTR_SIZE_VER0)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ rcu_read_lock();
|
|
|
|
+ p = find_process_by_pid(pid);
|
|
|
|
+ retval = -ESRCH;
|
|
|
|
+ if (!p)
|
|
|
|
+ goto out_unlock;
|
|
|
|
+
|
|
|
|
+ retval = security_task_getscheduler(p);
|
|
|
|
+ if (retval)
|
|
|
|
+ goto out_unlock;
|
|
|
|
+
|
|
|
|
+ attr.sched_policy = p->policy;
|
|
|
|
+ if (task_has_rt_policy(p))
|
|
|
|
+ attr.sched_priority = p->rt_priority;
|
|
|
|
+ else
|
|
|
|
+ attr.sched_nice = TASK_NICE(p);
|
|
|
|
+
|
|
|
|
+ rcu_read_unlock();
|
|
|
|
+
|
|
|
|
+ retval = sched_read_attr(uattr, &attr, size);
|
|
|
|
+ return retval;
|
|
|
|
+
|
|
|
|
+out_unlock:
|
|
|
|
+ rcu_read_unlock();
|
|
|
|
+ return retval;
|
|
|
|
+}
|
|
|
|
+
|
|
long sched_setaffinity(pid_t pid, const struct cpumask *in_mask)
|
|
long sched_setaffinity(pid_t pid, const struct cpumask *in_mask)
|
|
{
|
|
{
|
|
cpumask_var_t cpus_allowed, new_mask;
|
|
cpumask_var_t cpus_allowed, new_mask;
|
|
@@ -6400,13 +6620,16 @@ EXPORT_SYMBOL(__might_sleep);
|
|
static void normalize_task(struct rq *rq, struct task_struct *p)
|
|
static void normalize_task(struct rq *rq, struct task_struct *p)
|
|
{
|
|
{
|
|
const struct sched_class *prev_class = p->sched_class;
|
|
const struct sched_class *prev_class = p->sched_class;
|
|
|
|
+ struct sched_attr attr = {
|
|
|
|
+ .sched_policy = SCHED_NORMAL,
|
|
|
|
+ };
|
|
int old_prio = p->prio;
|
|
int old_prio = p->prio;
|
|
int on_rq;
|
|
int on_rq;
|
|
|
|
|
|
on_rq = p->on_rq;
|
|
on_rq = p->on_rq;
|
|
if (on_rq)
|
|
if (on_rq)
|
|
dequeue_task(rq, p, 0);
|
|
dequeue_task(rq, p, 0);
|
|
- __setscheduler(rq, p, SCHED_NORMAL, 0);
|
|
|
|
|
|
+ __setscheduler(rq, p, &attr);
|
|
if (on_rq) {
|
|
if (on_rq) {
|
|
enqueue_task(rq, p, 0);
|
|
enqueue_task(rq, p, 0);
|
|
resched_task(rq->curr);
|
|
resched_task(rq->curr);
|