mirror of
https://github.com/OpenVoiceOS/OpenVoiceOS
synced 2025-02-12 18:00:45 +01:00
713 lines
23 KiB
Diff
713 lines
23 KiB
Diff
From 48184a3bcad14f6c1a9813ab9dd376f02e540463 Mon Sep 17 00:00:00 2001
|
|
From: Thomas Gleixner <tglx@linutronix.de>
|
|
Date: Sat, 16 Sep 2023 21:26:01 +0206
|
|
Subject: [PATCH 103/195] printk: nbcon: Add acquire/release logic
|
|
|
|
Add per console acquire/release functionality.
|
|
|
|
The state of the console is maintained in the "nbcon_state" atomic
|
|
variable.
|
|
|
|
The console is locked when:
|
|
|
|
- The 'prio' field contains the priority of the context that owns the
|
|
console. Only higher priority contexts are allowed to take over the
|
|
lock. A value of 0 (NBCON_PRIO_NONE) means the console is not locked.
|
|
|
|
- The 'cpu' field denotes on which CPU the console is locked. It is used
|
|
to prevent busy waiting on the same CPU. Also it informs the lock owner
|
|
that it has lost the lock in a more complex scenario when the lock was
|
|
taken over by a higher priority context, released, and taken on another
|
|
CPU with the same priority as the interrupted owner.
|
|
|
|
The acquire mechanism uses a few more fields:
|
|
|
|
- The 'req_prio' field is used by the handover approach to make the
|
|
current owner aware that there is a context with a higher priority
|
|
waiting for the friendly handover.
|
|
|
|
- The 'unsafe' field allows to take over the console in a safe way in the
|
|
middle of emitting a message. The field is set only when accessing some
|
|
shared resources or when the console device is manipulated. It can be
|
|
cleared, for example, after emitting one character when the console
|
|
device is in a consistent state.
|
|
|
|
- The 'unsafe_takeover' field is set when a hostile takeover took the
|
|
console in an unsafe state. The console will stay in the unsafe state
|
|
until re-initialized.
|
|
|
|
The acquire mechanism uses three approaches:
|
|
|
|
1) Direct acquire when the console is not owned or is owned by a lower
|
|
priority context and is in a safe state.
|
|
|
|
2) Friendly handover mechanism uses a request/grant handshake. It is used
|
|
when the current owner has lower priority and the console is in an
|
|
unsafe state.
|
|
|
|
The requesting context:
|
|
|
|
a) Sets its priority into the 'req_prio' field.
|
|
|
|
b) Waits (with a timeout) for the owning context to unlock the
|
|
console.
|
|
|
|
c) Takes the lock and clears the 'req_prio' field.
|
|
|
|
The owning context:
|
|
|
|
a) Observes the 'req_prio' field set on exit from the unsafe
|
|
console state.
|
|
|
|
b) Gives up console ownership by clearing the 'prio' field.
|
|
|
|
3) Unsafe hostile takeover allows to take over the lock even when the
|
|
console is an unsafe state. It is used only in panic() by the final
|
|
attempt to flush consoles in a try and hope mode.
|
|
|
|
Note that separate record buffers are used in panic(). As a result,
|
|
the messages can be read and formatted without any risk even after
|
|
using the hostile takeover in unsafe state.
|
|
|
|
The release function simply clears the 'prio' field.
|
|
|
|
All operations on @console::nbcon_state are atomic cmpxchg based to
|
|
handle concurrency.
|
|
|
|
The acquire/release functions implement only minimal policies:
|
|
|
|
- Preference for higher priority contexts.
|
|
- Protection of the panic CPU.
|
|
|
|
All other policy decisions must be made at the call sites:
|
|
|
|
- What is marked as an unsafe section.
|
|
- Whether to spin-wait if there is already an owner and the console is
|
|
in an unsafe state.
|
|
- Whether to attempt an unsafe hostile takeover.
|
|
|
|
The design allows to implement the well known:
|
|
|
|
acquire()
|
|
output_one_printk_record()
|
|
release()
|
|
|
|
The output of one printk record might be interrupted with a higher priority
|
|
context. The new owner is supposed to reprint the entire interrupted record
|
|
from scratch.
|
|
|
|
Co-developed-by: John Ogness <john.ogness@linutronix.de>
|
|
Signed-off-by: John Ogness <john.ogness@linutronix.de>
|
|
Signed-off-by: Thomas Gleixner (Intel) <tglx@linutronix.de>
|
|
Signed-off-by: Petr Mladek <pmladek@suse.com>
|
|
Link: https://lore.kernel.org/r/20230916192007.608398-3-john.ogness@linutronix.de
|
|
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
|
---
|
|
include/linux/console.h | 56 +++++
|
|
kernel/printk/nbcon.c | 497 ++++++++++++++++++++++++++++++++++++++++
|
|
2 files changed, 553 insertions(+)
|
|
|
|
diff --git a/include/linux/console.h b/include/linux/console.h
|
|
index a2d37a7a98a8..98210fd01f18 100644
|
|
--- a/include/linux/console.h
|
|
+++ b/include/linux/console.h
|
|
@@ -175,13 +175,29 @@ enum cons_flags {
|
|
* struct nbcon_state - console state for nbcon consoles
|
|
* @atom: Compound of the state fields for atomic operations
|
|
*
|
|
+ * @req_prio: The priority of a handover request
|
|
+ * @prio: The priority of the current owner
|
|
+ * @unsafe: Console is busy in a non takeover region
|
|
+ * @unsafe_takeover: A hostile takeover in an unsafe state happened in the
|
|
+ * past. The console cannot be safe until re-initialized.
|
|
+ * @cpu: The CPU on which the owner runs
|
|
+ *
|
|
* To be used for reading and preparing of the value stored in the nbcon
|
|
* state variable @console::nbcon_state.
|
|
+ *
|
|
+ * The @prio and @req_prio fields are particularly important to allow
|
|
+ * spin-waiting to timeout and give up without the risk of a waiter being
|
|
+ * assigned the lock after giving up.
|
|
*/
|
|
struct nbcon_state {
|
|
union {
|
|
unsigned int atom;
|
|
struct {
|
|
+ unsigned int prio : 2;
|
|
+ unsigned int req_prio : 2;
|
|
+ unsigned int unsafe : 1;
|
|
+ unsigned int unsafe_takeover : 1;
|
|
+ unsigned int cpu : 24;
|
|
};
|
|
};
|
|
};
|
|
@@ -194,6 +210,46 @@ struct nbcon_state {
|
|
*/
|
|
static_assert(sizeof(struct nbcon_state) <= sizeof(int));
|
|
|
|
+/**
|
|
+ * nbcon_prio - console owner priority for nbcon consoles
|
|
+ * @NBCON_PRIO_NONE: Unused
|
|
+ * @NBCON_PRIO_NORMAL: Normal (non-emergency) usage
|
|
+ * @NBCON_PRIO_EMERGENCY: Emergency output (WARN/OOPS...)
|
|
+ * @NBCON_PRIO_PANIC: Panic output
|
|
+ * @NBCON_PRIO_MAX: The number of priority levels
|
|
+ *
|
|
+ * A higher priority context can takeover the console when it is
|
|
+ * in the safe state. The final attempt to flush consoles in panic()
|
|
+ * can be allowed to do so even in an unsafe state (Hope and pray).
|
|
+ */
|
|
+enum nbcon_prio {
|
|
+ NBCON_PRIO_NONE = 0,
|
|
+ NBCON_PRIO_NORMAL,
|
|
+ NBCON_PRIO_EMERGENCY,
|
|
+ NBCON_PRIO_PANIC,
|
|
+ NBCON_PRIO_MAX,
|
|
+};
|
|
+
|
|
+struct console;
|
|
+
|
|
+/**
|
|
+ * struct nbcon_context - Context for console acquire/release
|
|
+ * @console: The associated console
|
|
+ * @spinwait_max_us: Limit for spin-wait acquire
|
|
+ * @prio: Priority of the context
|
|
+ * @allow_unsafe_takeover: Allow performing takeover even if unsafe. Can
|
|
+ * be used only with NBCON_PRIO_PANIC @prio. It
|
|
+ * might cause a system freeze when the console
|
|
+ * is used later.
|
|
+ */
|
|
+struct nbcon_context {
|
|
+ /* members set by caller */
|
|
+ struct console *console;
|
|
+ unsigned int spinwait_max_us;
|
|
+ enum nbcon_prio prio;
|
|
+ unsigned int allow_unsafe_takeover : 1;
|
|
+};
|
|
+
|
|
/**
|
|
* struct console - The console descriptor structure
|
|
* @name: The name of the console driver
|
|
diff --git a/kernel/printk/nbcon.c b/kernel/printk/nbcon.c
|
|
index 63d24ca62ac5..a2a354f859f9 100644
|
|
--- a/kernel/printk/nbcon.c
|
|
+++ b/kernel/printk/nbcon.c
|
|
@@ -4,10 +4,98 @@
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/console.h>
|
|
+#include <linux/delay.h>
|
|
#include "internal.h"
|
|
/*
|
|
* Printk console printing implementation for consoles which does not depend
|
|
* on the legacy style console_lock mechanism.
|
|
+ *
|
|
+ * The state of the console is maintained in the "nbcon_state" atomic
|
|
+ * variable.
|
|
+ *
|
|
+ * The console is locked when:
|
|
+ *
|
|
+ * - The 'prio' field contains the priority of the context that owns the
|
|
+ * console. Only higher priority contexts are allowed to take over the
|
|
+ * lock. A value of 0 (NBCON_PRIO_NONE) means the console is not locked.
|
|
+ *
|
|
+ * - The 'cpu' field denotes on which CPU the console is locked. It is used
|
|
+ * to prevent busy waiting on the same CPU. Also it informs the lock owner
|
|
+ * that it has lost the lock in a more complex scenario when the lock was
|
|
+ * taken over by a higher priority context, released, and taken on another
|
|
+ * CPU with the same priority as the interrupted owner.
|
|
+ *
|
|
+ * The acquire mechanism uses a few more fields:
|
|
+ *
|
|
+ * - The 'req_prio' field is used by the handover approach to make the
|
|
+ * current owner aware that there is a context with a higher priority
|
|
+ * waiting for the friendly handover.
|
|
+ *
|
|
+ * - The 'unsafe' field allows to take over the console in a safe way in the
|
|
+ * middle of emitting a message. The field is set only when accessing some
|
|
+ * shared resources or when the console device is manipulated. It can be
|
|
+ * cleared, for example, after emitting one character when the console
|
|
+ * device is in a consistent state.
|
|
+ *
|
|
+ * - The 'unsafe_takeover' field is set when a hostile takeover took the
|
|
+ * console in an unsafe state. The console will stay in the unsafe state
|
|
+ * until re-initialized.
|
|
+ *
|
|
+ * The acquire mechanism uses three approaches:
|
|
+ *
|
|
+ * 1) Direct acquire when the console is not owned or is owned by a lower
|
|
+ * priority context and is in a safe state.
|
|
+ *
|
|
+ * 2) Friendly handover mechanism uses a request/grant handshake. It is used
|
|
+ * when the current owner has lower priority and the console is in an
|
|
+ * unsafe state.
|
|
+ *
|
|
+ * The requesting context:
|
|
+ *
|
|
+ * a) Sets its priority into the 'req_prio' field.
|
|
+ *
|
|
+ * b) Waits (with a timeout) for the owning context to unlock the
|
|
+ * console.
|
|
+ *
|
|
+ * c) Takes the lock and clears the 'req_prio' field.
|
|
+ *
|
|
+ * The owning context:
|
|
+ *
|
|
+ * a) Observes the 'req_prio' field set on exit from the unsafe
|
|
+ * console state.
|
|
+ *
|
|
+ * b) Gives up console ownership by clearing the 'prio' field.
|
|
+ *
|
|
+ * 3) Unsafe hostile takeover allows to take over the lock even when the
|
|
+ * console is an unsafe state. It is used only in panic() by the final
|
|
+ * attempt to flush consoles in a try and hope mode.
|
|
+ *
|
|
+ * The release function simply clears the 'prio' field.
|
|
+ *
|
|
+ * All operations on @console::nbcon_state are atomic cmpxchg based to
|
|
+ * handle concurrency.
|
|
+ *
|
|
+ * The acquire/release functions implement only minimal policies:
|
|
+ *
|
|
+ * - Preference for higher priority contexts.
|
|
+ * - Protection of the panic CPU.
|
|
+ *
|
|
+ * All other policy decisions must be made at the call sites:
|
|
+ *
|
|
+ * - What is marked as an unsafe section.
|
|
+ * - Whether to spin-wait if there is already an owner and the console is
|
|
+ * in an unsafe state.
|
|
+ * - Whether to attempt an unsafe hostile takeover.
|
|
+ *
|
|
+ * The design allows to implement the well known:
|
|
+ *
|
|
+ * acquire()
|
|
+ * output_one_printk_record()
|
|
+ * release()
|
|
+ *
|
|
+ * The output of one printk record might be interrupted with a higher priority
|
|
+ * context. The new owner is supposed to reprint the entire interrupted record
|
|
+ * from scratch.
|
|
*/
|
|
|
|
/**
|
|
@@ -47,6 +135,415 @@ static inline bool nbcon_state_try_cmpxchg(struct console *con, struct nbcon_sta
|
|
return atomic_try_cmpxchg(&ACCESS_PRIVATE(con, nbcon_state), &cur->atom, new->atom);
|
|
}
|
|
|
|
+/**
|
|
+ * nbcon_context_try_acquire_direct - Try to acquire directly
|
|
+ * @ctxt: The context of the caller
|
|
+ * @cur: The current console state
|
|
+ *
|
|
+ * Acquire the console when it is released. Also acquire the console when
|
|
+ * the current owner has a lower priority and the console is in a safe state.
|
|
+ *
|
|
+ * Return: 0 on success. Otherwise, an error code on failure. Also @cur
|
|
+ * is updated to the latest state when failed to modify it.
|
|
+ *
|
|
+ * Errors:
|
|
+ *
|
|
+ * -EPERM: A panic is in progress and this is not the panic CPU.
|
|
+ * Or the current owner or waiter has the same or higher
|
|
+ * priority. No acquire method can be successful in
|
|
+ * this case.
|
|
+ *
|
|
+ * -EBUSY: The current owner has a lower priority but the console
|
|
+ * in an unsafe state. The caller should try using
|
|
+ * the handover acquire method.
|
|
+ */
|
|
+static int nbcon_context_try_acquire_direct(struct nbcon_context *ctxt,
|
|
+ struct nbcon_state *cur)
|
|
+{
|
|
+ unsigned int cpu = smp_processor_id();
|
|
+ struct console *con = ctxt->console;
|
|
+ struct nbcon_state new;
|
|
+
|
|
+ do {
|
|
+ if (other_cpu_in_panic())
|
|
+ return -EPERM;
|
|
+
|
|
+ if (ctxt->prio <= cur->prio || ctxt->prio <= cur->req_prio)
|
|
+ return -EPERM;
|
|
+
|
|
+ if (cur->unsafe)
|
|
+ return -EBUSY;
|
|
+
|
|
+ /*
|
|
+ * The console should never be safe for a direct acquire
|
|
+ * if an unsafe hostile takeover has ever happened.
|
|
+ */
|
|
+ WARN_ON_ONCE(cur->unsafe_takeover);
|
|
+
|
|
+ new.atom = cur->atom;
|
|
+ new.prio = ctxt->prio;
|
|
+ new.req_prio = NBCON_PRIO_NONE;
|
|
+ new.unsafe = cur->unsafe_takeover;
|
|
+ new.cpu = cpu;
|
|
+
|
|
+ } while (!nbcon_state_try_cmpxchg(con, cur, &new));
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static bool nbcon_waiter_matches(struct nbcon_state *cur, int expected_prio)
|
|
+{
|
|
+ /*
|
|
+ * The request context is well defined by the @req_prio because:
|
|
+ *
|
|
+ * - Only a context with a higher priority can take over the request.
|
|
+ * - There are only three priorities.
|
|
+ * - Only one CPU is allowed to request PANIC priority.
|
|
+ * - Lower priorities are ignored during panic() until reboot.
|
|
+ *
|
|
+ * As a result, the following scenario is *not* possible:
|
|
+ *
|
|
+ * 1. Another context with a higher priority directly takes ownership.
|
|
+ * 2. The higher priority context releases the ownership.
|
|
+ * 3. A lower priority context takes the ownership.
|
|
+ * 4. Another context with the same priority as this context
|
|
+ * creates a request and starts waiting.
|
|
+ */
|
|
+
|
|
+ return (cur->req_prio == expected_prio);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * nbcon_context_try_acquire_requested - Try to acquire after having
|
|
+ * requested a handover
|
|
+ * @ctxt: The context of the caller
|
|
+ * @cur: The current console state
|
|
+ *
|
|
+ * This is a helper function for nbcon_context_try_acquire_handover().
|
|
+ * It is called when the console is in an unsafe state. The current
|
|
+ * owner will release the console on exit from the unsafe region.
|
|
+ *
|
|
+ * Return: 0 on success and @cur is updated to the new console state.
|
|
+ * Otherwise an error code on failure.
|
|
+ *
|
|
+ * Errors:
|
|
+ *
|
|
+ * -EPERM: A panic is in progress and this is not the panic CPU
|
|
+ * or this context is no longer the waiter.
|
|
+ *
|
|
+ * -EBUSY: The console is still locked. The caller should
|
|
+ * continue waiting.
|
|
+ *
|
|
+ * Note: The caller must still remove the request when an error has occurred
|
|
+ * except when this context is no longer the waiter.
|
|
+ */
|
|
+static int nbcon_context_try_acquire_requested(struct nbcon_context *ctxt,
|
|
+ struct nbcon_state *cur)
|
|
+{
|
|
+ unsigned int cpu = smp_processor_id();
|
|
+ struct console *con = ctxt->console;
|
|
+ struct nbcon_state new;
|
|
+
|
|
+ /* Note that the caller must still remove the request! */
|
|
+ if (other_cpu_in_panic())
|
|
+ return -EPERM;
|
|
+
|
|
+ /*
|
|
+ * Note that the waiter will also change if there was an unsafe
|
|
+ * hostile takeover.
|
|
+ */
|
|
+ if (!nbcon_waiter_matches(cur, ctxt->prio))
|
|
+ return -EPERM;
|
|
+
|
|
+ /* If still locked, caller should continue waiting. */
|
|
+ if (cur->prio != NBCON_PRIO_NONE)
|
|
+ return -EBUSY;
|
|
+
|
|
+ /*
|
|
+ * The previous owner should have never released ownership
|
|
+ * in an unsafe region.
|
|
+ */
|
|
+ WARN_ON_ONCE(cur->unsafe);
|
|
+
|
|
+ new.atom = cur->atom;
|
|
+ new.prio = ctxt->prio;
|
|
+ new.req_prio = NBCON_PRIO_NONE;
|
|
+ new.unsafe = cur->unsafe_takeover;
|
|
+ new.cpu = cpu;
|
|
+
|
|
+ if (!nbcon_state_try_cmpxchg(con, cur, &new)) {
|
|
+ /*
|
|
+ * The acquire could fail only when it has been taken
|
|
+ * over by a higher priority context.
|
|
+ */
|
|
+ WARN_ON_ONCE(nbcon_waiter_matches(cur, ctxt->prio));
|
|
+ return -EPERM;
|
|
+ }
|
|
+
|
|
+ /* Handover success. This context now owns the console. */
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * nbcon_context_try_acquire_handover - Try to acquire via handover
|
|
+ * @ctxt: The context of the caller
|
|
+ * @cur: The current console state
|
|
+ *
|
|
+ * The function must be called only when the context has higher priority
|
|
+ * than the current owner and the console is in an unsafe state.
|
|
+ * It is the case when nbcon_context_try_acquire_direct() returns -EBUSY.
|
|
+ *
|
|
+ * The function sets "req_prio" field to make the current owner aware of
|
|
+ * the request. Then it waits until the current owner releases the console,
|
|
+ * or an even higher context takes over the request, or timeout expires.
|
|
+ *
|
|
+ * The current owner checks the "req_prio" field on exit from the unsafe
|
|
+ * region and releases the console. It does not touch the "req_prio" field
|
|
+ * so that the console stays reserved for the waiter.
|
|
+ *
|
|
+ * Return: 0 on success. Otherwise, an error code on failure. Also @cur
|
|
+ * is updated to the latest state when failed to modify it.
|
|
+ *
|
|
+ * Errors:
|
|
+ *
|
|
+ * -EPERM: A panic is in progress and this is not the panic CPU.
|
|
+ * Or a higher priority context has taken over the
|
|
+ * console or the handover request.
|
|
+ *
|
|
+ * -EBUSY: The current owner is on the same CPU so that the hand
|
|
+ * shake could not work. Or the current owner is not
|
|
+ * willing to wait (zero timeout). Or the console does
|
|
+ * not enter the safe state before timeout passed. The
|
|
+ * caller might still use the unsafe hostile takeover
|
|
+ * when allowed.
|
|
+ *
|
|
+ * -EAGAIN: @cur has changed when creating the handover request.
|
|
+ * The caller should retry with direct acquire.
|
|
+ */
|
|
+static int nbcon_context_try_acquire_handover(struct nbcon_context *ctxt,
|
|
+ struct nbcon_state *cur)
|
|
+{
|
|
+ unsigned int cpu = smp_processor_id();
|
|
+ struct console *con = ctxt->console;
|
|
+ struct nbcon_state new;
|
|
+ int timeout;
|
|
+ int request_err = -EBUSY;
|
|
+
|
|
+ /*
|
|
+ * Check that the handover is called when the direct acquire failed
|
|
+ * with -EBUSY.
|
|
+ */
|
|
+ WARN_ON_ONCE(ctxt->prio <= cur->prio || ctxt->prio <= cur->req_prio);
|
|
+ WARN_ON_ONCE(!cur->unsafe);
|
|
+
|
|
+ /* Handover is not possible on the same CPU. */
|
|
+ if (cur->cpu == cpu)
|
|
+ return -EBUSY;
|
|
+
|
|
+ /*
|
|
+ * Console stays unsafe after an unsafe takeover until re-initialized.
|
|
+ * Waiting is not going to help in this case.
|
|
+ */
|
|
+ if (cur->unsafe_takeover)
|
|
+ return -EBUSY;
|
|
+
|
|
+ /* Is the caller willing to wait? */
|
|
+ if (ctxt->spinwait_max_us == 0)
|
|
+ return -EBUSY;
|
|
+
|
|
+ /*
|
|
+ * Setup a request for the handover. The caller should try to acquire
|
|
+ * the console directly when the current state has been modified.
|
|
+ */
|
|
+ new.atom = cur->atom;
|
|
+ new.req_prio = ctxt->prio;
|
|
+ if (!nbcon_state_try_cmpxchg(con, cur, &new))
|
|
+ return -EAGAIN;
|
|
+
|
|
+ cur->atom = new.atom;
|
|
+
|
|
+ /* Wait until there is no owner and then acquire the console. */
|
|
+ for (timeout = ctxt->spinwait_max_us; timeout >= 0; timeout--) {
|
|
+ /* On successful acquire, this request is cleared. */
|
|
+ request_err = nbcon_context_try_acquire_requested(ctxt, cur);
|
|
+ if (!request_err)
|
|
+ return 0;
|
|
+
|
|
+ /*
|
|
+ * If the acquire should be aborted, it must be ensured
|
|
+ * that the request is removed before returning to caller.
|
|
+ */
|
|
+ if (request_err == -EPERM)
|
|
+ break;
|
|
+
|
|
+ udelay(1);
|
|
+
|
|
+ /* Re-read the state because some time has passed. */
|
|
+ nbcon_state_read(con, cur);
|
|
+ }
|
|
+
|
|
+ /* Timed out or aborted. Carefully remove handover request. */
|
|
+ do {
|
|
+ /*
|
|
+ * No need to remove request if there is a new waiter. This
|
|
+ * can only happen if a higher priority context has taken over
|
|
+ * the console or the handover request.
|
|
+ */
|
|
+ if (!nbcon_waiter_matches(cur, ctxt->prio))
|
|
+ return -EPERM;
|
|
+
|
|
+ /* Unset request for handover. */
|
|
+ new.atom = cur->atom;
|
|
+ new.req_prio = NBCON_PRIO_NONE;
|
|
+ if (nbcon_state_try_cmpxchg(con, cur, &new)) {
|
|
+ /*
|
|
+ * Request successfully unset. Report failure of
|
|
+ * acquiring via handover.
|
|
+ */
|
|
+ cur->atom = new.atom;
|
|
+ return request_err;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Unable to remove request. Try to acquire in case
|
|
+ * the owner has released the lock.
|
|
+ */
|
|
+ } while (nbcon_context_try_acquire_requested(ctxt, cur));
|
|
+
|
|
+ /* Lucky timing. The acquire succeeded while removing the request. */
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * nbcon_context_try_acquire_hostile - Acquire via unsafe hostile takeover
|
|
+ * @ctxt: The context of the caller
|
|
+ * @cur: The current console state
|
|
+ *
|
|
+ * Acquire the console even in the unsafe state.
|
|
+ *
|
|
+ * It can be permitted by setting the 'allow_unsafe_takeover' field only
|
|
+ * by the final attempt to flush messages in panic().
|
|
+ *
|
|
+ * Return: 0 on success. -EPERM when not allowed by the context.
|
|
+ */
|
|
+static int nbcon_context_try_acquire_hostile(struct nbcon_context *ctxt,
|
|
+ struct nbcon_state *cur)
|
|
+{
|
|
+ unsigned int cpu = smp_processor_id();
|
|
+ struct console *con = ctxt->console;
|
|
+ struct nbcon_state new;
|
|
+
|
|
+ if (!ctxt->allow_unsafe_takeover)
|
|
+ return -EPERM;
|
|
+
|
|
+ /* Ensure caller is allowed to perform unsafe hostile takeovers. */
|
|
+ if (WARN_ON_ONCE(ctxt->prio != NBCON_PRIO_PANIC))
|
|
+ return -EPERM;
|
|
+
|
|
+ /*
|
|
+ * Check that try_acquire_direct() and try_acquire_handover() returned
|
|
+ * -EBUSY in the right situation.
|
|
+ */
|
|
+ WARN_ON_ONCE(ctxt->prio <= cur->prio || ctxt->prio <= cur->req_prio);
|
|
+ WARN_ON_ONCE(cur->unsafe != true);
|
|
+
|
|
+ do {
|
|
+ new.atom = cur->atom;
|
|
+ new.cpu = cpu;
|
|
+ new.prio = ctxt->prio;
|
|
+ new.unsafe |= cur->unsafe_takeover;
|
|
+ new.unsafe_takeover |= cur->unsafe;
|
|
+
|
|
+ } while (!nbcon_state_try_cmpxchg(con, cur, &new));
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * nbcon_context_try_acquire - Try to acquire nbcon console
|
|
+ * @ctxt: The context of the caller
|
|
+ *
|
|
+ * Return: True if the console was acquired. False otherwise.
|
|
+ *
|
|
+ * If the caller allowed an unsafe hostile takeover, on success the
|
|
+ * caller should check the current console state to see if it is
|
|
+ * in an unsafe state. Otherwise, on success the caller may assume
|
|
+ * the console is not in an unsafe state.
|
|
+ */
|
|
+__maybe_unused
|
|
+static bool nbcon_context_try_acquire(struct nbcon_context *ctxt)
|
|
+{
|
|
+ struct console *con = ctxt->console;
|
|
+ struct nbcon_state cur;
|
|
+ int err;
|
|
+
|
|
+ nbcon_state_read(con, &cur);
|
|
+try_again:
|
|
+ err = nbcon_context_try_acquire_direct(ctxt, &cur);
|
|
+ if (err != -EBUSY)
|
|
+ goto out;
|
|
+
|
|
+ err = nbcon_context_try_acquire_handover(ctxt, &cur);
|
|
+ if (err == -EAGAIN)
|
|
+ goto try_again;
|
|
+ if (err != -EBUSY)
|
|
+ goto out;
|
|
+
|
|
+ err = nbcon_context_try_acquire_hostile(ctxt, &cur);
|
|
+out:
|
|
+ return !err;
|
|
+}
|
|
+
|
|
+static bool nbcon_owner_matches(struct nbcon_state *cur, int expected_cpu,
|
|
+ int expected_prio)
|
|
+{
|
|
+ /*
|
|
+ * Since consoles can only be acquired by higher priorities,
|
|
+ * owning contexts are uniquely identified by @prio. However,
|
|
+ * since contexts can unexpectedly lose ownership, it is
|
|
+ * possible that later another owner appears with the same
|
|
+ * priority. For this reason @cpu is also needed.
|
|
+ */
|
|
+
|
|
+ if (cur->prio != expected_prio)
|
|
+ return false;
|
|
+
|
|
+ if (cur->cpu != expected_cpu)
|
|
+ return false;
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * nbcon_context_release - Release the console
|
|
+ * @ctxt: The nbcon context from nbcon_context_try_acquire()
|
|
+ */
|
|
+__maybe_unused
|
|
+static void nbcon_context_release(struct nbcon_context *ctxt)
|
|
+{
|
|
+ unsigned int cpu = smp_processor_id();
|
|
+ struct console *con = ctxt->console;
|
|
+ struct nbcon_state cur;
|
|
+ struct nbcon_state new;
|
|
+
|
|
+ nbcon_state_read(con, &cur);
|
|
+
|
|
+ do {
|
|
+ if (!nbcon_owner_matches(&cur, cpu, ctxt->prio))
|
|
+ return;
|
|
+
|
|
+ new.atom = cur.atom;
|
|
+ new.prio = NBCON_PRIO_NONE;
|
|
+
|
|
+ /*
|
|
+ * If @unsafe_takeover is set, it is kept set so that
|
|
+ * the state remains permanently unsafe.
|
|
+ */
|
|
+ new.unsafe |= cur.unsafe_takeover;
|
|
+
|
|
+ } while (!nbcon_state_try_cmpxchg(con, &cur, &new));
|
|
+}
|
|
+
|
|
/**
|
|
* nbcon_init - Initialize the nbcon console specific data
|
|
* @con: Console to initialize
|
|
--
|
|
2.43.0
|
|
|