To set time limit for process now we can use RLIMIT_CPU.
However, it has precision up to one second and it can be
too big for some purposes.

This patch adds support of RLIMIT_CPUNS, which works 
almost as RLIMIT_CPU, but has nanosecond precision.

At the moment, RLIMIT_CPU and RLIMIT_CPUNS are two
independent values, because I don't see any nice way
for them to be together.

Signed-off-by: Grigory Reznikov <griku...@mail.ru>
---
 fs/proc/base.c                      |  1 +
 include/asm-generic/resource.h      |  1 +
 include/linux/posix-timers.h        |  1 +
 include/uapi/asm-generic/resource.h |  4 +++-
 kernel/fork.c                       | 20 +++++++++++++----
 kernel/sys.c                        | 11 ++++++++-
 kernel/time/posix-cpu-timers.c      | 45 +++++++++++++++++++++++++++++++++----
 7 files changed, 73 insertions(+), 10 deletions(-)

diff --git a/fs/proc/base.c b/fs/proc/base.c
index 719c2e9..1e3049e 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -567,6 +567,7 @@ static const struct limit_names lnames[RLIM_NLIMITS] = {
        [RLIMIT_NICE] = {"Max nice priority", NULL},
        [RLIMIT_RTPRIO] = {"Max realtime priority", NULL},
        [RLIMIT_RTTIME] = {"Max realtime timeout", "us"},
+       [RLIMIT_CPUNS] = {"Max cpu time", "ns"},
 };
 
 /* Display limits for a process */
diff --git a/include/asm-generic/resource.h b/include/asm-generic/resource.h
index 5e752b9..ec7b0c5 100644
--- a/include/asm-generic/resource.h
+++ b/include/asm-generic/resource.h
@@ -25,6 +25,7 @@
        [RLIMIT_NICE]           = { 0, 0 },                             \
        [RLIMIT_RTPRIO]         = { 0, 0 },                             \
        [RLIMIT_RTTIME]         = {  RLIM_INFINITY,  RLIM_INFINITY },   \
+       [RLIMIT_CPUNS]          = {  RLIM_INFINITY,  RLIM_INFINITY },   \
 }
 
 #endif
diff --git a/include/linux/posix-timers.h b/include/linux/posix-timers.h
index 62839fd..0e22bde 100644
--- a/include/linux/posix-timers.h
+++ b/include/linux/posix-timers.h
@@ -110,6 +110,7 @@ void posix_cpu_timers_exit_group(struct task_struct *task);
 void set_process_cpu_timer(struct task_struct *task, unsigned int clock_idx,
                           u64 *newval, u64 *oldval);
 
+void update_rlimit_cpu_ns(struct task_struct *task, unsigned long rlim_new);
 void update_rlimit_cpu(struct task_struct *task, unsigned long rlim_new);
 
 void posixtimer_rearm(struct siginfo *info);
diff --git a/include/uapi/asm-generic/resource.h 
b/include/uapi/asm-generic/resource.h
index c6d10af..a86b2f4 100644
--- a/include/uapi/asm-generic/resource.h
+++ b/include/uapi/asm-generic/resource.h
@@ -45,7 +45,9 @@
                                           0-39 for nice level 19 .. -20 */
 #define RLIMIT_RTPRIO          14      /* maximum realtime priority */
 #define RLIMIT_RTTIME          15      /* timeout for RT tasks in us */
-#define RLIM_NLIMITS           16
+#define RLIMIT_CPUNS           16      /* CPU time in ns,
+                                       doesn't depend on RLIMIT_CPU */
+#define RLIM_NLIMITS           17
 
 /*
  * SuS says limits have to be unsigned.
diff --git a/kernel/fork.c b/kernel/fork.c
index e075b77..33f9bbf 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -1348,11 +1348,23 @@ void __cleanup_sighand(struct sighand_struct *sighand)
  */
 static void posix_cpu_timers_init_group(struct signal_struct *sig)
 {
-       unsigned long cpu_limit;
-
+       unsigned long cpu_limit, cpuns_limit, total_limit;
+       /* RLIMIT_CPU timeout, RLIMIT_CPUNS timeout and time
+        * to closest timeout
+        */
+
        cpu_limit = READ_ONCE(sig->rlim[RLIMIT_CPU].rlim_cur);
-       if (cpu_limit != RLIM_INFINITY) {
-               sig->cputime_expires.prof_exp = cpu_limit * NSEC_PER_SEC;
+       cpuns_limit = READ_ONCE(sig->rlim[RLIMIT_CPUNS].rlim_cur);
+
+       total_limit = RLIM_INFINITY;
+
+       if (cpu_limit != RLIM_INFINITY)
+               total_limit = cpu_limit * NSEC_PER_SEC;
+       if (cpuns_limit != RLIM_INFINITY && cpuns_limit < total_limit)
+               total_limit = cpuns_limit;
+
+       if (total_limit != RLIM_INFINITY) {
+               sig->cputime_expires.prof_exp = total_limit;
                sig->cputimer.running = true;
        }
 
diff --git a/kernel/sys.c b/kernel/sys.c
index 2855ee7..539b110 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -1504,6 +1504,8 @@ int do_prlimit(struct task_struct *tsk, unsigned int 
resource,
                         */
                        new_rlim->rlim_cur = 1;
                }
+               if (resource == RLIMIT_CPUNS && new_rlim->rlim_cur == 0)
+                       new_rlim->rlim_cur = NSEC_PER_SEC;
        }
        if (!retval) {
                if (old_rlim)
@@ -1519,10 +1521,17 @@ int do_prlimit(struct task_struct *tsk, unsigned int 
resource,
         * very long-standing error, and fixing it now risks breakage of
         * applications, so we live with it
         */
-        if (!retval && new_rlim && resource == RLIMIT_CPU &&
+       if (!retval && new_rlim && resource == RIMIT_CPU &&
             new_rlim->rlim_cur != RLIM_INFINITY &&
             IS_ENABLED(CONFIG_POSIX_TIMERS))
                update_rlimit_cpu(tsk, new_rlim->rlim_cur);
+
+       if (!retval && new_rlim && resource == RLIMIT_CPUNS &&
+            new_rlim->rlim_cur != RLIM_INFINITY &&
+            IS_ENABLED(CONFIG_POSIX_TIMERS))
+               update_rlimit_cpu_ns(tsk, new_rlim->rlim_cur);
+
+
 out:
        read_unlock(&tasklist_lock);
        return retval;
diff --git a/kernel/time/posix-cpu-timers.c b/kernel/time/posix-cpu-timers.c
index a3bd5db..e4830f6 100644
--- a/kernel/time/posix-cpu-timers.c
+++ b/kernel/time/posix-cpu-timers.c
@@ -19,20 +19,26 @@
 static void posix_cpu_timer_rearm(struct k_itimer *timer);
 
 /*
- * Called after updating RLIMIT_CPU to run cpu timer and update
+ * Called after updating RLIMIT_CPUNS to run cpu timer and update
  * tsk->signal->cputime_expires expiration cache if necessary. Needs
  * siglock protection since other code may update expiration cache as
  * well.
  */
-void update_rlimit_cpu(struct task_struct *task, unsigned long rlim_new)
+void update_rlimit_cpu_ns(struct task_struct *task, unsigned long rlim_new)
 {
-       u64 nsecs = rlim_new * NSEC_PER_SEC;
+       u64 nsecs = rlim_new;
 
        spin_lock_irq(&task->sighand->siglock);
        set_process_cpu_timer(task, CPUCLOCK_PROF, &nsecs, NULL);
        spin_unlock_irq(&task->sighand->siglock);
 }
 
+/* Same function for RLIMIT_CPU */
+void update_rlimit_cpu(struct task_struct *task, unsigned long rlim_new)
+{
+       update_rlimit_cpu(task, rlim_new * NSEC_PER_SEC);
+}
+
 static int check_clock(const clockid_t which_clock)
 {
        int error = 0;
@@ -938,6 +944,9 @@ static void check_process_timers(struct task_struct *tsk,
                         SIGPROF);
        check_cpu_itimer(tsk, &sig->it[CPUCLOCK_VIRT], &virt_expires, utime,
                         SIGVTALRM);
+       /*
+        * RLIMIT_CPU check
+        */
        soft = READ_ONCE(sig->rlim[RLIMIT_CPU].rlim_cur);
        if (soft != RLIM_INFINITY) {
                unsigned long psecs = div_u64(ptime, NSEC_PER_SEC);
@@ -974,7 +983,35 @@ static void check_process_timers(struct task_struct *tsk,
                if (!prof_expires || x < prof_expires)
                        prof_expires = x;
        }
-
+       /*
+        * RLIMIT_CPUNS check
+        */
+       soft = READ_ONCE(sig->rlim[RLIMIT_CPUNS].rlim_cur);
+       if (soft != RLIM_INFINITY) {
+               unsigned long hard =
+                       READ_ONCE(sig->rlim[RLIMIT_CPUNS].rlim_max);
+               if (ptime >= hard) {
+                       if (print_fatal_signals) {
+                               pr_info("RT Watchdog Timeout (hard): %s[%d]\n",
+                                       tsk->comm, task_pid_nr(tsk));
+                       }
+                       __group_send_sig_info(SIGKILL, SEND_SIG_PRIV, tsk);
+                       return;
+               }
+               if (ptime >= soft) {
+                       if (print_fatal_signals) {
+                               pr_info("CPU Watchdog Timeout (soft): %s[%d]\n",
+                                       tsk->comm, task_pid_nr(tsk));
+                       }
+                       __group_send_sig_info(SIGXCPU, SEND_SIG_PRIV, tsk);
+                       if (soft < hard) {
+                               soft += NSEC_PER_SEC;
+                               sig->rlim[RLIMIT_CPUNS].rlim_cur = soft;
+                       }
+               }
+               if (!prof_expires || soft < prof_expires)
+                       prof_expires = soft;
+       }
        sig->cputime_expires.prof_exp = prof_expires;
        sig->cputime_expires.virt_exp = virt_expires;
        sig->cputime_expires.sched_exp = sched_expires;
-- 
2.7.4

Reply via email to