]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
rtc: cmos: avoid taking rtc_lock for extended period of time
authorDmitry Torokhov <dmitry.torokhov@gmail.com>
Fri, 25 Oct 2024 20:14:57 +0000 (13:14 -0700)
committerAlexandre Belloni <alexandre.belloni@bootlin.com>
Thu, 31 Oct 2024 23:19:22 +0000 (00:19 +0100)
On my device reading entirety of /sys/devices/pnp0/00:03/cmos_nvram0/nvmem
takes about 9 msec during which time interrupts are off on the CPU that
does the read and the thread that performs the read can not be migrated
or preempted by another higher priority thread (RT or not).

Allow readers and writers be preempted by taking and releasing rtc_lock
spinlock for each individual byte read or written rather than once per
read/write request.

Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Reviewed-by: Mateusz Jończyk <mat.jonczyk@o2.pl>
Link: https://lore.kernel.org/r/Zxv8QWR21AV4ztC5@google.com
Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
drivers/rtc/rtc-cmos.c

index 35dca2accbb8df737f0a15ba17d6e8a4b6dcd2dd..5849d2970bba454eb0caa1eee62077209ace9bf6 100644 (file)
@@ -645,18 +645,17 @@ static int cmos_nvram_read(void *priv, unsigned int off, void *val,
        unsigned char *buf = val;
 
        off += NVRAM_OFFSET;
-       spin_lock_irq(&rtc_lock);
-       for (; count; count--, off++) {
+       for (; count; count--, off++, buf++) {
+               guard(spinlock_irq)(&rtc_lock);
                if (off < 128)
-                       *buf++ = CMOS_READ(off);
+                       *buf = CMOS_READ(off);
                else if (can_bank2)
-                       *buf++ = cmos_read_bank2(off);
+                       *buf = cmos_read_bank2(off);
                else
-                       break;
+                       return -EIO;
        }
-       spin_unlock_irq(&rtc_lock);
 
-       return count ? -EIO : 0;
+       return 0;
 }
 
 static int cmos_nvram_write(void *priv, unsigned int off, void *val,
@@ -671,23 +670,23 @@ static int cmos_nvram_write(void *priv, unsigned int off, void *val,
         * NVRAM to update, updating checksums is also part of its job.
         */
        off += NVRAM_OFFSET;
-       spin_lock_irq(&rtc_lock);
-       for (; count; count--, off++) {
+       for (; count; count--, off++, buf++) {
                /* don't trash RTC registers */
                if (off == cmos->day_alrm
                                || off == cmos->mon_alrm
                                || off == cmos->century)
-                       buf++;
-               else if (off < 128)
-                       CMOS_WRITE(*buf++, off);
+                       continue;
+
+               guard(spinlock_irq)(&rtc_lock);
+               if (off < 128)
+                       CMOS_WRITE(*buf, off);
                else if (can_bank2)
-                       cmos_write_bank2(*buf++, off);
+                       cmos_write_bank2(*buf, off);
                else
-                       break;
+                       return -EIO;
        }
-       spin_unlock_irq(&rtc_lock);
 
-       return count ? -EIO : 0;
+       return 0;
 }
 
 /*----------------------------------------------------------------*/