From a21e483b57c8c31beaa5063268ec35da375daf04 Mon Sep 17 00:00:00 2001 From: Bruce Ashfield Date: Tue, 12 Jul 2011 10:26:50 -0400 Subject: [PATCH] uptime: allow the optional limiting of kernel runtime Introduce the ability to limit the limit the uptime of a kernel. When enabled, these options set a maximum uptime on the kernel, and (optionally) trigger a clean reboot at expiration. This functionality may appear to be very close to the softdog watchdog implementation. It is. But can't be the softdog for several reasons: - The soft watchdog should be available while this functionality is active - The duration range is different between this and the softdog. The timeout available here is potentially quite a bit longer. - At expiration, there are different expiration requirements and actions. - This functionality is specific to a particular use case and should not impact mainline functionality To cleanly restart the kernel after one minute of uptime, the following config items would be required: CONFIG_UPTIME_LIMITED_KERNEL=y CONFIG_UPTIME_LIMIT_DURATION=1 CONFIG_UPTIME_LIMIT_KERNEL_REBOOT=y Signed-off-by: Bruce Ashfield diff --git a/init/Kconfig b/init/Kconfig index cac3f096050d..77d6d5fa1b1d 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1376,6 +1376,31 @@ menuconfig EXPERT environments which can tolerate a "non-standard" kernel. Only use this if you really know what you are doing. +config UPTIME_LIMITED_KERNEL + bool "Create a kernel with uptime limitations" + default n + help + Limit the amount of time a kernel can run. The associated UPTIME_LIMIT* + kernel config options should be used to tune the behaviour. + +config UPTIME_LIMIT_DURATION + int "Kernel uptime limit in minutes" + depends on UPTIME_LIMITED_KERNEL + range 0 14400 + default 0 + help + Define the uptime limitation on a kernel in minutes. Once + the defined time expires the kernel will emit a warning, cease + to be usable and eventually restart. The valid range is 0 (disable) + to 14400 (10 days) + +config UPTIME_LIMIT_KERNEL_REBOOT + bool "Reboot a time limited kernel at expiration" + depends on UPTIME_LIMITED_KERNEL + default y + help + Reboot an uptime limited kernel at expiration. + config UID16 bool "Enable 16-bit UID system calls" if EXPERT depends on HAVE_UID16 && MULTIUSER diff --git a/kernel/Makefile b/kernel/Makefile index e2ec54e2b952..6b7bdddd624b 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -45,6 +45,7 @@ obj-$(CONFIG_FREEZER) += freezer.o obj-$(CONFIG_PROFILING) += profile.o obj-$(CONFIG_STACKTRACE) += stacktrace.o obj-y += time/ +obj-$(CONFIG_UPTIME_LIMITED_KERNEL) += uptime_limit.o obj-$(CONFIG_FUTEX) += futex.o ifeq ($(CONFIG_COMPAT),y) obj-$(CONFIG_FUTEX) += futex_compat.o diff --git a/kernel/uptime_limit.c b/kernel/uptime_limit.c new file mode 100644 index 000000000000..b6a1a5e4f9d9 --- /dev/null +++ b/kernel/uptime_limit.c @@ -0,0 +1,166 @@ +/* + * uptime_limit.c + * + * This file contains the functions which can limit kernel uptime + * + * Copyright (C) 2011 Bruce Ashfield (bruce.ashfield@windriver.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * This functionality is somewhat close to the softdog watchdog + * implementation, but it cannot be used directly for several reasons: + * + * - The soft watchdog should be available while this functionality is active + * - The duration range is different between this and the softdog. The + * timeout available here is potentially quite a bit longer. + * - At expiration, there are different expiration requirements and actions. + * - This functionality is specific to a particular use case and should + * not impact mainline functionality + * + */ +#include +#include +#include +#include +#include + +#define UPTIME_LIMIT_IN_SECONDS (CONFIG_UPTIME_LIMIT_DURATION * 60) +#define MIN(X, Y) ((X) <= (Y) ? (X) : (Y)) +#define TEN_MINUTES_IN_SECONDS 600 + +enum uptime_expiration_type { + uptime_no_action, + uptime_reboot +}; + +static enum uptime_expiration_type uptime_expiration_action = uptime_no_action; +static struct timer_list timelimit_timer; +static struct task_struct *uptime_worker_task; + +static void timelimit_expire(unsigned long timeout_seconds) +{ + char msg[128]; + int msglen = 127; + + if (timeout_seconds) { + if (timeout_seconds >= 60) + snprintf(msg, msglen, + "Uptime: kernel validity duration has %d %s remaining\n", + (int) timeout_seconds / 60, "minute(s)"); + else + snprintf(msg, msglen, + "Uptime: kernel validity duration has %d %s remaining\n", + (int) timeout_seconds, "seconds"); + + printk(KERN_CRIT "%s", msg); + + timelimit_timer.expires = jiffies + timeout_seconds * HZ; + timelimit_timer.data = 0; + add_timer_on(&timelimit_timer, cpumask_first(cpu_online_mask)); + } else { + printk(KERN_CRIT "Uptime: Kernel validity timeout has expired\n"); +#ifdef CONFIG_UPTIME_LIMIT_KERNEL_REBOOT + uptime_expiration_action = uptime_reboot; + wake_up_process(uptime_worker_task); + } +#endif +} + +/* + * This thread starts and then immediately goes to sleep. When it is woken + * up, it carries out the instructions left in uptime_expiration_action. If + * no action was specified it simply goes back to sleep. + */ +static int uptime_worker(void *unused) +{ + set_current_state(TASK_INTERRUPTIBLE); + + while (!kthread_should_stop()) { + schedule(); + + if (kthread_should_stop()) + break; + + if (uptime_expiration_action == uptime_reboot) { + printk(KERN_CRIT "Uptime: restarting machine\n"); + kernel_restart(NULL); + } + + set_current_state(TASK_INTERRUPTIBLE); + } + __set_current_state(TASK_RUNNING); + + return 0; +} + +static int timeout_enable(int cpu) +{ + int err = 0; + int warning_limit; + + /* + * Create an uptime worker thread. This thread is required since the + * safe version of kernel restart cannot be called from a + * non-interruptible context. Which means we cannot call it directly + * from a timer callback. So we arrange for the timer expiration to + * wakeup a thread, which performs the action. + */ + uptime_worker_task = kthread_create(uptime_worker, + (void *)(unsigned long)cpu, + "uptime_worker/%d", cpu); + if (IS_ERR(uptime_worker_task)) { + printk(KERN_ERR "Uptime: task for cpu %i failed\n", cpu); + err = PTR_ERR(uptime_worker_task); + goto out; + } + /* bind to cpu0 to avoid migration and hot plug nastiness */ + kthread_bind(uptime_worker_task, cpu); + wake_up_process(uptime_worker_task); + + /* Create the timer that will wake the uptime thread at expiration */ + init_timer(&timelimit_timer); + timelimit_timer.function = timelimit_expire; + /* + * Fire two timers. One warning timeout and the final timer + * which will carry out the expiration action. The warning timer will + * expire at the minimum of half the original time or ten minutes. + */ + warning_limit = MIN(UPTIME_LIMIT_IN_SECONDS/2, TEN_MINUTES_IN_SECONDS); + timelimit_timer.expires = jiffies + warning_limit * HZ; + timelimit_timer.data = UPTIME_LIMIT_IN_SECONDS - warning_limit; + + add_timer_on(&timelimit_timer, cpumask_first(cpu_online_mask)); +out: + return err; +} + +static int __init timelimit_init(void) +{ + int err = 0; + + printk(KERN_INFO "Uptime: system uptime restrictions enabled\n"); + + /* + * Enable the timeout thread for cpu 0 only, assuming that the + * uptime limit is non-zero, to protect against any cpu + * migration issues. + */ + if (UPTIME_LIMIT_IN_SECONDS) + err = timeout_enable(0); + + return err; +} +device_initcall(timelimit_init); -- 2.10.1