aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/watchdog.c
diff options
context:
space:
mode:
authorUlrich Obergfell <uobergfe@redhat.com>2015-04-14 18:44:05 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2015-04-14 19:48:59 -0400
commitef246a216b02c604ff465b9a62bb0d2e1ea183a7 (patch)
tree959d6402bd5e193830fd1c8ee270ec9eb34a5a0f /kernel/watchdog.c
parentf54c2274f5515da6bae779c7340cd0dc69d0dd8d (diff)
watchdog: introduce proc_watchdog_common()
Three of four handlers for the watchdog parameters in /proc/sys/kernel essentially have to do the same thing. if the parameter is being read { return the state of the corresponding bit(s) in 'watchdog_enabled' } else { set/clear the state of the corresponding bit(s) in 'watchdog_enabled' update the run state of the lockup detector(s) } Hence, introduce a common function that can be called by those handlers. The callers pass a 'bit mask' to this function to indicate which bit(s) should be set/cleared in 'watchdog_enabled'. This function handles an uncommon race with watchdog_nmi_enable() where a concurrent update of 'watchdog_enabled' is possible. We use 'cmpxchg' to detect the concurrency. [This avoids introducing a new spinlock or a mutex to synchronize updates of 'watchdog_enabled'. Using the same lock or mutex in watchdog thread context and in system call context needs to be considered carefully because it can make the code prone to deadlock situations in connection with parking/unparking the watchdog threads.] Signed-off-by: Ulrich Obergfell <uobergfe@redhat.com> Signed-off-by: Don Zickus <dzickus@redhat.com> Cc: Ingo Molnar <mingo@elte.hu> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'kernel/watchdog.c')
-rw-r--r--kernel/watchdog.c65
1 files changed, 65 insertions, 0 deletions
diff --git a/kernel/watchdog.c b/kernel/watchdog.c
index 28c833b42124..3600a01c97a9 100644
--- a/kernel/watchdog.c
+++ b/kernel/watchdog.c
@@ -704,6 +704,71 @@ static int proc_watchdog_update(void)
704static DEFINE_MUTEX(watchdog_proc_mutex); 704static DEFINE_MUTEX(watchdog_proc_mutex);
705 705
706/* 706/*
707 * common function for watchdog, nmi_watchdog and soft_watchdog parameter
708 *
709 * caller | table->data points to | 'which' contains the flag(s)
710 * -------------------|-----------------------|-----------------------------
711 * proc_watchdog | watchdog_user_enabled | NMI_WATCHDOG_ENABLED or'ed
712 * | | with SOFT_WATCHDOG_ENABLED
713 * -------------------|-----------------------|-----------------------------
714 * proc_nmi_watchdog | nmi_watchdog_enabled | NMI_WATCHDOG_ENABLED
715 * -------------------|-----------------------|-----------------------------
716 * proc_soft_watchdog | soft_watchdog_enabled | SOFT_WATCHDOG_ENABLED
717 */
718static int proc_watchdog_common(int which, struct ctl_table *table, int write,
719 void __user *buffer, size_t *lenp, loff_t *ppos)
720{
721 int err, old, new;
722 int *watchdog_param = (int *)table->data;
723
724 mutex_lock(&watchdog_proc_mutex);
725
726 /*
727 * If the parameter is being read return the state of the corresponding
728 * bit(s) in 'watchdog_enabled', else update 'watchdog_enabled' and the
729 * run state of the lockup detectors.
730 */
731 if (!write) {
732 *watchdog_param = (watchdog_enabled & which) != 0;
733 err = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
734 } else {
735 err = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
736 if (err)
737 goto out;
738
739 /*
740 * There is a race window between fetching the current value
741 * from 'watchdog_enabled' and storing the new value. During
742 * this race window, watchdog_nmi_enable() can sneak in and
743 * clear the NMI_WATCHDOG_ENABLED bit in 'watchdog_enabled'.
744 * The 'cmpxchg' detects this race and the loop retries.
745 */
746 do {
747 old = watchdog_enabled;
748 /*
749 * If the parameter value is not zero set the
750 * corresponding bit(s), else clear it(them).
751 */
752 if (*watchdog_param)
753 new = old | which;
754 else
755 new = old & ~which;
756 } while (cmpxchg(&watchdog_enabled, old, new) != old);
757
758 /*
759 * Update the run state of the lockup detectors.
760 * Restore 'watchdog_enabled' on failure.
761 */
762 err = proc_watchdog_update();
763 if (err)
764 watchdog_enabled = old;
765 }
766out:
767 mutex_unlock(&watchdog_proc_mutex);
768 return err;
769}
770
771/*
707 * proc handler for /proc/sys/kernel/nmi_watchdog,watchdog_thresh 772 * proc handler for /proc/sys/kernel/nmi_watchdog,watchdog_thresh
708 */ 773 */
709 774