Appart from Andrew said about the usecase make sure to CC linux-api for
any user visible interface change.

On Thu 17-08-17 22:45:45, Григорий Резников wrote:
> 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

-- 
Michal Hocko
SUSE Labs

Reply via email to