EXPORT_SYMBOL(set_user_nice);
 
 /*
- * can_nice - check if a task can reduce its nice value
+ * is_nice_reduction - check if nice value is an actual reduction
+ *
+ * Similar to can_nice() but does not perform a capability check.
+ *
  * @p: task
  * @nice: nice value
  */
-int can_nice(const struct task_struct *p, const int nice)
+static bool is_nice_reduction(const struct task_struct *p, const int nice)
 {
        /* Convert nice value [19,-20] to rlimit style value [1,40]: */
        int nice_rlim = nice_to_rlimit(nice);
 
-       return (nice_rlim <= task_rlimit(p, RLIMIT_NICE) ||
-               capable(CAP_SYS_NICE));
+       return (nice_rlim <= task_rlimit(p, RLIMIT_NICE));
+}
+
+/*
+ * can_nice - check if a task can reduce its nice value
+ * @p: task
+ * @nice: nice value
+ */
+int can_nice(const struct task_struct *p, const int nice)
+{
+       return is_nice_reduction(p, nice) || capable(CAP_SYS_NICE);
 }
 
 #ifdef __ARCH_WANT_SYS_NICE
        return match;
 }
 
+/*
+ * Allow unprivileged RT tasks to decrease priority.
+ * Only issue a capable test if needed and only once to avoid an audit
+ * event on permitted non-privileged operations:
+ */
+static int user_check_sched_setscheduler(struct task_struct *p,
+                                        const struct sched_attr *attr,
+                                        int policy, int reset_on_fork)
+{
+       if (fair_policy(policy)) {
+               if (attr->sched_nice < task_nice(p) &&
+                   !is_nice_reduction(p, attr->sched_nice))
+                       goto req_priv;
+       }
+
+       if (rt_policy(policy)) {
+               unsigned long rlim_rtprio = task_rlimit(p, RLIMIT_RTPRIO);
+
+               /* Can't set/change the rt policy: */
+               if (policy != p->policy && !rlim_rtprio)
+                       goto req_priv;
+
+               /* Can't increase priority: */
+               if (attr->sched_priority > p->rt_priority &&
+                   attr->sched_priority > rlim_rtprio)
+                       goto req_priv;
+       }
+
+       /*
+        * Can't set/change SCHED_DEADLINE policy at all for now
+        * (safest behavior); in the future we would like to allow
+        * unprivileged DL tasks to increase their relative deadline
+        * or reduce their runtime (both ways reducing utilization)
+        */
+       if (dl_policy(policy))
+               goto req_priv;
+
+       /*
+        * Treat SCHED_IDLE as nice 20. Only allow a switch to
+        * SCHED_NORMAL if the RLIMIT_NICE would normally permit it.
+        */
+       if (task_has_idle_policy(p) && !idle_policy(policy)) {
+               if (!is_nice_reduction(p, task_nice(p)))
+                       goto req_priv;
+       }
+
+       /* Can't change other user's priorities: */
+       if (!check_same_owner(p))
+               goto req_priv;
+
+       /* Normal users shall not reset the sched_reset_on_fork flag: */
+       if (p->sched_reset_on_fork && !reset_on_fork)
+               goto req_priv;
+
+       return 0;
+
+req_priv:
+       if (!capable(CAP_SYS_NICE))
+               return -EPERM;
+
+       return 0;
+}
+
 static int __sched_setscheduler(struct task_struct *p,
                                const struct sched_attr *attr,
                                bool user, bool pi)
            (rt_policy(policy) != (attr->sched_priority != 0)))
                return -EINVAL;
 
-       /*
-        * Allow unprivileged RT tasks to decrease priority:
-        */
-       if (user && !capable(CAP_SYS_NICE)) {
-               if (fair_policy(policy)) {
-                       if (attr->sched_nice < task_nice(p) &&
-                           !can_nice(p, attr->sched_nice))
-                               return -EPERM;
-               }
-
-               if (rt_policy(policy)) {
-                       unsigned long rlim_rtprio =
-                                       task_rlimit(p, RLIMIT_RTPRIO);
-
-                       /* Can't set/change the rt policy: */
-                       if (policy != p->policy && !rlim_rtprio)
-                               return -EPERM;
-
-                       /* Can't increase priority: */
-                       if (attr->sched_priority > p->rt_priority &&
-                           attr->sched_priority > rlim_rtprio)
-                               return -EPERM;
-               }
-
-                /*
-                 * Can't set/change SCHED_DEADLINE policy at all for now
-                 * (safest behavior); in the future we would like to allow
-                 * unprivileged DL tasks to increase their relative deadline
-                 * or reduce their runtime (both ways reducing utilization)
-                 */
-               if (dl_policy(policy))
-                       return -EPERM;
-
-               /*
-                * Treat SCHED_IDLE as nice 20. Only allow a switch to
-                * SCHED_NORMAL if the RLIMIT_NICE would normally permit it.
-                */
-               if (task_has_idle_policy(p) && !idle_policy(policy)) {
-                       if (!can_nice(p, task_nice(p)))
-                               return -EPERM;
-               }
-
-               /* Can't change other user's priorities: */
-               if (!check_same_owner(p))
-                       return -EPERM;
-
-               /* Normal users shall not reset the sched_reset_on_fork flag: */
-               if (p->sched_reset_on_fork && !reset_on_fork)
-                       return -EPERM;
-       }
-
        if (user) {
+               retval = user_check_sched_setscheduler(p, attr, policy, reset_on_fork);
+               if (retval)
+                       return retval;
+
                if (attr->sched_flags & SCHED_FLAG_SUGOV)
                        return -EINVAL;