Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

rtc: cmos: avoid taking rtc_lock for extended period of time

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>

authored by

Dmitry Torokhov and committed by
Alexandre Belloni
0a6efab3 d4488377

+18 -19
+18 -19
drivers/rtc/rtc-cmos.c
··· 645 645 unsigned char *buf = val; 646 646 647 647 off += NVRAM_OFFSET; 648 - spin_lock_irq(&rtc_lock); 649 - for (; count; count--, off++) { 648 + for (; count; count--, off++, buf++) { 649 + guard(spinlock_irq)(&rtc_lock); 650 650 if (off < 128) 651 - *buf++ = CMOS_READ(off); 651 + *buf = CMOS_READ(off); 652 652 else if (can_bank2) 653 - *buf++ = cmos_read_bank2(off); 653 + *buf = cmos_read_bank2(off); 654 654 else 655 - break; 655 + return -EIO; 656 656 } 657 - spin_unlock_irq(&rtc_lock); 658 657 659 - return count ? -EIO : 0; 658 + return 0; 660 659 } 661 660 662 661 static int cmos_nvram_write(void *priv, unsigned int off, void *val, ··· 670 671 * NVRAM to update, updating checksums is also part of its job. 671 672 */ 672 673 off += NVRAM_OFFSET; 673 - spin_lock_irq(&rtc_lock); 674 - for (; count; count--, off++) { 674 + for (; count; count--, off++, buf++) { 675 675 /* don't trash RTC registers */ 676 676 if (off == cmos->day_alrm 677 677 || off == cmos->mon_alrm 678 678 || off == cmos->century) 679 - buf++; 680 - else if (off < 128) 681 - CMOS_WRITE(*buf++, off); 682 - else if (can_bank2) 683 - cmos_write_bank2(*buf++, off); 684 - else 685 - break; 686 - } 687 - spin_unlock_irq(&rtc_lock); 679 + continue; 688 680 689 - return count ? -EIO : 0; 681 + guard(spinlock_irq)(&rtc_lock); 682 + if (off < 128) 683 + CMOS_WRITE(*buf, off); 684 + else if (can_bank2) 685 + cmos_write_bank2(*buf, off); 686 + else 687 + return -EIO; 688 + } 689 + 690 + return 0; 690 691 } 691 692 692 693 /*----------------------------------------------------------------*/