From: Corey Minyard <minyard@acm.org>
To: lkml <linux-kernel@vger.kernel.org>
Subject: Re: Race betwen the NMI handler and the RTC clock in practially all kernels
Date: Tue, 26 Oct 2004 08:56:46 -0500 [thread overview]
Message-ID: <417E579E.3090207@acm.org> (raw)
In-Reply-To: <417D2305.3020209@acm.org>
[-- Attachment #1: Type: text/plain, Size: 115 bytes --]
There was a misplaced "+1" in my previous version of this patch (in
lock_cmos()).
Here is a corrected version...
[-- Attachment #2: nmicmos.diff --]
[-- Type: text/plain, Size: 5117 bytes --]
Index: linux-2.6.9-nmifix.diff/arch/i386/kernel/time.c
===================================================================
--- linux-2.6.9-nmifix.diff.orig/arch/i386/kernel/time.c 2004-10-25 08:08:26.000000000 -0500
+++ linux-2.6.9-nmifix.diff/arch/i386/kernel/time.c 2004-10-25 10:18:17.000000000 -0500
@@ -82,6 +82,14 @@
spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED;
+/*
+ * This is a special lock that is owned by the CPU and holds the index
+ * register we are working with. It is required for NMI access to the
+ * CMOS/RTC registers. See include/asm-i386/mc146818rtc.h for details.
+ */
+volatile unsigned long cmos_lock = 0;
+EXPORT_SYMBOL(cmos_lock);
+
spinlock_t i8253_lock = SPIN_LOCK_UNLOCKED;
EXPORT_SYMBOL(i8253_lock);
Index: linux-2.6.9-nmifix.diff/include/asm-i386/mach-default/mach_traps.h
===================================================================
--- linux-2.6.9-nmifix.diff.orig/include/asm-i386/mach-default/mach_traps.h 2004-06-16 00:19:35.000000000 -0500
+++ linux-2.6.9-nmifix.diff/include/asm-i386/mach-default/mach_traps.h 2004-10-25 09:59:44.000000000 -0500
@@ -7,6 +7,8 @@
#ifndef _MACH_TRAPS_H
#define _MACH_TRAPS_H
+#include <asm/mc146818rtc.h>
+
static inline void clear_mem_error(unsigned char reason)
{
reason = (reason & 0xf) | 4;
@@ -20,10 +22,20 @@
static inline void reassert_nmi(void)
{
+ int old_reg = -1;
+
+ if (do_i_have_lock_cmos())
+ old_reg = current_lock_cmos_reg();
+ else
+ lock_cmos(0); /* register doesn't matter here */
outb(0x8f, 0x70);
inb(0x71); /* dummy */
outb(0x0f, 0x70);
inb(0x71); /* dummy */
+ if (old_reg >= 0)
+ outb(old_reg, 0x70);
+ else
+ unlock_cmos();
}
#endif /* !_MACH_TRAPS_H */
Index: linux-2.6.9-nmifix.diff/include/asm-i386/mc146818rtc.h
===================================================================
--- linux-2.6.9-nmifix.diff.orig/include/asm-i386/mc146818rtc.h 2004-06-16 00:19:43.000000000 -0500
+++ linux-2.6.9-nmifix.diff/include/asm-i386/mc146818rtc.h 2004-10-25 13:14:28.000000000 -0500
@@ -5,24 +5,102 @@
#define _ASM_MC146818RTC_H
#include <asm/io.h>
+#include <asm/system.h>
+#include <linux/mc146818rtc.h>
#ifndef RTC_PORT
#define RTC_PORT(x) (0x70 + (x))
#define RTC_ALWAYS_BCD 1 /* RTC operates in binary mode */
#endif
+#ifdef __HAVE_ARCH_CMPXCHG
+/*
+ * This lock provides nmi access to the CMOS/RTC registers. It has some
+ * special properties. It is owned by a CPU and stored the index register
+ * currently being accessed (if owned). The idea here is that it works
+ * like a normal lock (normally). However, in an NMI, the NMI code will
+ * first check to see if it's CPU owns the lock, meaning that the NMI
+ * interrupted during the read/write of the device. If it does, it goes ahead
+ * and performs the access and then restores the index register. If it does
+ * not, it locks normally.
+ *
+ * Note that since we are working with NMIs, we need this lock even in
+ * a non-SMP machine just to mark that the lock is owned.
+ *
+ * This only works with compare-and-swap. There is no other way to
+ * atomically claim the lock and set the owner.
+ */
+extern volatile unsigned long cmos_lock;
+
+/*
+ * All of these below must be called with interrupts off, preempt
+ * disabled, etc.
+ */
+
+static inline void lock_cmos(unsigned char reg)
+{
+ unsigned long new;
+ new = ((smp_processor_id()+1) << 8) | reg;
+ for (;;) {
+ if (cmos_lock)
+ continue;
+ if (__cmpxchg(&cmos_lock, 0, new, sizeof(cmos_lock)) == 0)
+ return;
+ }
+}
+
+static inline void unlock_cmos(void)
+{
+ cmos_lock = 0;
+}
+static inline int do_i_have_lock_cmos(void)
+{
+ return (cmos_lock >> 8) == (smp_processor_id()+1);
+}
+static inline unsigned char current_lock_cmos_reg(void)
+{
+ return cmos_lock & 0xff;
+}
+#define lock_cmos_prefix(reg) \
+ do { \
+ unsigned long cmos_flags; \
+ local_irq_save(cmos_flags); \
+ lock_cmos(reg)
+#define lock_cmos_suffix(reg) \
+ unlock_cmos(); \
+ local_irq_restore(cmos_flags); \
+ } while (0)
+#else
+#define lock_cmos_prefix(reg) do {} while (0)
+#define lock_cmos_suffix(reg) do {} while (0)
+#define lock_cmos(reg)
+#define unlock_cmos()
+#define do_i_have_lock_cmos() 0
+#define current_lock_cmos_reg() 0
+#endif
+
/*
* The yet supported machines all access the RTC index register via
* an ISA port access but the way to access the date register differs ...
+ * Note that these are functions, not defines, to keep the locking
+ * semantics correct even if you do CMOS_WRITE(CMOS_READ(x) | v, x).
*/
-#define CMOS_READ(addr) ({ \
-outb_p((addr),RTC_PORT(0)); \
-inb_p(RTC_PORT(1)); \
-})
-#define CMOS_WRITE(val, addr) ({ \
-outb_p((addr),RTC_PORT(0)); \
-outb_p((val),RTC_PORT(1)); \
-})
+static inline unsigned char CMOS_READ(unsigned char addr)
+{
+ unsigned char val;
+ lock_cmos_prefix(addr);
+ outb_p(addr, RTC_PORT(0));
+ val = inb_p(RTC_PORT(1));
+ lock_cmos_suffix(addr);
+ return val;
+}
+static inline void CMOS_WRITE(unsigned char val, unsigned char addr)
+{
+ lock_cmos_prefix(addr);
+ outb_p(addr, RTC_PORT(0));
+ outb_p(val, RTC_PORT(1));
+ lock_cmos_suffix(addr);
+}
#define RTC_IRQ 8
next prev parent reply other threads:[~2004-10-26 13:56 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2004-10-25 16:00 Race betwen the NMI handler and the RTC clock in practially all kernels Corey Minyard
2004-10-26 13:56 ` Corey Minyard [this message]
[not found] <417D2305.3020209@acm.org.suse.lists.linux.kernel>
2004-10-25 19:44 ` Andi Kleen
2004-10-25 19:50 ` Corey Minyard
2004-10-25 20:14 ` Andi Kleen
2004-10-25 20:15 ` linux-os
2004-10-25 20:07 ` Maciej W. Rozycki
2004-10-25 20:17 ` Andi Kleen
2004-10-25 20:47 ` Maciej W. Rozycki
2004-10-25 20:11 ` linux-os
2004-10-25 20:23 ` Andi Kleen
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=417E579E.3090207@acm.org \
--to=minyard@acm.org \
--cc=linux-kernel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.