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

serial: core: Acquire nbcon context in port->lock wrapper

Currently the port->lock wrappers uart_port_lock(),
uart_port_unlock() (and their variants) only lock/unlock
the spin_lock.

If the port is an nbcon console that has implemented the
write_atomic() callback, the wrappers must also acquire/release
the console context and mark the region as unsafe. This allows
general port->lock synchronization to be synchronized against
the nbcon write_atomic() callback.

Note that __uart_port_using_nbcon() relies on the port->lock
being held while a console is added and removed from the
console list (i.e. all uart nbcon drivers *must* take the
port->lock in their device_lock() callbacks).

Signed-off-by: John Ogness <john.ogness@linutronix.de>
Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Reviewed-by: Petr Mladek <pmladek@suse.com>
Link: https://lore.kernel.org/r/20240820063001.36405-15-john.ogness@linutronix.de
Signed-off-by: Petr Mladek <pmladek@suse.com>

authored by

John Ogness and committed by
Petr Mladek
4126f149 adf6f37d

+80 -2
+80 -2
include/linux/serial_core.h
··· 11 11 #include <linux/compiler.h> 12 12 #include <linux/console.h> 13 13 #include <linux/interrupt.h> 14 + #include <linux/lockdep.h> 15 + #include <linux/printk.h> 14 16 #include <linux/spinlock.h> 15 17 #include <linux/sched.h> 16 18 #include <linux/tty.h> ··· 627 625 up->cons = con; 628 626 __uart_port_unlock_irqrestore(up, flags); 629 627 } 628 + 629 + /* Only for internal port lock wrapper usage. */ 630 + static inline bool __uart_port_using_nbcon(struct uart_port *up) 631 + { 632 + lockdep_assert_held_once(&up->lock); 633 + 634 + if (likely(!uart_console(up))) 635 + return false; 636 + 637 + /* 638 + * @up->cons is only modified under the port lock. Therefore it is 639 + * certain that it cannot disappear here. 640 + * 641 + * @up->cons->node is added/removed from the console list under the 642 + * port lock. Therefore it is certain that the registration status 643 + * cannot change here, thus @up->cons->flags can be read directly. 644 + */ 645 + if (hlist_unhashed_lockless(&up->cons->node) || 646 + !(up->cons->flags & CON_NBCON) || 647 + !up->cons->write_atomic) { 648 + return false; 649 + } 650 + 651 + return true; 652 + } 653 + 654 + /* Only for internal port lock wrapper usage. */ 655 + static inline bool __uart_port_nbcon_try_acquire(struct uart_port *up) 656 + { 657 + if (!__uart_port_using_nbcon(up)) 658 + return true; 659 + 660 + return nbcon_device_try_acquire(up->cons); 661 + } 662 + 663 + /* Only for internal port lock wrapper usage. */ 664 + static inline void __uart_port_nbcon_acquire(struct uart_port *up) 665 + { 666 + if (!__uart_port_using_nbcon(up)) 667 + return; 668 + 669 + while (!nbcon_device_try_acquire(up->cons)) 670 + cpu_relax(); 671 + } 672 + 673 + /* Only for internal port lock wrapper usage. */ 674 + static inline void __uart_port_nbcon_release(struct uart_port *up) 675 + { 676 + if (!__uart_port_using_nbcon(up)) 677 + return; 678 + 679 + nbcon_device_release(up->cons); 680 + } 681 + 630 682 /** 631 683 * uart_port_lock - Lock the UART port 632 684 * @up: Pointer to UART port structure ··· 688 632 static inline void uart_port_lock(struct uart_port *up) 689 633 { 690 634 spin_lock(&up->lock); 635 + __uart_port_nbcon_acquire(up); 691 636 } 692 637 693 638 /** ··· 698 641 static inline void uart_port_lock_irq(struct uart_port *up) 699 642 { 700 643 spin_lock_irq(&up->lock); 644 + __uart_port_nbcon_acquire(up); 701 645 } 702 646 703 647 /** ··· 709 651 static inline void uart_port_lock_irqsave(struct uart_port *up, unsigned long *flags) 710 652 { 711 653 spin_lock_irqsave(&up->lock, *flags); 654 + __uart_port_nbcon_acquire(up); 712 655 } 713 656 714 657 /** ··· 720 661 */ 721 662 static inline bool uart_port_trylock(struct uart_port *up) 722 663 { 723 - return spin_trylock(&up->lock); 664 + if (!spin_trylock(&up->lock)) 665 + return false; 666 + 667 + if (!__uart_port_nbcon_try_acquire(up)) { 668 + spin_unlock(&up->lock); 669 + return false; 670 + } 671 + 672 + return true; 724 673 } 725 674 726 675 /** ··· 740 673 */ 741 674 static inline bool uart_port_trylock_irqsave(struct uart_port *up, unsigned long *flags) 742 675 { 743 - return spin_trylock_irqsave(&up->lock, *flags); 676 + if (!spin_trylock_irqsave(&up->lock, *flags)) 677 + return false; 678 + 679 + if (!__uart_port_nbcon_try_acquire(up)) { 680 + spin_unlock_irqrestore(&up->lock, *flags); 681 + return false; 682 + } 683 + 684 + return true; 744 685 } 745 686 746 687 /** ··· 757 682 */ 758 683 static inline void uart_port_unlock(struct uart_port *up) 759 684 { 685 + __uart_port_nbcon_release(up); 760 686 spin_unlock(&up->lock); 761 687 } 762 688 ··· 767 691 */ 768 692 static inline void uart_port_unlock_irq(struct uart_port *up) 769 693 { 694 + __uart_port_nbcon_release(up); 770 695 spin_unlock_irq(&up->lock); 771 696 } 772 697 ··· 778 701 */ 779 702 static inline void uart_port_unlock_irqrestore(struct uart_port *up, unsigned long flags) 780 703 { 704 + __uart_port_nbcon_release(up); 781 705 spin_unlock_irqrestore(&up->lock, flags); 782 706 } 783 707