From 76c17264c85f370427d256579cb4409f4e3c5d43 Mon Sep 17 00:00:00 2001 From: John Ogness Date: Tue, 12 Feb 2019 15:29:40 +0100 Subject: [PATCH] printk-rb: add prb locking functions Add processor-reentrant spin locking functions. These allow restricting the number of possible contexts to 2, which can simplify implementing code that also supports NMI interruptions. prb_lock(); /* * This code is synchronized with all contexts * except an NMI on the same processor. */ prb_unlock(); In order to support printk's emergency messages, a processor-reentrant spin lock will be used to control raw access to the emergency console. However, it must be the same processor-reentrant spin lock as the one used by the ring buffer, otherwise a deadlock can occur: CPU1: printk lock -> emergency -> serial lock CPU2: serial lock -> printk lock By making the processor-reentrant implemtation available externally, printk can use the same atomic_t for the ring buffer as for the emergency console and thus avoid the above deadlock. Signed-off-by: John Ogness Signed-off-by: Sebastian Andrzej Siewior --- include/linux/printk_ringbuffer.h | 24 ++++++++++ lib/Makefile | 2 +- lib/printk_ringbuffer.c | 77 +++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 include/linux/printk_ringbuffer.h create mode 100644 lib/printk_ringbuffer.c diff --git a/include/linux/printk_ringbuffer.h b/include/linux/printk_ringbuffer.h new file mode 100644 index 000000000000..75f5708ea902 --- /dev/null +++ b/include/linux/printk_ringbuffer.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_PRINTK_RINGBUFFER_H +#define _LINUX_PRINTK_RINGBUFFER_H + +#include +#include + +struct prb_cpulock { + atomic_t owner; + unsigned long __percpu *irqflags; +}; + +#define DECLARE_STATIC_PRINTKRB_CPULOCK(name) \ +static DEFINE_PER_CPU(unsigned long, _##name##_percpu_irqflags); \ +static struct prb_cpulock name = { \ + .owner = ATOMIC_INIT(-1), \ + .irqflags = &_##name##_percpu_irqflags, \ +} + +/* utility functions */ +void prb_lock(struct prb_cpulock *cpu_lock, unsigned int *cpu_store); +void prb_unlock(struct prb_cpulock *cpu_lock, unsigned int cpu_store); + +#endif /*_LINUX_PRINTK_RINGBUFFER_H */ diff --git a/lib/Makefile b/lib/Makefile index 3b8977aed1b4..e28335bfc63c 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -26,7 +26,7 @@ endif lib-y := ctype.o string.o vsprintf.o cmdline.o \ rbtree.o radix-tree.o timerqueue.o xarray.o \ - idr.o extable.o \ + idr.o extable.o printk_ringbuffer.o \ sha1.o chacha.o irq_regs.o argv_split.o \ flex_proportions.o ratelimit.o show_mem.o \ is_single_threaded.o plist.o decompress.o kobject_uevent.o \ diff --git a/lib/printk_ringbuffer.c b/lib/printk_ringbuffer.c new file mode 100644 index 000000000000..28958b0cf774 --- /dev/null +++ b/lib/printk_ringbuffer.c @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include + +static bool __prb_trylock(struct prb_cpulock *cpu_lock, + unsigned int *cpu_store) +{ + unsigned long *flags; + unsigned int cpu; + + cpu = get_cpu(); + + *cpu_store = atomic_read(&cpu_lock->owner); + /* memory barrier to ensure the current lock owner is visible */ + smp_rmb(); + if (*cpu_store == -1) { + flags = per_cpu_ptr(cpu_lock->irqflags, cpu); + local_irq_save(*flags); + if (atomic_try_cmpxchg_acquire(&cpu_lock->owner, + cpu_store, cpu)) { + return true; + } + local_irq_restore(*flags); + } else if (*cpu_store == cpu) { + return true; + } + + put_cpu(); + return false; +} + +/* + * prb_lock: Perform a processor-reentrant spin lock. + * @cpu_lock: A pointer to the lock object. + * @cpu_store: A "flags" pointer to store lock status information. + * + * If no processor has the lock, the calling processor takes the lock and + * becomes the owner. If the calling processor is already the owner of the + * lock, this function succeeds immediately. If lock is locked by another + * processor, this function spins until the calling processor becomes the + * owner. + * + * It is safe to call this function from any context and state. + */ +void prb_lock(struct prb_cpulock *cpu_lock, unsigned int *cpu_store) +{ + for (;;) { + if (__prb_trylock(cpu_lock, cpu_store)) + break; + cpu_relax(); + } +} + +/* + * prb_unlock: Perform a processor-reentrant spin unlock. + * @cpu_lock: A pointer to the lock object. + * @cpu_store: A "flags" object storing lock status information. + * + * Release the lock. The calling processor must be the owner of the lock. + * + * It is safe to call this function from any context and state. + */ +void prb_unlock(struct prb_cpulock *cpu_lock, unsigned int cpu_store) +{ + unsigned long *flags; + unsigned int cpu; + + cpu = atomic_read(&cpu_lock->owner); + atomic_set_release(&cpu_lock->owner, cpu_store); + + if (cpu_store == -1) { + flags = per_cpu_ptr(cpu_lock->irqflags, cpu); + local_irq_restore(*flags); + } + + put_cpu(); +}