+static int enable_nmi_watchdog(int cpu)
+{
+ struct perf_event *event;
+
+ event = per_cpu(nmi_watchdog_ev, cpu);
+ if (event && event->state > PERF_EVENT_STATE_OFF)
+ return 0;
+
+ if (event == NULL) {
+ /* Try to register using hardware perf events first */
+ wd_attr.sample_period = hw_nmi_get_sample_period();
+ event = perf_event_create_kernel_counter(&wd_attr, cpu, -1, wd_overflow);
+ if (IS_ERR(event)) {
+ wd_attr.type = PERF_TYPE_SOFTWARE;
+ event = perf_event_create_kernel_counter(&wd_attr, cpu, -1, wd_overflow);
+ if (IS_ERR(event)) {
+ printk(KERN_ERR "nmi watchdog failed to create perf event on %i: %p\n", cpu, event);
+ return -1;
+ }
+ }
+ per_cpu(nmi_watchdog_ev, cpu) = event;
+ }
+ perf_event_enable(per_cpu(nmi_watchdog_ev, cpu));
+ return 0;
+}
+
+static void disable_nmi_watchdog(int cpu)
+{
+ struct perf_event *event;
+
+ event = per_cpu(nmi_watchdog_ev, cpu);
+ if (event) {
+ perf_event_disable(per_cpu(nmi_watchdog_ev, cpu));
+ per_cpu(nmi_watchdog_ev, cpu) = NULL;
+ perf_event_release_kernel(event);
+ }
+}
+
+#ifdef CONFIG_SYSCTL
+/*
+ * proc handler for /proc/sys/kernel/nmi_watchdog
+ */
+int nmi_watchdog_enabled;
+
+int proc_nmi_enabled(struct ctl_table *table, int write,
+ void __user *buffer, size_t *length, loff_t *ppos)
+{
+ int cpu;
+
+ if (!write) {
+ struct perf_event *event;
+ for_each_online_cpu(cpu) {
+ event = per_cpu(nmi_watchdog_ev, cpu);
+ if (event && event->state > PERF_EVENT_STATE_OFF) {
+ nmi_watchdog_enabled = 1;
+ break;
+ }
+ }
+ proc_dointvec(table, write, buffer, length, ppos);
+ return 0;
+ }
+
+ touch_all_nmi_watchdog();
+ proc_dointvec(table, write, buffer, length, ppos);
+ if (nmi_watchdog_enabled) {
+ for_each_online_cpu(cpu)
+ if (enable_nmi_watchdog(cpu)) {
+ printk("NMI watchdog failed configuration, "
+ " can not be enabled\n");
+ }
+ } else {
+ for_each_online_cpu(cpu)
+ disable_nmi_watchdog(cpu);
+ }
+ return 0;
+}
+
+#endif /* CONFIG_SYSCTL */
+