| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816 |
- // SPDX-License-Identifier: GPL-2.0-only
- // Copyright (C) 2022 Linutronix GmbH, John Ogness
- // Copyright (C) 2022 Intel, Thomas Gleixner
- #include <linux/atomic.h>
- #include <linux/bug.h>
- #include <linux/console.h>
- #include <linux/delay.h>
- #include <linux/errno.h>
- #include <linux/export.h>
- #include <linux/init.h>
- #include <linux/irqflags.h>
- #include <linux/kthread.h>
- #include <linux/minmax.h>
- #include <linux/percpu.h>
- #include <linux/preempt.h>
- #include <linux/slab.h>
- #include <linux/smp.h>
- #include <linux/stddef.h>
- #include <linux/string.h>
- #include <linux/types.h>
- #include "internal.h"
- #include "printk_ringbuffer.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.
- *
- * 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.
- */
- /**
- * nbcon_state_set - Helper function to set the console state
- * @con: Console to update
- * @new: The new state to write
- *
- * Only to be used when the console is not yet or no longer visible in the
- * system. Otherwise use nbcon_state_try_cmpxchg().
- */
- static inline void nbcon_state_set(struct console *con, struct nbcon_state *new)
- {
- atomic_set(&ACCESS_PRIVATE(con, nbcon_state), new->atom);
- }
- /**
- * nbcon_state_read - Helper function to read the console state
- * @con: Console to read
- * @state: The state to store the result
- */
- static inline void nbcon_state_read(struct console *con, struct nbcon_state *state)
- {
- state->atom = atomic_read(&ACCESS_PRIVATE(con, nbcon_state));
- }
- /**
- * nbcon_state_try_cmpxchg() - Helper function for atomic_try_cmpxchg() on console state
- * @con: Console to update
- * @cur: Old/expected state
- * @new: New state
- *
- * Return: True on success. False on fail and @cur is updated.
- */
- static inline bool nbcon_state_try_cmpxchg(struct console *con, struct nbcon_state *cur,
- struct nbcon_state *new)
- {
- return atomic_try_cmpxchg(&ACCESS_PRIVATE(con, nbcon_state), &cur->atom, new->atom);
- }
- /**
- * nbcon_seq_read - Read the current console sequence
- * @con: Console to read the sequence of
- *
- * Return: Sequence number of the next record to print on @con.
- */
- u64 nbcon_seq_read(struct console *con)
- {
- unsigned long nbcon_seq = atomic_long_read(&ACCESS_PRIVATE(con, nbcon_seq));
- return __ulseq_to_u64seq(prb, nbcon_seq);
- }
- /**
- * nbcon_seq_force - Force console sequence to a specific value
- * @con: Console to work on
- * @seq: Sequence number value to set
- *
- * Only to be used during init (before registration) or in extreme situations
- * (such as panic with CONSOLE_REPLAY_ALL).
- */
- void nbcon_seq_force(struct console *con, u64 seq)
- {
- /*
- * If the specified record no longer exists, the oldest available record
- * is chosen. This is especially important on 32bit systems because only
- * the lower 32 bits of the sequence number are stored. The upper 32 bits
- * are derived from the sequence numbers available in the ringbuffer.
- */
- u64 valid_seq = max_t(u64, seq, prb_first_valid_seq(prb));
- atomic_long_set(&ACCESS_PRIVATE(con, nbcon_seq), __u64seq_to_ulseq(valid_seq));
- }
- /**
- * nbcon_seq_try_update - Try to update the console sequence number
- * @ctxt: Pointer to an acquire context that contains
- * all information about the acquire mode
- * @new_seq: The new sequence number to set
- *
- * @ctxt->seq is updated to the new value of @con::nbcon_seq (expanded to
- * the 64bit value). This could be a different value than @new_seq if
- * nbcon_seq_force() was used or the current context no longer owns the
- * console. In the later case, it will stop printing anyway.
- */
- static void nbcon_seq_try_update(struct nbcon_context *ctxt, u64 new_seq)
- {
- unsigned long nbcon_seq = __u64seq_to_ulseq(ctxt->seq);
- struct console *con = ctxt->console;
- if (atomic_long_try_cmpxchg(&ACCESS_PRIVATE(con, nbcon_seq), &nbcon_seq,
- __u64seq_to_ulseq(new_seq))) {
- ctxt->seq = new_seq;
- } else {
- ctxt->seq = nbcon_seq_read(con);
- }
- }
- /**
- * 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 {
- /*
- * Panic does not imply that the console is owned. However, it
- * is critical that non-panic CPUs during panic are unable to
- * acquire ownership in order to satisfy the assumptions of
- * nbcon_waiter_matches(). In particular, the assumption that
- * lower priorities are ignored during panic.
- */
- 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 priority higher than the owner can become
- * a waiter.
- * - Only a context with a priority higher than the waiter can
- * directly 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. This context is currently a waiter.
- * 2. Another context with a higher priority than this context
- * directly takes ownership.
- * 3. The higher priority context releases the ownership.
- * 4. Another lower priority context takes the ownership.
- * 5. Another context with the same priority as this context
- * creates a request and starts waiting.
- *
- * Event #1 implies this context is EMERGENCY.
- * Event #2 implies the new context is PANIC.
- * Event #3 occurs when panic() has flushed the console.
- * Events #4 and #5 are not possible due to the other_cpu_in_panic()
- * check in nbcon_context_try_acquire_direct().
- */
- 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;
- }
- static struct printk_buffers panic_nbcon_pbufs;
- /**
- * nbcon_context_try_acquire - Try to acquire nbcon console
- * @ctxt: The context of the caller
- *
- * Context: Under @ctxt->con->device_lock() or local_irq_save().
- * 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.
- */
- static bool nbcon_context_try_acquire(struct nbcon_context *ctxt)
- {
- unsigned int cpu = smp_processor_id();
- 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:
- if (err)
- return false;
- /* Acquire succeeded. */
- /* Assign the appropriate buffer for this context. */
- if (atomic_read(&panic_cpu) == cpu)
- ctxt->pbufs = &panic_nbcon_pbufs;
- else
- ctxt->pbufs = con->pbufs;
- /* Set the record sequence for this context to print. */
- ctxt->seq = nbcon_seq_read(ctxt->console);
- return true;
- }
- static bool nbcon_owner_matches(struct nbcon_state *cur, int expected_cpu,
- int expected_prio)
- {
- /*
- * A similar function, nbcon_waiter_matches(), only deals with
- * EMERGENCY and PANIC priorities. However, this function must also
- * deal with the NORMAL priority, which requires additional checks
- * and constraints.
- *
- * For the case where preemption and interrupts are disabled, it is
- * enough to also verify that the owning CPU has not changed.
- *
- * For the case where preemption or interrupts are enabled, an
- * external synchronization method *must* be used. In particular,
- * the driver-specific locking mechanism used in device_lock()
- * (including disabling migration) should be used. It prevents
- * scenarios such as:
- *
- * 1. [Task A] owns a context with NBCON_PRIO_NORMAL on [CPU X] and
- * is scheduled out.
- * 2. Another context takes over the lock with NBCON_PRIO_EMERGENCY
- * and releases it.
- * 3. [Task B] acquires a context with NBCON_PRIO_NORMAL on [CPU X]
- * and is scheduled out.
- * 4. [Task A] gets running on [CPU X] and sees that the console is
- * still owned by a task on [CPU X] with NBON_PRIO_NORMAL. Thus
- * [Task A] thinks it is the owner when it is not.
- */
- 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()
- */
- 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))
- break;
- 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));
- ctxt->pbufs = NULL;
- }
- /**
- * nbcon_context_can_proceed - Check whether ownership can proceed
- * @ctxt: The nbcon context from nbcon_context_try_acquire()
- * @cur: The current console state
- *
- * Return: True if this context still owns the console. False if
- * ownership was handed over or taken.
- *
- * Must be invoked when entering the unsafe state to make sure that it still
- * owns the lock. Also must be invoked when exiting the unsafe context
- * to eventually free the lock for a higher priority context which asked
- * for the friendly handover.
- *
- * It can be called inside an unsafe section when the console is just
- * temporary in safe state instead of exiting and entering the unsafe
- * state.
- *
- * Also it can be called in the safe context before doing an expensive
- * safe operation. It does not make sense to do the operation when
- * a higher priority context took the lock.
- *
- * When this function returns false then the calling context no longer owns
- * the console and is no longer allowed to go forward. In this case it must
- * back out immediately and carefully. The buffer content is also no longer
- * trusted since it no longer belongs to the calling context.
- */
- static bool nbcon_context_can_proceed(struct nbcon_context *ctxt, struct nbcon_state *cur)
- {
- unsigned int cpu = smp_processor_id();
- /* Make sure this context still owns the console. */
- if (!nbcon_owner_matches(cur, cpu, ctxt->prio))
- return false;
- /* The console owner can proceed if there is no waiter. */
- if (cur->req_prio == NBCON_PRIO_NONE)
- return true;
- /*
- * A console owner within an unsafe region is always allowed to
- * proceed, even if there are waiters. It can perform a handover
- * when exiting the unsafe region. Otherwise the waiter will
- * need to perform an unsafe hostile takeover.
- */
- if (cur->unsafe)
- return true;
- /* Waiters always have higher priorities than owners. */
- WARN_ON_ONCE(cur->req_prio <= cur->prio);
- /*
- * Having a safe point for take over and eventually a few
- * duplicated characters or a full line is way better than a
- * hostile takeover. Post processing can take care of the garbage.
- * Release and hand over.
- */
- nbcon_context_release(ctxt);
- /*
- * It is not clear whether the waiter really took over ownership. The
- * outermost callsite must make the final decision whether console
- * ownership is needed for it to proceed. If yes, it must reacquire
- * ownership (possibly hostile) before carefully proceeding.
- *
- * The calling context no longer owns the console so go back all the
- * way instead of trying to implement reacquire heuristics in tons of
- * places.
- */
- return false;
- }
- /**
- * nbcon_can_proceed - Check whether ownership can proceed
- * @wctxt: The write context that was handed to the write function
- *
- * Return: True if this context still owns the console. False if
- * ownership was handed over or taken.
- *
- * It is used in nbcon_enter_unsafe() to make sure that it still owns the
- * lock. Also it is used in nbcon_exit_unsafe() to eventually free the lock
- * for a higher priority context which asked for the friendly handover.
- *
- * It can be called inside an unsafe section when the console is just
- * temporary in safe state instead of exiting and entering the unsafe state.
- *
- * Also it can be called in the safe context before doing an expensive safe
- * operation. It does not make sense to do the operation when a higher
- * priority context took the lock.
- *
- * When this function returns false then the calling context no longer owns
- * the console and is no longer allowed to go forward. In this case it must
- * back out immediately and carefully. The buffer content is also no longer
- * trusted since it no longer belongs to the calling context.
- */
- bool nbcon_can_proceed(struct nbcon_write_context *wctxt)
- {
- struct nbcon_context *ctxt = &ACCESS_PRIVATE(wctxt, ctxt);
- struct console *con = ctxt->console;
- struct nbcon_state cur;
- nbcon_state_read(con, &cur);
- return nbcon_context_can_proceed(ctxt, &cur);
- }
- EXPORT_SYMBOL_GPL(nbcon_can_proceed);
- #define nbcon_context_enter_unsafe(c) __nbcon_context_update_unsafe(c, true)
- #define nbcon_context_exit_unsafe(c) __nbcon_context_update_unsafe(c, false)
- /**
- * __nbcon_context_update_unsafe - Update the unsafe bit in @con->nbcon_state
- * @ctxt: The nbcon context from nbcon_context_try_acquire()
- * @unsafe: The new value for the unsafe bit
- *
- * Return: True if the unsafe state was updated and this context still
- * owns the console. Otherwise false if ownership was handed
- * over or taken.
- *
- * This function allows console owners to modify the unsafe status of the
- * console.
- *
- * When this function returns false then the calling context no longer owns
- * the console and is no longer allowed to go forward. In this case it must
- * back out immediately and carefully. The buffer content is also no longer
- * trusted since it no longer belongs to the calling context.
- *
- * Internal helper to avoid duplicated code.
- */
- static bool __nbcon_context_update_unsafe(struct nbcon_context *ctxt, bool unsafe)
- {
- struct console *con = ctxt->console;
- struct nbcon_state cur;
- struct nbcon_state new;
- nbcon_state_read(con, &cur);
- do {
- /*
- * The unsafe bit must not be cleared if an
- * unsafe hostile takeover has occurred.
- */
- if (!unsafe && cur.unsafe_takeover)
- goto out;
- if (!nbcon_context_can_proceed(ctxt, &cur))
- return false;
- new.atom = cur.atom;
- new.unsafe = unsafe;
- } while (!nbcon_state_try_cmpxchg(con, &cur, &new));
- cur.atom = new.atom;
- out:
- return nbcon_context_can_proceed(ctxt, &cur);
- }
- static void nbcon_write_context_set_buf(struct nbcon_write_context *wctxt,
- char *buf, unsigned int len)
- {
- struct nbcon_context *ctxt = &ACCESS_PRIVATE(wctxt, ctxt);
- struct console *con = ctxt->console;
- struct nbcon_state cur;
- wctxt->outbuf = buf;
- wctxt->len = len;
- nbcon_state_read(con, &cur);
- wctxt->unsafe_takeover = cur.unsafe_takeover;
- }
- /**
- * nbcon_enter_unsafe - Enter an unsafe region in the driver
- * @wctxt: The write context that was handed to the write function
- *
- * Return: True if this context still owns the console. False if
- * ownership was handed over or taken.
- *
- * When this function returns false then the calling context no longer owns
- * the console and is no longer allowed to go forward. In this case it must
- * back out immediately and carefully. The buffer content is also no longer
- * trusted since it no longer belongs to the calling context.
- */
- bool nbcon_enter_unsafe(struct nbcon_write_context *wctxt)
- {
- struct nbcon_context *ctxt = &ACCESS_PRIVATE(wctxt, ctxt);
- bool is_owner;
- is_owner = nbcon_context_enter_unsafe(ctxt);
- if (!is_owner)
- nbcon_write_context_set_buf(wctxt, NULL, 0);
- return is_owner;
- }
- EXPORT_SYMBOL_GPL(nbcon_enter_unsafe);
- /**
- * nbcon_exit_unsafe - Exit an unsafe region in the driver
- * @wctxt: The write context that was handed to the write function
- *
- * Return: True if this context still owns the console. False if
- * ownership was handed over or taken.
- *
- * When this function returns false then the calling context no longer owns
- * the console and is no longer allowed to go forward. In this case it must
- * back out immediately and carefully. The buffer content is also no longer
- * trusted since it no longer belongs to the calling context.
- */
- bool nbcon_exit_unsafe(struct nbcon_write_context *wctxt)
- {
- struct nbcon_context *ctxt = &ACCESS_PRIVATE(wctxt, ctxt);
- bool ret;
- ret = nbcon_context_exit_unsafe(ctxt);
- if (!ret)
- nbcon_write_context_set_buf(wctxt, NULL, 0);
- return ret;
- }
- EXPORT_SYMBOL_GPL(nbcon_exit_unsafe);
- /**
- * nbcon_reacquire_nobuf - Reacquire a console after losing ownership
- * while printing
- * @wctxt: The write context that was handed to the write callback
- *
- * Since ownership can be lost at any time due to handover or takeover, a
- * printing context _must_ be prepared to back out immediately and
- * carefully. However, there are scenarios where the printing context must
- * reacquire ownership in order to finalize or revert hardware changes.
- *
- * This function allows a printing context to reacquire ownership using the
- * same priority as its previous ownership.
- *
- * Note that after a successful reacquire the printing context will have no
- * output buffer because that has been lost. This function cannot be used to
- * resume printing.
- */
- void nbcon_reacquire_nobuf(struct nbcon_write_context *wctxt)
- {
- struct nbcon_context *ctxt = &ACCESS_PRIVATE(wctxt, ctxt);
- while (!nbcon_context_try_acquire(ctxt))
- cpu_relax();
- nbcon_write_context_set_buf(wctxt, NULL, 0);
- }
- EXPORT_SYMBOL_GPL(nbcon_reacquire_nobuf);
- /**
- * nbcon_emit_next_record - Emit a record in the acquired context
- * @wctxt: The write context that will be handed to the write function
- * @use_atomic: True if the write_atomic() callback is to be used
- *
- * Return: True if this context still owns the console. False if
- * ownership was handed over or taken.
- *
- * When this function returns false then the calling context no longer owns
- * the console and is no longer allowed to go forward. In this case it must
- * back out immediately and carefully. The buffer content is also no longer
- * trusted since it no longer belongs to the calling context. If the caller
- * wants to do more it must reacquire the console first.
- *
- * When true is returned, @wctxt->ctxt.backlog indicates whether there are
- * still records pending in the ringbuffer,
- */
- static bool nbcon_emit_next_record(struct nbcon_write_context *wctxt, bool use_atomic)
- {
- struct nbcon_context *ctxt = &ACCESS_PRIVATE(wctxt, ctxt);
- struct console *con = ctxt->console;
- bool is_extended = console_srcu_read_flags(con) & CON_EXTENDED;
- struct printk_message pmsg = {
- .pbufs = ctxt->pbufs,
- };
- unsigned long con_dropped;
- struct nbcon_state cur;
- unsigned long dropped;
- unsigned long ulseq;
- /*
- * This function should never be called for consoles that have not
- * implemented the necessary callback for writing: i.e. legacy
- * consoles and, when atomic, nbcon consoles with no write_atomic().
- * Handle it as if ownership was lost and try to continue.
- *
- * Note that for nbcon consoles the write_thread() callback is
- * mandatory and was already checked in nbcon_alloc().
- */
- if (WARN_ON_ONCE((use_atomic && !con->write_atomic) ||
- !(console_srcu_read_flags(con) & CON_NBCON))) {
- nbcon_context_release(ctxt);
- return false;
- }
- /*
- * The printk buffers are filled within an unsafe section. This
- * prevents NBCON_PRIO_NORMAL and NBCON_PRIO_EMERGENCY from
- * clobbering each other.
- */
- if (!nbcon_context_enter_unsafe(ctxt))
- return false;
- ctxt->backlog = printk_get_next_message(&pmsg, ctxt->seq, is_extended, true);
- if (!ctxt->backlog)
- return nbcon_context_exit_unsafe(ctxt);
- /*
- * @con->dropped is not protected in case of an unsafe hostile
- * takeover. In that situation the update can be racy so
- * annotate it accordingly.
- */
- con_dropped = data_race(READ_ONCE(con->dropped));
- dropped = con_dropped + pmsg.dropped;
- if (dropped && !is_extended)
- console_prepend_dropped(&pmsg, dropped);
- /*
- * If the previous owner was assigned the same record, this context
- * has taken over ownership and is replaying the record. Prepend a
- * message to let the user know the record is replayed.
- */
- ulseq = atomic_long_read(&ACCESS_PRIVATE(con, nbcon_prev_seq));
- if (__ulseq_to_u64seq(prb, ulseq) == pmsg.seq) {
- console_prepend_replay(&pmsg);
- } else {
- /*
- * Ensure this context is still the owner before trying to
- * update @nbcon_prev_seq. Otherwise the value in @ulseq may
- * not be from the previous owner and instead be some later
- * value from the context that took over ownership.
- */
- nbcon_state_read(con, &cur);
- if (!nbcon_context_can_proceed(ctxt, &cur))
- return false;
- atomic_long_try_cmpxchg(&ACCESS_PRIVATE(con, nbcon_prev_seq), &ulseq,
- __u64seq_to_ulseq(pmsg.seq));
- }
- if (!nbcon_context_exit_unsafe(ctxt))
- return false;
- /* For skipped records just update seq/dropped in @con. */
- if (pmsg.outbuf_len == 0)
- goto update_con;
- /* Initialize the write context for driver callbacks. */
- nbcon_write_context_set_buf(wctxt, &pmsg.pbufs->outbuf[0], pmsg.outbuf_len);
- if (use_atomic)
- con->write_atomic(con, wctxt);
- else
- con->write_thread(con, wctxt);
- if (!wctxt->outbuf) {
- /*
- * Ownership was lost and reacquired by the driver. Handle it
- * as if ownership was lost.
- */
- nbcon_context_release(ctxt);
- return false;
- }
- /*
- * Ownership may have been lost but _not_ reacquired by the driver.
- * This case is detected and handled when entering unsafe to update
- * dropped/seq values.
- */
- /*
- * Since any dropped message was successfully output, reset the
- * dropped count for the console.
- */
- dropped = 0;
- update_con:
- /*
- * The dropped count and the sequence number are updated within an
- * unsafe section. This limits update races to the panic context and
- * allows the panic context to win.
- */
- if (!nbcon_context_enter_unsafe(ctxt))
- return false;
- if (dropped != con_dropped) {
- /* Counterpart to the READ_ONCE() above. */
- WRITE_ONCE(con->dropped, dropped);
- }
- nbcon_seq_try_update(ctxt, pmsg.seq + 1);
- return nbcon_context_exit_unsafe(ctxt);
- }
- /*
- * nbcon_emit_one - Print one record for an nbcon console using the
- * specified callback
- * @wctxt: An initialized write context struct to use for this context
- * @use_atomic: True if the write_atomic() callback is to be used
- *
- * Return: True, when a record has been printed and there are still
- * pending records. The caller might want to continue flushing.
- *
- * False, when there is no pending record, or when the console
- * context cannot be acquired, or the ownership has been lost.
- * The caller should give up. Either the job is done, cannot be
- * done, or will be handled by the owning context.
- *
- * This is an internal helper to handle the locking of the console before
- * calling nbcon_emit_next_record().
- */
- static bool nbcon_emit_one(struct nbcon_write_context *wctxt, bool use_atomic)
- {
- struct nbcon_context *ctxt = &ACCESS_PRIVATE(wctxt, ctxt);
- struct console *con = ctxt->console;
- unsigned long flags;
- bool ret = false;
- if (!use_atomic) {
- con->device_lock(con, &flags);
- /*
- * Ensure this stays on the CPU to make handover and
- * takeover possible.
- */
- cant_migrate();
- }
- if (!nbcon_context_try_acquire(ctxt))
- goto out;
- /*
- * nbcon_emit_next_record() returns false when the console was
- * handed over or taken over. In both cases the context is no
- * longer valid.
- *
- * The higher priority printing context takes over responsibility
- * to print the pending records.
- */
- if (!nbcon_emit_next_record(wctxt, use_atomic))
- goto out;
- nbcon_context_release(ctxt);
- ret = ctxt->backlog;
- out:
- if (!use_atomic)
- con->device_unlock(con, flags);
- return ret;
- }
- /**
- * nbcon_kthread_should_wakeup - Check whether a printer thread should wakeup
- * @con: Console to operate on
- * @ctxt: The nbcon context from nbcon_context_try_acquire()
- *
- * Return: True if the thread should shutdown or if the console is
- * allowed to print and a record is available. False otherwise.
- *
- * After the thread wakes up, it must first check if it should shutdown before
- * attempting any printing.
- */
- static bool nbcon_kthread_should_wakeup(struct console *con, struct nbcon_context *ctxt)
- {
- bool ret = false;
- short flags;
- int cookie;
- if (kthread_should_stop())
- return true;
- cookie = console_srcu_read_lock();
- flags = console_srcu_read_flags(con);
- if (console_is_usable(con, flags, false)) {
- /* Bring the sequence in @ctxt up to date */
- ctxt->seq = nbcon_seq_read(con);
- ret = prb_read_valid(prb, ctxt->seq, NULL);
- }
- console_srcu_read_unlock(cookie);
- return ret;
- }
- /**
- * nbcon_kthread_func - The printer thread function
- * @__console: Console to operate on
- *
- * Return: 0
- */
- static int nbcon_kthread_func(void *__console)
- {
- struct console *con = __console;
- struct nbcon_write_context wctxt = {
- .ctxt.console = con,
- .ctxt.prio = NBCON_PRIO_NORMAL,
- };
- struct nbcon_context *ctxt = &ACCESS_PRIVATE(&wctxt, ctxt);
- short con_flags;
- bool backlog;
- int cookie;
- wait_for_event:
- /*
- * Guarantee this task is visible on the rcuwait before
- * checking the wake condition.
- *
- * The full memory barrier within set_current_state() of
- * ___rcuwait_wait_event() pairs with the full memory
- * barrier within rcuwait_has_sleeper().
- *
- * This pairs with rcuwait_has_sleeper:A and nbcon_kthread_wake:A.
- */
- rcuwait_wait_event(&con->rcuwait,
- nbcon_kthread_should_wakeup(con, ctxt),
- TASK_INTERRUPTIBLE); /* LMM(nbcon_kthread_func:A) */
- do {
- if (kthread_should_stop())
- return 0;
- backlog = false;
- /*
- * Keep the srcu read lock around the entire operation so that
- * synchronize_srcu() can guarantee that the kthread stopped
- * or suspended printing.
- */
- cookie = console_srcu_read_lock();
- con_flags = console_srcu_read_flags(con);
- if (console_is_usable(con, con_flags, false))
- backlog = nbcon_emit_one(&wctxt, false);
- console_srcu_read_unlock(cookie);
- cond_resched();
- } while (backlog);
- goto wait_for_event;
- }
- /**
- * nbcon_irq_work - irq work to wake console printer thread
- * @irq_work: The irq work to operate on
- */
- static void nbcon_irq_work(struct irq_work *irq_work)
- {
- struct console *con = container_of(irq_work, struct console, irq_work);
- nbcon_kthread_wake(con);
- }
- static inline bool rcuwait_has_sleeper(struct rcuwait *w)
- {
- /*
- * Guarantee any new records can be seen by tasks preparing to wait
- * before this context checks if the rcuwait is empty.
- *
- * This full memory barrier pairs with the full memory barrier within
- * set_current_state() of ___rcuwait_wait_event(), which is called
- * after prepare_to_rcuwait() adds the waiter but before it has
- * checked the wait condition.
- *
- * This pairs with nbcon_kthread_func:A.
- */
- smp_mb(); /* LMM(rcuwait_has_sleeper:A) */
- return rcuwait_active(w);
- }
- /**
- * nbcon_kthreads_wake - Wake up printing threads using irq_work
- */
- void nbcon_kthreads_wake(void)
- {
- struct console *con;
- int cookie;
- if (!printk_kthreads_running)
- return;
- cookie = console_srcu_read_lock();
- for_each_console_srcu(con) {
- if (!(console_srcu_read_flags(con) & CON_NBCON))
- continue;
- /*
- * Only schedule irq_work if the printing thread is
- * actively waiting. If not waiting, the thread will
- * notice by itself that it has work to do.
- */
- if (rcuwait_has_sleeper(&con->rcuwait))
- irq_work_queue(&con->irq_work);
- }
- console_srcu_read_unlock(cookie);
- }
- /*
- * nbcon_kthread_stop - Stop a console printer thread
- * @con: Console to operate on
- */
- void nbcon_kthread_stop(struct console *con)
- {
- lockdep_assert_console_list_lock_held();
- if (!con->kthread)
- return;
- kthread_stop(con->kthread);
- con->kthread = NULL;
- }
- /**
- * nbcon_kthread_create - Create a console printer thread
- * @con: Console to operate on
- *
- * Return: True if the kthread was started or already exists.
- * Otherwise false and @con must not be registered.
- *
- * This function is called when it will be expected that nbcon consoles are
- * flushed using the kthread. The messages printed with NBCON_PRIO_NORMAL
- * will be no longer flushed by the legacy loop. This is why failure must
- * be fatal for console registration.
- *
- * If @con was already registered and this function fails, @con must be
- * unregistered before the global state variable @printk_kthreads_running
- * can be set.
- */
- bool nbcon_kthread_create(struct console *con)
- {
- struct task_struct *kt;
- lockdep_assert_console_list_lock_held();
- if (con->kthread)
- return true;
- kt = kthread_run(nbcon_kthread_func, con, "pr/%s%d", con->name, con->index);
- if (WARN_ON(IS_ERR(kt))) {
- con_printk(KERN_ERR, con, "failed to start printing thread\n");
- return false;
- }
- con->kthread = kt;
- /*
- * It is important that console printing threads are scheduled
- * shortly after a printk call and with generous runtime budgets.
- */
- sched_set_normal(con->kthread, -20);
- return true;
- }
- /* Track the nbcon emergency nesting per CPU. */
- static DEFINE_PER_CPU(unsigned int, nbcon_pcpu_emergency_nesting);
- static unsigned int early_nbcon_pcpu_emergency_nesting __initdata;
- /**
- * nbcon_get_cpu_emergency_nesting - Get the per CPU emergency nesting pointer
- *
- * Context: For reading, any context. For writing, any context which could
- * not be migrated to another CPU.
- * Return: Either a pointer to the per CPU emergency nesting counter of
- * the current CPU or to the init data during early boot.
- *
- * The function is safe for reading per-CPU variables in any context because
- * preemption is disabled if the current CPU is in the emergency state. See
- * also nbcon_cpu_emergency_enter().
- */
- static __ref unsigned int *nbcon_get_cpu_emergency_nesting(void)
- {
- /*
- * The value of __printk_percpu_data_ready gets set in normal
- * context and before SMP initialization. As a result it could
- * never change while inside an nbcon emergency section.
- */
- if (!printk_percpu_data_ready())
- return &early_nbcon_pcpu_emergency_nesting;
- return raw_cpu_ptr(&nbcon_pcpu_emergency_nesting);
- }
- /**
- * nbcon_get_default_prio - The appropriate nbcon priority to use for nbcon
- * printing on the current CPU
- *
- * Context: Any context.
- * Return: The nbcon_prio to use for acquiring an nbcon console in this
- * context for printing.
- *
- * The function is safe for reading per-CPU data in any context because
- * preemption is disabled if the current CPU is in the emergency or panic
- * state.
- */
- enum nbcon_prio nbcon_get_default_prio(void)
- {
- unsigned int *cpu_emergency_nesting;
- if (this_cpu_in_panic())
- return NBCON_PRIO_PANIC;
- cpu_emergency_nesting = nbcon_get_cpu_emergency_nesting();
- if (*cpu_emergency_nesting)
- return NBCON_PRIO_EMERGENCY;
- return NBCON_PRIO_NORMAL;
- }
- /**
- * nbcon_legacy_emit_next_record - Print one record for an nbcon console
- * in legacy contexts
- * @con: The console to print on
- * @handover: Will be set to true if a printk waiter has taken over the
- * console_lock, in which case the caller is no longer holding
- * both the console_lock and the SRCU read lock. Otherwise it
- * is set to false.
- * @cookie: The cookie from the SRCU read lock.
- * @use_atomic: Set true when called in an atomic or unknown context.
- * It affects which nbcon callback will be used: write_atomic()
- * or write_thread().
- *
- * When false, the write_thread() callback is used and would be
- * called in a preemtible context unless disabled by the
- * device_lock. The legacy handover is not allowed in this mode.
- *
- * Context: Any context except NMI.
- * Return: True, when a record has been printed and there are still
- * pending records. The caller might want to continue flushing.
- *
- * False, when there is no pending record, or when the console
- * context cannot be acquired, or the ownership has been lost.
- * The caller should give up. Either the job is done, cannot be
- * done, or will be handled by the owning context.
- *
- * This function is meant to be called by console_flush_all() to print records
- * on nbcon consoles from legacy context (printing via console unlocking).
- * Essentially it is the nbcon version of console_emit_next_record().
- */
- bool nbcon_legacy_emit_next_record(struct console *con, bool *handover,
- int cookie, bool use_atomic)
- {
- struct nbcon_write_context wctxt = { };
- struct nbcon_context *ctxt = &ACCESS_PRIVATE(&wctxt, ctxt);
- unsigned long flags;
- bool progress;
- ctxt->console = con;
- ctxt->prio = nbcon_get_default_prio();
- if (use_atomic) {
- /*
- * In an atomic or unknown context, use the same procedure as
- * in console_emit_next_record(). It allows to handover.
- */
- printk_safe_enter_irqsave(flags);
- console_lock_spinning_enable();
- stop_critical_timings();
- }
- progress = nbcon_emit_one(&wctxt, use_atomic);
- if (use_atomic) {
- start_critical_timings();
- *handover = console_lock_spinning_disable_and_check(cookie);
- printk_safe_exit_irqrestore(flags);
- } else {
- /* Non-atomic does not perform legacy spinning handovers. */
- *handover = false;
- }
- return progress;
- }
- /**
- * __nbcon_atomic_flush_pending_con - Flush specified nbcon console using its
- * write_atomic() callback
- * @con: The nbcon console to flush
- * @stop_seq: Flush up until this record
- * @allow_unsafe_takeover: True, to allow unsafe hostile takeovers
- *
- * Return: 0 if @con was flushed up to @stop_seq Otherwise, error code on
- * failure.
- *
- * Errors:
- *
- * -EPERM: Unable to acquire console ownership.
- *
- * -EAGAIN: Another context took over ownership while printing.
- *
- * -ENOENT: A record before @stop_seq is not available.
- *
- * If flushing up to @stop_seq was not successful, it only makes sense for the
- * caller to try again when -EAGAIN was returned. When -EPERM is returned,
- * this context is not allowed to acquire the console. When -ENOENT is
- * returned, it cannot be expected that the unfinalized record will become
- * available.
- */
- static int __nbcon_atomic_flush_pending_con(struct console *con, u64 stop_seq,
- bool allow_unsafe_takeover)
- {
- struct nbcon_write_context wctxt = { };
- struct nbcon_context *ctxt = &ACCESS_PRIVATE(&wctxt, ctxt);
- int err = 0;
- ctxt->console = con;
- ctxt->spinwait_max_us = 2000;
- ctxt->prio = nbcon_get_default_prio();
- ctxt->allow_unsafe_takeover = allow_unsafe_takeover;
- if (!nbcon_context_try_acquire(ctxt))
- return -EPERM;
- while (nbcon_seq_read(con) < stop_seq) {
- /*
- * nbcon_emit_next_record() returns false when the console was
- * handed over or taken over. In both cases the context is no
- * longer valid.
- */
- if (!nbcon_emit_next_record(&wctxt, true))
- return -EAGAIN;
- if (!ctxt->backlog) {
- /* Are there reserved but not yet finalized records? */
- if (nbcon_seq_read(con) < stop_seq)
- err = -ENOENT;
- break;
- }
- }
- nbcon_context_release(ctxt);
- return err;
- }
- /**
- * nbcon_atomic_flush_pending_con - Flush specified nbcon console using its
- * write_atomic() callback
- * @con: The nbcon console to flush
- * @stop_seq: Flush up until this record
- * @allow_unsafe_takeover: True, to allow unsafe hostile takeovers
- *
- * This will stop flushing before @stop_seq if another context has ownership.
- * That context is then responsible for the flushing. Likewise, if new records
- * are added while this context was flushing and there is no other context
- * to handle the printing, this context must also flush those records.
- */
- static void nbcon_atomic_flush_pending_con(struct console *con, u64 stop_seq,
- bool allow_unsafe_takeover)
- {
- struct console_flush_type ft;
- unsigned long flags;
- int err;
- again:
- /*
- * Atomic flushing does not use console driver synchronization (i.e.
- * it does not hold the port lock for uart consoles). Therefore IRQs
- * must be disabled to avoid being interrupted and then calling into
- * a driver that will deadlock trying to acquire console ownership.
- */
- local_irq_save(flags);
- err = __nbcon_atomic_flush_pending_con(con, stop_seq, allow_unsafe_takeover);
- local_irq_restore(flags);
- /*
- * If there was a new owner (-EPERM, -EAGAIN), that context is
- * responsible for completing.
- *
- * Do not wait for records not yet finalized (-ENOENT) to avoid a
- * possible deadlock. They will either get flushed by the writer or
- * eventually skipped on panic CPU.
- */
- if (err)
- return;
- /*
- * If flushing was successful but more records are available, this
- * context must flush those remaining records if the printer thread
- * is not available do it.
- */
- printk_get_console_flush_type(&ft);
- if (!ft.nbcon_offload &&
- prb_read_valid(prb, nbcon_seq_read(con), NULL)) {
- stop_seq = prb_next_reserve_seq(prb);
- goto again;
- }
- }
- /**
- * __nbcon_atomic_flush_pending - Flush all nbcon consoles using their
- * write_atomic() callback
- * @stop_seq: Flush up until this record
- * @allow_unsafe_takeover: True, to allow unsafe hostile takeovers
- */
- static void __nbcon_atomic_flush_pending(u64 stop_seq, bool allow_unsafe_takeover)
- {
- struct console *con;
- int cookie;
- cookie = console_srcu_read_lock();
- for_each_console_srcu(con) {
- short flags = console_srcu_read_flags(con);
- if (!(flags & CON_NBCON))
- continue;
- if (!console_is_usable(con, flags, true))
- continue;
- if (nbcon_seq_read(con) >= stop_seq)
- continue;
- nbcon_atomic_flush_pending_con(con, stop_seq, allow_unsafe_takeover);
- }
- console_srcu_read_unlock(cookie);
- }
- /**
- * nbcon_atomic_flush_pending - Flush all nbcon consoles using their
- * write_atomic() callback
- *
- * Flush the backlog up through the currently newest record. Any new
- * records added while flushing will not be flushed if there is another
- * context available to handle the flushing. This is to avoid one CPU
- * printing unbounded because other CPUs continue to add records.
- */
- void nbcon_atomic_flush_pending(void)
- {
- __nbcon_atomic_flush_pending(prb_next_reserve_seq(prb), false);
- }
- /**
- * nbcon_atomic_flush_unsafe - Flush all nbcon consoles using their
- * write_atomic() callback and allowing unsafe hostile takeovers
- *
- * Flush the backlog up through the currently newest record. Unsafe hostile
- * takeovers will be performed, if necessary.
- */
- void nbcon_atomic_flush_unsafe(void)
- {
- __nbcon_atomic_flush_pending(prb_next_reserve_seq(prb), true);
- }
- /**
- * nbcon_cpu_emergency_enter - Enter an emergency section where printk()
- * messages for that CPU are flushed directly
- *
- * Context: Any context. Disables preemption.
- *
- * When within an emergency section, printk() calls will attempt to flush any
- * pending messages in the ringbuffer.
- */
- void nbcon_cpu_emergency_enter(void)
- {
- unsigned int *cpu_emergency_nesting;
- preempt_disable();
- cpu_emergency_nesting = nbcon_get_cpu_emergency_nesting();
- (*cpu_emergency_nesting)++;
- }
- /**
- * nbcon_cpu_emergency_exit - Exit an emergency section
- *
- * Context: Within an emergency section. Enables preemption.
- */
- void nbcon_cpu_emergency_exit(void)
- {
- unsigned int *cpu_emergency_nesting;
- cpu_emergency_nesting = nbcon_get_cpu_emergency_nesting();
- if (!WARN_ON_ONCE(*cpu_emergency_nesting == 0))
- (*cpu_emergency_nesting)--;
- preempt_enable();
- }
- /**
- * nbcon_alloc - Allocate and init the nbcon console specific data
- * @con: Console to initialize
- *
- * Return: True if the console was fully allocated and initialized.
- * Otherwise @con must not be registered.
- *
- * When allocation and init was successful, the console must be properly
- * freed using nbcon_free() once it is no longer needed.
- */
- bool nbcon_alloc(struct console *con)
- {
- struct nbcon_state state = { };
- /* The write_thread() callback is mandatory. */
- if (WARN_ON(!con->write_thread))
- return false;
- rcuwait_init(&con->rcuwait);
- init_irq_work(&con->irq_work, nbcon_irq_work);
- atomic_long_set(&ACCESS_PRIVATE(con, nbcon_prev_seq), -1UL);
- nbcon_state_set(con, &state);
- /*
- * Initialize @nbcon_seq to the highest possible sequence number so
- * that practically speaking it will have nothing to print until a
- * desired initial sequence number has been set via nbcon_seq_force().
- */
- atomic_long_set(&ACCESS_PRIVATE(con, nbcon_seq), ULSEQ_MAX(prb));
- if (con->flags & CON_BOOT) {
- /*
- * Boot console printing is synchronized with legacy console
- * printing, so boot consoles can share the same global printk
- * buffers.
- */
- con->pbufs = &printk_shared_pbufs;
- } else {
- con->pbufs = kmalloc(sizeof(*con->pbufs), GFP_KERNEL);
- if (!con->pbufs) {
- con_printk(KERN_ERR, con, "failed to allocate printing buffer\n");
- return false;
- }
- if (printk_kthreads_running) {
- if (!nbcon_kthread_create(con)) {
- kfree(con->pbufs);
- con->pbufs = NULL;
- return false;
- }
- }
- }
- return true;
- }
- /**
- * nbcon_free - Free and cleanup the nbcon console specific data
- * @con: Console to free/cleanup nbcon data
- */
- void nbcon_free(struct console *con)
- {
- struct nbcon_state state = { };
- if (printk_kthreads_running)
- nbcon_kthread_stop(con);
- nbcon_state_set(con, &state);
- /* Boot consoles share global printk buffers. */
- if (!(con->flags & CON_BOOT))
- kfree(con->pbufs);
- con->pbufs = NULL;
- }
- /**
- * nbcon_device_try_acquire - Try to acquire nbcon console and enter unsafe
- * section
- * @con: The nbcon console to acquire
- *
- * Context: Under the locking mechanism implemented in
- * @con->device_lock() including disabling migration.
- * Return: True if the console was acquired. False otherwise.
- *
- * Console drivers will usually use their own internal synchronization
- * mechasism to synchronize between console printing and non-printing
- * activities (such as setting baud rates). However, nbcon console drivers
- * supporting atomic consoles may also want to mark unsafe sections when
- * performing non-printing activities in order to synchronize against their
- * atomic_write() callback.
- *
- * This function acquires the nbcon console using priority NBCON_PRIO_NORMAL
- * and marks it unsafe for handover/takeover.
- */
- bool nbcon_device_try_acquire(struct console *con)
- {
- struct nbcon_context *ctxt = &ACCESS_PRIVATE(con, nbcon_device_ctxt);
- cant_migrate();
- memset(ctxt, 0, sizeof(*ctxt));
- ctxt->console = con;
- ctxt->prio = NBCON_PRIO_NORMAL;
- if (!nbcon_context_try_acquire(ctxt))
- return false;
- if (!nbcon_context_enter_unsafe(ctxt))
- return false;
- return true;
- }
- EXPORT_SYMBOL_GPL(nbcon_device_try_acquire);
- /**
- * nbcon_device_release - Exit unsafe section and release the nbcon console
- * @con: The nbcon console acquired in nbcon_device_try_acquire()
- */
- void nbcon_device_release(struct console *con)
- {
- struct nbcon_context *ctxt = &ACCESS_PRIVATE(con, nbcon_device_ctxt);
- struct console_flush_type ft;
- int cookie;
- if (!nbcon_context_exit_unsafe(ctxt))
- return;
- nbcon_context_release(ctxt);
- /*
- * This context must flush any new records added while the console
- * was locked if the printer thread is not available to do it. The
- * console_srcu_read_lock must be taken to ensure the console is
- * usable throughout flushing.
- */
- cookie = console_srcu_read_lock();
- printk_get_console_flush_type(&ft);
- if (console_is_usable(con, console_srcu_read_flags(con), true) &&
- !ft.nbcon_offload &&
- prb_read_valid(prb, nbcon_seq_read(con), NULL)) {
- /*
- * If nbcon_atomic flushing is not available, fallback to
- * using the legacy loop.
- */
- if (ft.nbcon_atomic) {
- __nbcon_atomic_flush_pending_con(con, prb_next_reserve_seq(prb), false);
- } else if (ft.legacy_direct) {
- if (console_trylock())
- console_unlock();
- } else if (ft.legacy_offload) {
- printk_trigger_flush();
- }
- }
- console_srcu_read_unlock(cookie);
- }
- EXPORT_SYMBOL_GPL(nbcon_device_release);
|